I want to create this SQL query:
SELECT
a.[Seat],
b.[PlayerId],
b.[UserName],
b.[NickName],
COUNT(c.PlayerId) AS Trophy
FROM [dbo].[tbl_PlayerTableSeat] AS a
INNER JOIN [dbo].[tbl_Player] AS b ON a.[PlayerId] = b.[PlayerId]
INNER JOIN [dbo].[tbl_GameVirtualTable] AS d ON d.GameVirtualTableId = a.GameVirtualTableId
LEFT OUTER JOIN [dbo].[tbl_PlayerTableWinning] AS c ON a.[PlayerId] = c.[PlayerId] AND c.GameTableId = d.GameTableId
WHERE a.GameVirtualTableId = 36
GROUP BY a.[Seat], b.[PlayerId], b.[UserName], b.[NickName]
I have this Linq
var virtualTableSeatList = (from s in db.PlayerTableSeat
join p in db.Player on s.PlayerId equals p.PlayerId
join v in db.GameVirtualTable on s.GameVirtualTableId equals v.GameVirtualTableId
join w in db.PlayerTableWinning on new { X1 = s.PlayerId, X2 = v.GameTableId } equals new { X1 = w.PlayerId, X2 = w.GameTableId } into gj
from g in gj.DefaultIfEmpty()
where s.GameVirtualTableId == virtualGameTableId
group new { p, s } by new { p.PlayerId, s.Seat, p.NickName, p.UserName } into grp
select new VirtualTableSeatDto
{
PlayerId = grp.Key.PlayerId,
Seat = grp.Key.Seat,
NickName = grp.Key.NickName,
UserName = grp.Key.UserName,
Trophy = grp.Count()
}
).ToList();
From SQL Profiler, the Linq generates this SQL query:
exec sp_executesql N'SELECT
[GroupBy1].[K2] AS [PlayerId],
CAST( [GroupBy1].[K1] AS int) AS [C1],
[GroupBy1].[K4] AS [NickName],
[GroupBy1].[K3] AS [UserName],
[GroupBy1].[A1] AS [C2]
FROM ( SELECT
[Extent1].[Seat] AS [K1],
[Extent2].[PlayerId] AS [K2],
[Extent2].[UserName] AS [K3],
[Extent2].[NickName] AS [K4],
COUNT(1) AS [A1]
FROM [dbo].[tbl_PlayerTableSeat] AS [Extent1]
INNER JOIN [dbo].[tbl_Player] AS [Extent2] ON [Extent1].[PlayerId] = [Extent2].[PlayerId]
INNER JOIN [dbo].[tbl_GameVirtualTable] AS [Extent3] ON [Extent1].[GameVirtualTableId] = [Extent3].[GameVirtualTableId]
LEFT OUTER JOIN [dbo].[tbl_PlayerTableWinning] AS [Extent4] ON ([Extent1].[PlayerId] = [Extent4].[PlayerId]) AND ([Extent3].[GameTableId] = [Extent4].[GameTableId])
WHERE [Extent1].[GameVirtualTableId] = #p__linq__0
GROUP BY [Extent1].[Seat], [Extent2].[PlayerId], [Extent2].[UserName], [Extent2].[NickName]
) AS [GroupBy1]',N'#p__linq__0 int',#p__linq__0=36
I want to change COUNT(1) AS [A1] to COUNT([Extent4].[PlayerId]) AS [A1]
so it can return correct data.
I have no idea how to change the LinQ
Trophy = grp.Count()
so that it can count PlayerId of PlayerTableWinning instead of COUNT(1)
Updated: #Ivan Stoev
By adding the g into the group.
group new { p, s, g }
And sum the group
Trophy = grp.Sum(item => item.w != null ? 1 : 0)
It return the correct answer. However, it is using SUM instead of count. The SQL query generated is as below:
exec sp_executesql N'SELECT
[GroupBy1].[K2] AS [PlayerId],
CAST( [GroupBy1].[K1] AS int) AS [C1],
[GroupBy1].[K4] AS [NickName],
[GroupBy1].[K3] AS [UserName],
[GroupBy1].[A1] AS [C2]
FROM ( SELECT
[Filter1].[K1] AS [K1],
[Filter1].[K2] AS [K2],
[Filter1].[K3] AS [K3],
[Filter1].[K4] AS [K4],
SUM([Filter1].[A1]) AS [A1]
FROM ( SELECT
[Extent1].[Seat] AS [K1],
[Extent2].[PlayerId] AS [K2],
[Extent2].[UserName] AS [K3],
[Extent2].[NickName] AS [K4],
CASE WHEN ( NOT (([Extent4].[GameTableId] IS NULL) AND ([Extent4].[PlayerId] IS NULL) AND ([Extent4].[GameRoundId] IS NULL))) THEN 1 ELSE 0 END AS [A1]
FROM [dbo].[tbl_PlayerTableSeat] AS [Extent1]
INNER JOIN [dbo].[tbl_Player] AS [Extent2] ON [Extent1].[PlayerId] = [Extent2].[PlayerId]
INNER JOIN [dbo].[tbl_GameVirtualTable] AS [Extent3] ON [Extent1].[GameVirtualTableId] = [Extent3].[GameVirtualTableId]
LEFT OUTER JOIN [dbo].[tbl_PlayerTableWinning] AS [Extent4] ON ([Extent1].[PlayerId] = [Extent4].[PlayerId]) AND ([Extent3].[GameTableId] = [Extent4].[GameTableId])
WHERE [Extent1].[GameVirtualTableId] = #p__linq__0
) AS [Filter1]
GROUP BY [K1], [K2], [K3], [K4]
) AS [GroupBy1]',N'#p__linq__0 int',#p__linq__0=36
The only (but significant) difference between SQL COUNT(field) and COUNT(1) is that the former is excluding the NULL values, which when applied to the normally required field from the right side of a left outer join like in your case produces a different result when there are no matching records - the former returns 0 while the latter returns 1.
The "natural" LINQ equivalent would be Count(field != null), but that unfortunately is translated to a quite different SQL by the current EF query provider. So in such cases I personally use the closer equivalent expression Sum(field != null ? 1 : 0) which produces a much better SQL.
In order to apply the above to your query, you'll need an access to w inside the grouping, so change
group new { p, s }
to
group new { p, s, w }
and then use
Trophy = grp.Sum(item => item.w != null ? 1 : 0)
I have a LINQ query that gets data via Entity Framework Code First from an SQL database. This works, but it works very very slow.
This is the original query:
var tmpResult = from mdv in allMetaDataValues
where mdv.Metadata.InputType == MetadataInputType.String && mdv.Metadata.ShowInFilter && !mdv.Metadata.IsHidden && !string.IsNullOrEmpty(mdv.ValueString)
group mdv by new
{
mdv.ValueString,
mdv.Metadata
} into g
let first = g.FirstOrDefault()
select new
{
MetadataTitle = g.Key.Metadata.Title,
MetadataID = g.Key.Metadata.ID,
CollectionColor = g.Key.Metadata.Collection.Color,
CollectionID = g.Key.Metadata.Collection.ID,
MetadataValueCount = 0,
MetadataValueTitle = g.Key.ValueString,
MetadataValueID = first.ID
};
This is the generated SQL from the original query:
{SELECT
0 AS [C1],
[Project4].[Title] AS [Title],
[Project4].[ID] AS [ID],
[Extent9].[Color] AS [Color],
[Project4].[Collection_ID] AS [Collection_ID],
[Project4].[ValueString] AS [ValueString],
[Project4].[C1] AS [C2]
FROM (SELECT
[Project2].[ValueString] AS [ValueString],
[Project2].[ID] AS [ID],
[Project2].[Title] AS [Title],
[Project2].[Collection_ID] AS [Collection_ID],
(SELECT TOP (1)
[Filter4].[ID1] AS [ID]
FROM ( SELECT [Extent6].[ID] AS [ID1], [Extent6].[ValueString] AS [ValueString], [Extent7].[Collection_ID] AS [Collection_ID1], [Extent8].[ID] AS [ID2], [Extent8].[InputType] AS [InputType], [Extent8].[ShowInFilter] AS [ShowInFilter], [Extent8].[IsHidden] AS [IsHidden1]
FROM [dbo].[MetadataValue] AS [Extent6]
LEFT OUTER JOIN [dbo].[Media] AS [Extent7] ON [Extent6].[Media_ID] = [Extent7].[ID]
INNER JOIN [dbo].[Metadata] AS [Extent8] ON [Extent6].[Metadata_ID] = [Extent8].[ID]
WHERE ( NOT (([Extent6].[ValueString] IS NULL) OR (( CAST(LEN([Extent6].[ValueString]) AS int)) = 0))) AND ([Extent7].[IsHidden] <> cast(1 as bit))
) AS [Filter4]
WHERE (2 = CAST( [Filter4].[InputType] AS int)) AND ([Filter4].[ShowInFilter] = 1) AND ([Filter4].[IsHidden1] <> cast(1 as bit)) AND ([Filter4].[Collection_ID1] = #p__linq__0) AND (([Project2].[ValueString] = [Filter4].[ValueString]) OR (([Project2].[ValueString] IS NULL) AND ([Filter4].[ValueString] IS NULL))) AND (([Project2].[ID] = [Filter4].[ID2]) OR (1 = 0))) AS [C1]
FROM ( SELECT
[Distinct1].[ValueString] AS [ValueString],
[Distinct1].[ID] AS [ID],
[Distinct1].[Title] AS [Title],
[Distinct1].[Collection_ID] AS [Collection_ID]
FROM ( SELECT DISTINCT
[Filter2].[ValueString] AS [ValueString],
[Filter2].[ID3] AS [ID],
[Filter2].[InputType1] AS [InputType],
[Filter2].[Title1] AS [Title],
[Filter2].[ShowInFilter1] AS [ShowInFilter],
[Filter2].[IsHidden2] AS [IsHidden],
[Filter2].[Collection_ID2] AS [Collection_ID]
FROM ( SELECT [Filter1].[ValueString], [Filter1].[Collection_ID3], [Filter1].[IsHidden3], [Filter1].[ID3], [Filter1].[InputType1], [Filter1].[Title1], [Filter1].[ShowInFilter1], [Filter1].[IsHidden2], [Filter1].[Collection_ID2]
FROM ( SELECT [Extent1].[ValueString] AS [ValueString], [Extent2].[Collection_ID] AS [Collection_ID3], [Extent4].[IsHidden] AS [IsHidden3], [Extent5].[ID] AS [ID3], [Extent5].[InputType] AS [InputType1], [Extent5].[Title] AS [Title1], [Extent5].[ShowInFilter] AS [ShowInFilter1], [Extent5].[IsHidden] AS [IsHidden2], [Extent5].[Collection_ID] AS [Collection_ID2]
FROM [dbo].[MetadataValue] AS [Extent1]
LEFT OUTER JOIN [dbo].[Media] AS [Extent2] ON [Extent1].[Media_ID] = [Extent2].[ID]
INNER JOIN [dbo].[Metadata] AS [Extent3] ON [Extent1].[Metadata_ID] = [Extent3].[ID]
LEFT OUTER JOIN [dbo].[Metadata] AS [Extent4] ON [Extent1].[Metadata_ID] = [Extent4].[ID]
LEFT OUTER JOIN [dbo].[Metadata] AS [Extent5] ON [Extent1].[Metadata_ID] = [Extent5].[ID]
WHERE ( NOT (([Extent1].[ValueString] IS NULL) OR (( CAST(LEN([Extent1].[ValueString]) AS int)) = 0))) AND ([Extent2].[IsHidden] <> cast(1 as bit)) AND (2 = CAST( [Extent3].[InputType] AS int)) AND ([Extent3].[ShowInFilter] = 1)
) AS [Filter1]
WHERE [Filter1].[IsHidden3] <> cast(1 as bit)
) AS [Filter2]
WHERE [Filter2].[Collection_ID3] = #p__linq__0
) AS [Distinct1]
) AS [Project2] ) AS [Project4]
LEFT OUTER JOIN [dbo].[Collection] AS [Extent9] ON [Project4].[Collection_ID] = [Extent9].[ID]}
If we remove the "let first = g.FirstOrDefault()" and change "MetadataValueID = first.ID" to "MetadataValueID = 0" so that we just have a fixed ID = 0 for testing purposes, then the data loads very fast and the generated query itself is half the size compared to the original
So it seems that this part is making the query very slow:
let first = g.FirstOrDefault()
...
MetadataValueID = first.ID
};
How can this be rewritten?
If I try to rewrite the code, it is still slow:
MetadataValueID = g.Select(x => x.ID).FirstOrDefault()
or
let first = g.Select(x => x.ID).FirstOrDefault()
...
MetadataValueID = first
};
Any suggestions?
Using EF I have allways felt that it has problems efficiently translating stuff like g.Key.Metadata.Collection, so I try to join more explicitly and to include only fields, that are neccessary for your result. You can use include instead of join using repository pattern.
Then your query would look like this:
from mdv in allMetaDataValues.Include("Metadata").Include("Metadata.Collection")
where mdv.Metadata.InputType == MetadataInputType.String &&
mdv.Metadata.ShowInFilter &&
!mdv.Metadata.IsHidden &&
!string.IsNullOrEmpty(mdv.ValueString)
group mdv by new
{
MetadataID = mdv.Metadata.ID,
CollectionID = mdv.Metadata.Collection.ID,
mdv.Metadata.Title,
mdv.Metadata.Collection.Color,
mdv.ValueString
} into g
let first = g.FirstOrDefault().ID
select new
{
MetadataTitle = g.Key.Title,
MetadataID = g.Key.MetadataID,
CollectionColor = g.Key.Color,
CollectionID = g.Key.CollectionID,
MetadataValueCount = 0,
MetadataValueTitle = g.Key.ValueString,
MetadataValueID = first
}
Good tool for playing with linq is LinqPad.
The problem is also that:
let first = g.FirstOrDefault().ID
cannot be easily translated to SQL see this answer. But this rewrite simplifies the underlying query for it at least. It remains to me unclear, why you need first ID from a set without using orderby.
It could be rewriten like this:
let first = (from f in allMetaDataValues
where f.Metadata.ID == g.Key.MetadataID &&
f.ValuesString == g.Key.ValuesString select f.ID)
.FirstOrDefault()
This way you do not let EF write the query for you and you can specify exactly how to do the select.
To speed up the query you can also consider adding indexes to database according to the generated query - namely index using both colums used in where clause of this let first query.
Try the following solution.
Replace FirstOrDefault() with .Take(1). FirstOrDefault() is not lazy loaded.
var tmpResult = from mdv in allMetaDataValues
where mdv.Metadata.InputType == MetadataInputType.String && mdv.Metadata.ShowInFilter && !mdv.Metadata.IsHidden && !string.IsNullOrEmpty(mdv.ValueString)
group mdv by new
{
mdv.ValueString,
mdv.Metadata
} into g
let first = g.Take(1)
select new
{
MetadataTitle = g.Key.Metadata.Title,
MetadataID = g.Key.Metadata.ID,
CollectionColor = g.Key.Metadata.Collection.Color,
CollectionID = g.Key.Metadata.Collection.ID,
MetadataValueCount = 0,
MetadataValueTitle = g.Key.ValueString,
MetadataValueID = first.ID
};
i have some stored procedure that i use it too much , i want to know that its better that to use temp table in tempdb database or use user defined table and get it from c# dataTable or forget them and use linq c# to handle it in c# code .
which of them have a better performance in below situation :
1. a litter record (one record too 1000)
2. 1000 to 1000000
3. larger 1000000 record
some sample of my stored procedure :
ALTER PROCEDURE [dbo].[doc_SyncServer_GetTablesFromTemps]
(
#SyncMode int,
#LastExportDate datetime,
#CurrentDate datetime,
#ThisServerGuid uniqueidentifier,
#PartnerServerGuid uniqueidentifier,
#MasterServerGuid uniqueidentifier
) AS
IF OBJECT_ID('tempdb..##SyncLetterWithoutFiles') IS NOT NULL --Drop Temp Table for Candidate Letter Files
DROP TABLE ##SyncLetters
--Letters Without Files--
SELECT
files.[ID], files.[ReferenceID],
files.[ReferenceGuid],
files.[ReferenceFlag], files.[ReferenceSoftwareGuid],
files.[FileTypeID], files.[FileName], files.[Date], files.[Size], files.[Comments], files.[ModificationDate],
files.[FileNatureID], files.[Guid], files.[ModificationServerGuid], files.[RepositoryGuid], files.[Hash], files.[Age]
INTO ##SyncLetterWithoutFiles
FROM
doc_SyncInquiryList inquiry LEFT JOIN
com_Files files ON inquiry.[Guid] = files.[ReferenceGuid]
WHERE
inquiry.[ReferenceFlag] = 75 AND inquiry.[ServerGuid] = #PartnerServerGuid
DELETE
doc_SyncInquiryList
FROM
doc_SyncInquiryList inquiry INNER JOIN
##SyncLetterWithoutFiles temp ON inquiry.[Guid] = temp.[Guid]
WHERE
inquiry.[ReferenceFlag] = 75
-- Letters --
SELECT
letters.[ID], [CentralDeptNo], [IncomingNo], [IncomingDate], [SequenceNo], [Subject], [Summary], [Keywords], [DeliverFrom], [DeliverTo],
[DeliverDate], [DeliverComments], [SourceFlag], [Type], [LetterNo], [Security], [LetterType], [Indicator], [AttachmentCount],
[BodyFileCount], [WordDocCount], [SenderTitle], [ToReceiverList], [Date], [PartitionFactor], [Priority], [RegisterarUserFullName],
[RegisterationDate], [Archived], letters.[Guid], letters.[ModificationDate], letters.[Age], [ModificationServerGuid], [FirstRootInstanceGuid],
[FirstRootInstanceOwnerGuid], [FirstRootInstanceOwnerSecretarialGuid], [SecurityGuid], [LetterTypeGuid], [PriorityGuid],
[TrackingStatusGuid], [IndicatorGuid], [SecretarialGuid], [RegisterarUserGuid], [SourceGuid]
FROM
doc_vwLettersForSync letters INNER JOIN
##SyncLetters ON ##SyncLetters.[ID] = letters.[ID]
-- LetterInstances --
SELECT
instances.[ID], [InstanceDate], [DeadlineDate], instances.[Comments], [Flag], [ViewDate], [SenderTitle], [ToReceiverList], [BrowseSenderTitle],
[BrowseToReceiverList], [FolderTitle], [FolderFlag], [OwnerTitle], [Priority], [ForwardedToOwner], [AttachmentCount], [HasDrawing],
[ForwardDate], [ChildCount], [ChildToReceiversList], [BrowseChildToReceiversList], [ForwarderFullname], [Hierarchy], [Unlisted],
[Unread], [AlertCount], [NoteCount], [OwnerNote], [OwnerNotePublic], [InWorkflow], [Conditions], [AttachedObjects], [PartitionFactor],
[LetterPartitionFactor], [TerminationDate], instances.[Guid], instances.[ModificationDate], instances.[Age], [ModificationServerGuid], [LetterGuid], [OwnerGuid],
[OwnerDeptGuid], [ParentInstanceGuid], [PriorityGuid], [FolderGuid], [TopChildrenGuid], [OrderIndex],[Status]
FROM
doc_vwLetterInstancesForSync instances INNER JOIN
##SyncLetterInstances ON ##SyncLetterInstances.[ID] = instances.[ID]
-- Drafts --
SELECT
drafts.[ID], [Date], [Subject], [Body], [SenderTitle], [ToReceiverList], [BrowseToReceiverList], [CCReceiverList], [DestinationLetterReceivers],
[Priority], [Security], [AttachmentCount], [BodyFileCount], [ReferenceCount], [DestinationLetterType], [PartitionFactor], drafts.[Guid],
drafts.[ModificationDate], drafts.[Age], [ModificationServerGuid], [ParentDraftGuid], [PriorityGuid], [SecurityGuid], [SenderGuid],
[SenderInstanceGuid], [SenderSecretarialGuid], [RegisterarUserGuid], [DestinationSenderGuid], [DestinationSecretarialGuid]
FROM
doc_vwDraftsForSync drafts INNER JOIN
##SyncDrafts ON ##SyncDrafts.[ID] = drafts.[ID]
-- DraftInstances --
SELECT
instances.[ID], [Flag], [ViewDate], [OwnerTitle], [FolderFlag], [Unlisted], [Unread], [AlertCount], [NoteCount], [OwnerNote],
[OwnerNotePublic], [Relapsed], [RelapseComments], [PartitionFactor], instances.[Guid], instances.[ModificationDate], instances.[Age], [ModificationServerGuid],
[DraftGuid], [OwnerGuid], [FolderGuid], [RelapsedPendingLetterGuid], [TargetPendingLetterGuid], [TargetLetterGuid], instances.[Comments]
FROM
doc_vwDraftInstancesForSync instances INNER JOIN
##SyncDraftInstances ON ##SyncDraftInstances.[ID] = instances.[ID]
-- Messages --
SELECT
messages.[ID], [Date], [Subject], [Body], [SenderTitle], [ToReceiverList], [BrowseToReceiverList], [CCReceiverList], [BCCReceiverList], [AttachmentCount],
[PartitionFactor], messages.[Guid], messages.[ModificationDate], messages.[Age], [ModificationServerGuid], [ParentMessageGuid], [SenderGuid]
FROM
doc_vwMessagesForSync messages INNER JOIN
##SyncMessages ON ##SyncMessages.[ID] = messages.[ID]
-- MessageInstances --
SELECT
instances.[ID], [Flag], [ViewDate], [RequestReadReceipt], [Unlisted], [Unread], [AlertCount], [NoteCount], [OwnerNote], [OwnerNotePublic], [PartitionFactor],
instances.[Guid], instances.[ModificationDate], instances.[Age], [ModificationServerGuid], [MessageGuid], [FolderGuid], [OwnerGuid]
FROM
doc_vwMessageInstancesForSync instances INNER JOIN
##SyncMessageInstances ON ##SyncMessageInstances.[ID] = instances.[ID]
-- PendingLetters --
SELECT
pendings.[ID], [TargetLetterGuid], [TargetLetterDate], [TargetLetterNo], [SourceTitle], [SourceType], [ReferenceNo], [Date], [Subject], [Body], [Keywords], [Processed], [ReferenceFlag], [ViewDate],
[Uid], [Sent], [CentralDeptNo], [DestinationLetterReceivers], [Status], [ModificationSyncSent], [NoteCount], [PartitionFactor], pendings.[Guid],
pendings.[ModificationDate], pendings.[Age], [ModificationServerGuid], [DepartmentGuid], [SourceGuid], [PriorityGuid], [SecurityGuid],
[LetterTypeGuid], [ReferenceGuid], [ViewerUserGuid], [RegisterarUserGuid]
FROM
doc_vwPendingLettersForSync pendings INNER JOIN
##SyncPendingLetters ON ##SyncPendingLetters.[ID] = pendings.[ID]
-- LetterReferences --
SELECT
[ReferenceFlag], [ReferenceSoftwareGuid], [ReferenceNo], [ReferenceDate], [ReferenceObjectType], letters.[Guid] AS [ReferenceGuid],
types.[Guid] AS [ReferenceTypeGuid], objects.[Guid] AS [ReferenceObjectGuid],[ReferenceNoType]
FROM
(SELECT * FROM doc_LetterReferences WHERE [ReferenceFlag] = 1 /*ReferenceFlag.Letter*/) ref INNER JOIN
##SyncLetters letters ON letters.[ID] = ref.[ReferenceID] INNER JOIN
com_Permanents types ON types.[ID] = ref.[ReferenceTypeID] LEFT JOIN
doc_Letters objects ON objects.[ID] = ref.[ReferenceObjectID]
UNION
SELECT
[ReferenceFlag], [ReferenceSoftwareGuid], [ReferenceNo], [ReferenceDate], [ReferenceObjectType], drafts.[Guid] AS [ReferenceGuid],
types.[Guid] AS [ReferenceTypeGuid], objects.[Guid] AS [ReferenceObjectGuid],[ReferenceNoType]
FROM
(SELECT * FROM doc_LetterReferences WHERE [ReferenceFlag] = 3 /*ReferenceFlag.Draft*/) ref INNER JOIN
##SyncDrafts drafts ON drafts.[ID] = ref.[ReferenceID] INNER JOIN
com_Permanents types ON types.[ID] = ref.[ReferenceTypeID] LEFT JOIN
doc_Letters objects ON objects.[ID] = ref.[ReferenceObjectID]
UNION
SELECT
[ReferenceFlag], [ReferenceSoftwareGuid], [ReferenceNo], [ReferenceDate], [ReferenceObjectType], pendings.[Guid] AS [ReferenceGuid],
types.[Guid] AS [ReferenceTypeGuid], objects.[Guid] AS [ReferenceObjectGuid],[ReferenceNoType]
FROM
(SELECT * FROM doc_LetterReferences WHERE [ReferenceFlag] = 14 /*ReferenceFlag.PendingLetter*/) ref INNER JOIN
##SyncPendingLetters pendings ON pendings.[ID] = ref.[ReferenceID] INNER JOIN
com_Permanents types ON types.[ID] = ref.[ReferenceTypeID] LEFT JOIN
doc_Letters objects ON objects.[ID] = ref.[ReferenceObjectID]
-- LetterInstancePersons --
SELECT
[Flag], [Comments], [Sent], [Forwarded], instances.[Guid] AS [LetterInstanceGuid], persons.[Guid] AS [PersonGuid], instancePersons.[OrderIndex]
FROM
doc_LetterInstancePersons instancePersons INNER JOIN
##SyncLetterInstances instances ON instances.[ID] = instancePersons.[LetterInstanceID] INNER JOIN
com_OrganizationRelatedPeople persons ON persons.[ID] = instancePersons.[PersonID]
-- LetterArchiveFolders --
SELECT
letters.[Guid] AS [LetterGuid], archives.[Guid] AS [ArchiveFolderGuid]
FROM
doc_LetterArchiveFolders letterArchives INNER JOIN
##SyncLetters letters ON letters.[ID] = letterArchives.[LetterID] INNER JOIN
doc_ArchiveFolders archives ON archives.[ID] = letterArchives.[ArchiveFolderID]
-- DraftDestinationReceivers --
SELECT
drafts.[Guid] AS [DraftGuid], receivers.[ReceiverTitle], receivers.[SendType], receivers.[OrderIndex], receivers.[ReceiverType], receivers.[Comments],
CASE
WHEN [ReceiverType] = 1 /*DocumentOwnerType.Staff*/ THEN staff.[Guid]
WHEN [ReceiverType] = 4 /*DocumentOwnerType.ExternalPerson*/ THEN externalPersons.[Guid]
WHEN [ReceiverType] = 5 /*DocumentOwnerType.Department*/ THEN depts.[Guid]
END AS [ReceiverGuid]
FROM
doc_DraftDestinationReceivers receivers INNER JOIN
##SyncDrafts drafts ON drafts.[ID] = receivers.[DraftID] LEFT JOIN
com_Staff staff ON staff.[ID] = receivers.[ReceiverID] LEFT JOIN
com_OrganizationRelatedPeople externalPersons ON externalPersons.[ID] = receivers.[ReceiverID] LEFT JOIN
com_Departments depts ON depts.[ID] = receivers.[ReceiverID]
-- PendingLetterDestinationReceivers --
SELECT
pendings.[Guid] AS [PendingLetterGuid], receivers.[ReceiverTitle], receivers.[SendType], receivers.[OrderIndex], receivers.[ReceiverType], receivers.[Comments],
CASE
WHEN [ReceiverType] = 1 /*DocumentOwnerType.Staff*/ THEN staff.[Guid]
WHEN [ReceiverType] = 4 /*DocumentOwnerType.ExternalPerson*/ THEN externalPersons.[Guid]
WHEN [ReceiverType] = 5 /*DocumentOwnerType.Department*/ THEN depts.[Guid]
END AS [ReceiverGuid]
FROM
doc_PendingLetterDestinationReceivers receivers INNER JOIN
##SyncPendingLetters pendings ON pendings.[ID] = receivers.[PendingLetterID] LEFT JOIN
com_Staff staff ON staff.[ID] = receivers.[ReceiverID] LEFT JOIN
com_OrganizationRelatedPeople externalPersons ON externalPersons.[ID] = receivers.[ReceiverID] LEFT JOIN
com_Departments depts ON depts.[ID] = receivers.[ReceiverID]
--AttachedFiles--
SELECT * FROM
(SELECT
files.[ID],
files.[ReferenceID],
messageInstances.[Guid] AS [ReferenceGuid],
files.[ReferenceFlag], files.[ReferenceSoftwareGuid],
files.[FileTypeID], files.[FileName], files.[Date], files.[Size], files.[Comments], files.[ModificationDate],
files.[FileNatureID], files.[Guid], files.[ModificationServerGuid], files.[RepositoryGuid], files.[Hash], files.[Age]
FROM
##SyncAttachedFiles attachedFiles INNER JOIN
com_Files files ON attachedFiles.[Guid] = files.[Guid] INNER JOIN
doc_MessageInstances messageInstances ON files.[ReferenceID] = messageInstances.[ID]
WHERE
files.[ReferenceFlag] = 30
UNION ALL
SELECT
files.[ID], files.[ReferenceID],
files.[ReferenceGuid],
files.[ReferenceFlag], files.[ReferenceSoftwareGuid],
files.[FileTypeID], files.[FileName], files.[Date], files.[Size], files.[Comments], files.[ModificationDate],
files.[FileNatureID], files.[Guid], files.[ModificationServerGuid], files.[RepositoryGuid], files.[Hash], files.[Age]
FROM
##SyncAttachedFiles attachedFiles INNER JOIN
com_Files files ON attachedFiles.[Guid] = files.[Guid]
WHERE
files.[ReferenceFlag] != 30
UNION ALL
SELECT
[ID], [ReferenceID],
[ReferenceGuid],
[ReferenceFlag], [ReferenceSoftwareGuid],
[FileTypeID],[FileName],[Date], [Size], [Comments], [ModificationDate],
[FileNatureID], [Guid], [ModificationServerGuid],[RepositoryGuid], [Hash],[Age]
FROM
##SyncLetterWithoutFiles) SyncAttachedFiles
ORDER BY [ID]
ALTER PROCEDURE [dbo].[doc_Letters_SyncBulkInsert]
#ObjectTable doc_Sync_Letters READONLY
AS
INSERT INTO doc_Letters
([CentralDeptNo], [IncomingNo], [IncomingDate], [SecurityID], [LetterTypeID], [IndicatorID], [SequenceNo], [Subject], [Summary], [Keywords], [DeliverFrom], [DeliverTo],
[DeliverDate], [DeliverComments], [SourceFlag], [SecretarialID], [Type], [LetterNo], [Security], [LetterType], [Indicator], [AttachmentCount], [BodyFileCount], [WordDocCount],
[SenderTitle], [ToReceiverList], [Date], [PartitionFactor], [PriorityID], [Priority], [RegisterarUserID], [RegisterarUserFullName], [RegisterationDate], [Archived], [TrackingStatusID],
[ModificationServerGuid], [Guid], [ModificationDate], [Age],[FirstRootInstanceOwnerID], [FirstRootInstanceOwnerSecretarialID])
SELECT
tempLetters.[CentralDeptNo],
tempLetters.[IncomingNo],
tempLetters.[IncomingDate],
0 AS [SecurityID],
0 AS [LetterTypeID],
indicators.[ID] AS [IndicatorID],
tempLetters.[SequenceNo],
tempLetters.[Subject],
tempLetters.[Summary],
tempLetters.[Keywords],
tempLetters.[DeliverFrom],
tempLetters.[DeliverTo],
tempLetters.[DeliverDate],
tempLetters.[DeliverComments],
tempLetters.[SourceFlag],
0 AS [SecretarialID],
tempLetters.[Type],
tempLetters.[LetterNo],
tempLetters.[Security],
tempLetters.[LetterType],
tempLetters.[Indicator],
tempLetters.[AttachmentCount],
tempLetters.[BodyFileCount],
tempLetters.[WordDocCount],
tempLetters.[SenderTitle],
tempLetters.[ToReceiverList],
tempLetters.[Date],
tempLetters.[PartitionFactor],
0 AS [PriorityID],
tempLetters.[Priority],
0 AS RegisterarUserID,
tempLetters.[RegisterarUserFullName],
tempLetters.[RegisterationDate],
tempLetters.[Archived],
0 AS [TrackingStatusID],
tempLetters.[ModificationServerGuid],
tempLetters.[Guid],
tempLetters.[ModificationDate],
tempLetters.[Age],
0 AS [FirstRootInstanceOwnerID],
0 AS [FirstRootInstanceOwnerSecretarialID]
FROM #ObjectTable tempLetters LEFT JOIN
doc_Letters letters ON tempLetters.[Guid] = letters.[Guid] LEFT JOIN
doc_Indicators indicators ON indicators.[Guid] = tempLetters.[IndicatorGuid]
WHERE
letters.[Guid] IS NULL
If you are performing lot of operations on your temp table then it will better to use Linq because SQL is good for set based operation while C# has better performance in logical operations. But in you'r SP you are mostly using set based operation so it is good to have them in SQL.
I have a very heavy LINQ-to-SQL query, which does a number of joins onto different tables to return an anonymous type. The problem is, if the amount of rows returned is fairly large (> 200), then the query becomes awfully slow and ends up timing out. I know I can increase the data context timeout setting, but that's a last resort.
I'm just wondering if my query would work better if I split it up, and do my comparisons as LINQ-to-Objects queries so I can possibly even use PLINQ to maximise the the processing power. But I'm that's a foreign concept to me, and I can't get my head around on how I would split it up. Can anyone offer any advice? I'm not asking for the code to be written for me, just some general guidance on how I could improve this would be great.
Note I've ensured the database has all the correct keys that I'm joining on, and I've ensured these keys are up to date.
The query is below:
var cons = (from c in dc.Consignments
join p in dc.PODs on c.IntConNo equals p.Consignment into pg
join d in dc.Depots on c.DeliveryDepot equals d.Letter
join sl in dc.Accounts on c.Customer equals sl.LegacyID
join ss in dc.Accounts on sl.InvoiceAccount equals ss.LegacyID
join su in dc.Accounts on c.Subcontractor equals su.Name into sug
join sub in dc.Accountsubbies on ss.ID equals sub.AccountID into subg
where (sug.FirstOrDefault() == null
|| sug.FirstOrDefault().Customer == false)
select new
{
ID = c.ID,
IntConNo = c.IntConNo,
LegacyID = c.LegacyID,
PODs = pg.DefaultIfEmpty(),
TripNumber = c.TripNumber,
DropSequence = c.DropSequence,
TripDate = c.TripDate,
Depot = d.Name,
CustomerName = c.Customer,
CustomerReference = c.CustomerReference,
DeliveryName = c.DeliveryName,
DeliveryTown = c.DeliveryTown,
DeliveryPostcode = c.DeliveryPostcode,
VehicleText = c.VehicleReg + c.Subcontractor,
SubbieID = sug.DefaultIfEmpty().FirstOrDefault().ID.ToString(),
SubbieList = subg.DefaultIfEmpty(),
ScanType = ss.PODScanning == null ? 0 : ss.PODScanning
});
Here's the generated SQL as requested:
{SELECT [t0].[ID], [t0].[IntConNo], [t0].[LegacyID], [t6].[test], [t6].[ID] AS [ID2], [t6].[Consignment], [t6].[Status], [t6].[NTConsignment], [t6].[CustomerRef], [t6].[Timestamp], [t6].[SignedBy], [t6].[Clause], [t6].[BarcodeNumber], [t6].[MainRef], [t6].[Notes], [t6].[ConsignmentRef], [t6].[PODedBy], (
SELECT COUNT(*)
FROM (
SELECT NULL AS [EMPTY]
) AS [t10]
LEFT OUTER JOIN (
SELECT NULL AS [EMPTY]
FROM [dbo].[PODs] AS [t11]
WHERE [t0].[IntConNo] = [t11].[Consignment]
) AS [t12] ON 1=1
) AS [value], [t0].[TripNumber], [t0].[DropSequence], [t0].[TripDate], [t1].[Name] AS [Depot], [t0].[Customer] AS [CustomerName], [t0].[CustomerReference], [t0].[DeliveryName], [t0].[DeliveryTown], [t0].[DeliveryPostcode], [t0].[VehicleReg] + [t0].[Subcontractor] AS [VehicleText], CONVERT(NVarChar,(
SELECT [t16].[ID]
FROM (
SELECT TOP (1) [t15].[ID]
FROM (
SELECT NULL AS [EMPTY]
) AS [t13]
LEFT OUTER JOIN (
SELECT [t14].[ID]
FROM [dbo].[Account] AS [t14]
WHERE [t0].[Subcontractor] = [t14].[Name]
) AS [t15] ON 1=1
ORDER BY [t15].[ID]
) AS [t16]
)) AS [SubbieID],
(CASE
WHEN [t3].[PODScanning] IS NULL THEN #p0
ELSE [t3].[PODScanning]
END) AS [ScanType], [t3].[ID] AS [ID3]
FROM [dbo].[Consignments] AS [t0]
INNER JOIN [dbo].[Depots] AS [t1] ON [t0].[DeliveryDepot] = [t1].[Letter]
INNER JOIN [dbo].[Account] AS [t2] ON [t0].[Customer] = [t2].[LegacyID]
INNER JOIN [dbo].[Account] AS [t3] ON [t2].[InvoiceAccount] = [t3].[LegacyID]
LEFT OUTER JOIN ((
SELECT NULL AS [EMPTY]
) AS [t4]
LEFT OUTER JOIN (
SELECT 1 AS [test], [t5].[ID], [t5].[Consignment], [t5].[Status], [t5].[NTConsignment], [t5].[CustomerRef], [t5].[Timestamp], [t5].[SignedBy], [t5].[Clause], [t5].[BarcodeNumber], [t5].[MainRef], [t5].[Notes], [t5].[ConsignmentRef], [t5].[PODedBy]
FROM [dbo].[PODs] AS [t5]
) AS [t6] ON 1=1 ) ON [t0].[IntConNo] = [t6].[Consignment]
WHERE ((NOT (EXISTS(
SELECT TOP (1) NULL AS [EMPTY]
FROM [dbo].[Account] AS [t7]
WHERE [t0].[Subcontractor] = [t7].[Name]
ORDER BY [t7].[ID]
))) OR (NOT (((
SELECT [t9].[Customer]
FROM (
SELECT TOP (1) [t8].[Customer]
FROM [dbo].[Account] AS [t8]
WHERE [t0].[Subcontractor] = [t8].[Name]
ORDER BY [t8].[ID]
) AS [t9]
)) = 1))) AND ([t2].[Customer] = 1) AND ([t3].[Customer] = 1)
ORDER BY [t0].[ID], [t1].[ID], [t2].[ID], [t3].[ID], [t6].[ID]
}
Try moving the subcontractor join up higher and push the where clause along with it. That way you're not unnecessarily making joins which would fail at the end.
I would also modify the select for the subcontractor id, so you don't get the Id of a potentially null value.
var cons = (from c in dc.Consignments
join su in dc.Accounts on c.Subcontractor equals su.Name into sug
where (sug.FirstOrDefault() == null || sug.FirstOrDefault().Customer == false)
join p in dc.PODs on c.IntConNo equals p.Consignment into pg
join d in dc.Depots on c.DeliveryDepot equals d.Letter
join sl in dc.Accounts on c.Customer equals sl.LegacyID
join ss in dc.Accounts on sl.InvoiceAccount equals ss.LegacyID
join sub in dc.Accountsubbies on ss.ID equals sub.AccountID into subg
let firstSubContractor = sug.DefaultIfEmpty().FirstOrDefault()
select new
{
ID = c.ID,
IntConNo = c.IntConNo,
LegacyID = c.LegacyID,
PODs = pg.DefaultIfEmpty(),
TripNumber = c.TripNumber,
DropSequence = c.DropSequence,
TripDate = c.TripDate,
Depot = d.Name,
CustomerName = c.Customer,
CustomerReference = c.CustomerReference,
DeliveryName = c.DeliveryName,
DeliveryTown = c.DeliveryTown,
DeliveryPostcode = c.DeliveryPostcode,
VehicleText = c.VehicleReg + c.Subcontractor,
SubbieID = firstSubContractor == null ? "" : firstSubContractor.ID.ToString(),
SubbieList = subg.DefaultIfEmpty(),
ScanType = ss.PODScanning == null ? 0 : ss.PODScanning
});