In my database there is a table Question with Content and Answer. I implemented a search engine which uses this:
private Expression<Func<QuestionModel, bool>> CreatePredicate(string query, bool negate = false)
{
query = query.ToUpper();
if (IsATag(ref query))
{
return question => negate == question.Tags.Any(tag => tag.NamesCollection
.Any(translation =>
translation.Language == currentLanguage &&
translation.Translation.ToUpper().Equals(query)));
}
else if(IsSpecial(ref query))
{
return question => true;
}
else
{
return question => negate == question.Content.ToUpper().Contains(query) ||
question.Answer.ToUpper().Contains(query) ||
question.Specialization.NamesCollection.Any(trans =>
trans.Language == currentLanguage &&
trans.Translation.ToUpper().Contains(query));
}
}
Actually it supports multiple queries but this is nicely handled by PrebicateBuilder
I created some performance tests and it doesn't run very fast for 10K records (up to 30 seconds if I use 5 queries. Is there a smart way to enhance the process? Honestly I've never seen a website running so slow, so there must be something.
This is the sql generated by my query:
SELECT TOP (100)
[top].[Id] AS [Id],
[top].[Content] AS [Content],
[top].[Answer] AS [Answer],
[top].[Assessment] AS [Assessment],
[top].[State] AS [State],
[top].[AuthorId] AS [AuthorId],
[top].[AttachmentFileName] AS [AttachmentFileName],
[top].[SubmissionDate] AS [SubmissionDate],
[top].[Specialization_Id] AS [Specialization_Id],
[top].[Doctor_Id] AS [Doctor_Id]
FROM ( SELECT [Project16].[Id] AS [Id], [Project16].[Content] AS [Content], [Project16].[Answer] AS [Answer], [Project16].[Assessment] AS [Assessment], [Project16].[State] AS [State], [Project16].[AuthorId] AS [AuthorId], [Project16].[AttachmentFileName] AS [AttachmentFileName], [Project16].[SubmissionDate] AS [SubmissionDate], [Project16].[Specialization_Id] AS [Specialization_Id], [Project16].[Doctor_Id] AS [Doctor_Id]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Content] AS [Content],
[Extent1].[Answer] AS [Answer],
[Extent1].[Assessment] AS [Assessment],
[Extent1].[State] AS [State],
[Extent1].[AuthorId] AS [AuthorId],
[Extent1].[AttachmentFileName] AS [AttachmentFileName],
[Extent1].[SubmissionDate] AS [SubmissionDate],
[Extent1].[Specialization_Id] AS [Specialization_Id],
[Extent1].[Doctor_Id] AS [Doctor_Id]
FROM [QuestionModels] AS [Extent1]
WHERE ((#p__linq__0 = (CASE WHEN ((CHARINDEX(#p__linq__1, UPPER([Extent1].[Content]))) > 0) THEN cast(1 as bit) WHEN ( NOT ((CHARINDEX(#p__linq__1, UPPER([Extent1].[Content]))) > 0)) THEN cast(0 as bit) END)) OR ((CHARINDEX(#p__linq__2, UPPER([Extent1].[Answer]))) > 0) OR ( EXISTS (SELECT
1 AS [C1]
FROM [TranslationModels] AS [Extent2]
WHERE ([Extent2].[SpecializationModel_Id] IS NOT NULL) AND ([Extent1].[Specialization_Id] = [Extent2].[SpecializationModel_Id]) AND ([Extent2].[Language] = #p__linq__3) AND ((CHARINDEX(#p__linq__4, UPPER([Extent2].[Translation]))) > 0)
)) OR (#p__linq__5 = (CASE WHEN ((CHARINDEX(#p__linq__6, UPPER([Extent1].[Content]))) > 0) THEN cast(1 as bit) WHEN ( NOT ((CHARINDEX(#p__linq__6, UPPER([Extent1].[Content]))) > 0)) THEN cast(0 as bit) END)) OR ((CHARINDEX(#p__linq__7, UPPER([Extent1].[Answer]))) > 0) OR ( EXISTS (SELECT
1 AS [C1]
FROM [TranslationModels] AS [Extent3]
WHERE ([Extent3].[SpecializationModel_Id] IS NOT NULL) AND ([Extent1].[Specialization_Id] = [Extent3].[SpecializationModel_Id]) AND ([Extent3].[Language] = #p__linq__8) AND ((CHARINDEX(#p__linq__9, UPPER([Extent3].[Translation]))) > 0)
)) OR (#p__linq__10 = (CASE WHEN ((CHARINDEX(#p__linq__11, UPPER([Extent1].[Content]))) > 0) THEN cast(1 as bit) WHEN ( NOT ((CHARINDEX(#p__linq__11, UPPER([Extent1].[Content]))) > 0)) THEN cast(0 as bit) END)) OR ((CHARINDEX(#p__linq__12, UPPER([Extent1].[Answer]))) > 0) OR ( EXISTS (SELECT
1 AS [C1]
FROM [TranslationModels] AS [Extent4]
WHERE ([Extent4].[SpecializationModel_Id] IS NOT NULL) AND ([Extent1].[Specialization_Id] = [Extent4].[SpecializationModel_Id]) AND ([Extent4].[Language] = #p__linq__13) AND ((CHARINDEX(#p__linq__14, UPPER([Extent4].[Translation]))) > 0)
)) OR (#p__linq__15 = (CASE WHEN ( EXISTS (SELECT
1 AS [C1]
FROM ( SELECT
[Extent5].[TagModel_Id] AS [TagModel_Id]
FROM [TagModelQuestionModels] AS [Extent5]
WHERE [Extent1].[Id] = [Extent5].[QuestionModel_Id]
) AS [Project4]
WHERE EXISTS (SELECT
1 AS [C1]
FROM [TranslationModels] AS [Extent6]
WHERE ([Project4].[TagModel_Id] = [Extent6].[TagModel_Id]) AND ([Extent6].[Language] = #p__linq__16) AND ((UPPER([Extent6].[Translation])) = #p__linq__17)
)
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT
1 AS [C1]
FROM ( SELECT
[Extent7].[TagModel_Id] AS [TagModel_Id]
FROM [TagModelQuestionModels] AS [Extent7]
WHERE [Extent1].[Id] = [Extent7].[QuestionModel_Id]
) AS [Project7]
WHERE EXISTS (SELECT
1 AS [C1]
FROM [TranslationModels] AS [Extent8]
WHERE ([Project7].[TagModel_Id] = [Extent8].[TagModel_Id]) AND ([Extent8].[Language] = #p__linq__16) AND ((UPPER([Extent8].[Translation])) = #p__linq__17)
)
)) THEN cast(0 as bit) END))) AND (#p__linq__18 = (CASE WHEN ( EXISTS (SELECT
1 AS [C1]
FROM ( SELECT
[Extent9].[TagModel_Id] AS [TagModel_Id]
FROM [TagModelQuestionModels] AS [Extent9]
WHERE [Extent1].[Id] = [Extent9].[QuestionModel_Id]
) AS [Project10]
WHERE EXISTS (SELECT
1 AS [C1]
FROM [TranslationModels] AS [Extent10]
WHERE ([Project10].[TagModel_Id] = [Extent10].[TagModel_Id]) AND ([Extent10].[Language] = #p__linq__19) AND ((UPPER([Extent10].[Translation])) = #p__linq__20)
)
)) THEN cast(1 as bit) WHEN ( NOT EXISTS (SELECT
1 AS [C1]
FROM ( SELECT
[Extent11].[TagModel_Id] AS [TagModel_Id]
FROM [TagModelQuestionModels] AS [Extent11]
WHERE [Extent1].[Id] = [Extent11].[QuestionModel_Id]
) AS [Project13]
WHERE EXISTS (SELECT
1 AS [C1]
FROM [TranslationModels] AS [Extent12]
WHERE ([Project13].[TagModel_Id] = [Extent12].[TagModel_Id]) AND ([Extent12].[Language] = #p__linq__19) AND ((UPPER([Extent12].[Translation])) = #p__linq__20)
)
)) THEN cast(0 as bit) END))
) AS [Project16]
ORDER BY [Project16].[SubmissionDate] ASC
OFFSET 1000 ROWS
) AS [top]
These db relations don't seem to affect performance very much. If I use a query which doesn't involve them the speed is pretty much the same.
Table full scans and twice-nested per-row subqueries will be slow. A faster approach is to arrange your database such that a query need involve only a single index seek or index range scan.
Many people who want to build a search feature use a search index of some kind. Some relational databases support a limited form of full-text search, and this may suffice for your case.
Many people prefer to use a specialized search index. Lucene is a popular engine, with Solr and Elasticsearch exposing Lucene over the network.
Related
I'm wondering why the generated SQL is checking for nulls on a column that is not nullable (column Value which is a not-null float),
var query = _context.Events
.Select(e => new
{
eventId = e.EventId,
data = e.Data.Take(10).Select(x => new
{
name = x.Name,
value = Math.Round(x.Value,1),
})
});
Generates the following SQL code:
SELECT
[Project2].[EventId] AS [EventId],
[Project2].[DeviceId] AS [DeviceId],
[Project2].[TimeEnd] AS [TimeEnd],
[Project2].[C2] AS [C1],
[Project2].[EventId1] AS [EventId1],
[Project2].[Name] AS [Name],
[Project2].[C1] AS [C2]
FROM ( SELECT
[Limit1].[EventId] AS [EventId],
[Limit1].[TimeEnd] AS [TimeEnd],
[Limit1].[DeviceId] AS [DeviceId],
[Extent2].[Name] AS [Name],
[Extent2].[EventId] AS [EventId1],
CASE WHEN ([Extent2].[Value] IS NULL) THEN CAST(NULL AS float) ELSE ROUND([Extent2].[Value], 1) END AS [C1],
CASE WHEN ([Extent2].[Value] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C2]
FROM (SELECT [Project1].[EventId] AS [EventId], [Project1].[TimeEnd] AS [TimeEnd], [Project1].[DeviceId] AS [DeviceId]
FROM ( SELECT
[Extent1].[EventId] AS [EventId],
[Extent1].[TimeEnd] AS [TimeEnd],
[Extent1].[DeviceId] AS [DeviceId]
FROM [dbo].[Events] AS [Extent1]
WHERE ([Extent1].[DeviceId] = 1)
) AS [Project1]
ORDER BY [Project1].[TimeEnd] DESC ) AS [Limit1]
LEFT OUTER JOIN [dbo].[Octaves] AS [Extent2] ON [Limit1].[EventId] = [Extent2].[EventId]
) AS [Project2]
ORDER BY [Project2].[TimeEnd] DESC, [Project2].[EventId] ASC, [Project2].[C2] ASC
If I remove the two CASE WHEN ([Extent2].[Value] IS NULL) and just leave ROUND([Limit2].[Value], 1) AS [C1] and also remove the ORDER BY Project2].[C2] ASC in SQL Server Management the query speeds up.
There is a LEFT OUTER JOIN, so the values (in the query) can definitely be NULL (even if the underlying table definition is not nullable).
See http://docs.oracle.com/javadb/10.8.3.0/ref/rrefsqlj18922.html (for Oracle - but the same basic pattern applies for all the main SQL vendors).
In this case, it is a bit pointless since ROUND(NULL, 1) would return NULL anyway (well, it would on SQL Server). But there isn't much you can do about that, given you can't control the SQL it generates.
I'm starting with Linq to entities and maybe someone could shed some light.
I have two tables - Vizite (parent table) and AngajatiVizite (child table). I'm using Database first, so I have created the relationship between them using Vizite.Id and AngajatiVizite.IdVizita.
I need to get the rows from Vizite and one more bit field which must be 0 if the DataStart or DataEnd fields are null or count of child records from AngajatiVizite is zero. That's it, if the Vizite has zero subordinate records or any of those Data#### fields is null, the calculated field is 0.
So far so good, the linq I'm using works properly. The syntax I have used is this one:
var list = ctx.Vizite
.OrderBy(p => p.DataEnd != null && p.DataStart != null && p.AngajatiVizite.Count > 0)
.ThenBy(p => p.Data)
.Select(p => new
{
p.Id,
p.Numar,
p.Data,
p.DataStart,
p.DataEnd,
Programat = p.DataEnd != null && p.DataStart != null && p.AngajatiVizite.Count > 0
})
.ToList();
The sql command generated by Linq is extremely complex and I don't understand why it has to be that complex and what's the difference.
What I'm getting from linq is this:
SELECT
[Project6].[Numar] AS [Numar],
[Project6].[Id] AS [Id],
[Project6].[Data] AS [Data],
[Project6].[DataStart] AS [DataStart],
[Project6].[DataEnd] AS [DataEnd],
[Project6].[C2] AS [C1]
FROM ( SELECT
[Project5].[C1] AS [C1],
[Project5].[Id] AS [Id],
[Project5].[Numar] AS [Numar],
[Project5].[Data] AS [Data],
[Project5].[DataStart] AS [DataStart],
[Project5].[DataEnd] AS [DataEnd],
CASE WHEN ([Project5].[C2] > 0) THEN cast(1 as bit) WHEN ( NOT ([Project5].[C3] > 0)) THEN cast(0 as bit) END AS [C2]
FROM ( SELECT
[Project4].[C1] AS [C1],
[Project4].[Id] AS [Id],
[Project4].[Numar] AS [Numar],
[Project4].[Data] AS [Data],
[Project4].[DataStart] AS [DataStart],
[Project4].[DataEnd] AS [DataEnd],
[Project4].[C2] AS [C2],
(SELECT
COUNT(1) AS [A1]
FROM [dbo].[AngajatiVizite] AS [Extent5]
WHERE [Project4].[Id] = [Extent5].[IdVizita]) AS [C3]
FROM ( SELECT
[Project3].[C1] AS [C1],
[Project3].[Id] AS [Id],
[Project3].[Numar] AS [Numar],
[Project3].[Data] AS [Data],
[Project3].[DataStart] AS [DataStart],
[Project3].[DataEnd] AS [DataEnd],
(SELECT
COUNT(1) AS [A1]
FROM [dbo].[AngajatiVizite] AS [Extent4]
WHERE [Project3].[Id] = [Extent4].[IdVizita]) AS [C2]
FROM ( SELECT
CASE WHEN ([Project2].[C1] > 0) THEN cast(1 as bit) WHEN ( NOT ([Project2].[C2] > 0)) THEN cast(0 as bit) END AS [C1],
[Project2].[Id] AS [Id],
[Project2].[Numar] AS [Numar],
[Project2].[Data] AS [Data],
[Project2].[DataStart] AS [DataStart],
[Project2].[DataEnd] AS [DataEnd]
FROM ( SELECT
[Project1].[Id] AS [Id],
[Project1].[Numar] AS [Numar],
[Project1].[Data] AS [Data],
[Project1].[DataStart] AS [DataStart],
[Project1].[DataEnd] AS [DataEnd],
[Project1].[C1] AS [C1],
(SELECT
COUNT(1) AS [A1]
FROM [dbo].[AngajatiVizite] AS [Extent3]
WHERE [Project1].[Id] = [Extent3].[IdVizita]) AS [C2]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Numar] AS [Numar],
[Extent1].[Data] AS [Data],
[Extent1].[DataStart] AS [DataStart],
[Extent1].[DataEnd] AS [DataEnd],
(SELECT
COUNT(1) AS [A1]
FROM [dbo].[AngajatiVizite] AS [Extent2]
WHERE [Extent1].[Id] = [Extent2].[IdVizita]) AS [C1]
FROM [dbo].[Vizite] AS [Extent1]
) AS [Project1]
) AS [Project2]
) AS [Project3]
) AS [Project4]
) AS [Project5]
) AS [Project6]
when all I needed was actually this:
Select
Vizite.Id
, Vizite.Numar
, Vizite.Data
, Vizite.DataStart
, Vizite.DataEnd
, Case
When DataStart != Null And DataEnd != Null And (Select Count(Id) From AngajatiVizite Where Vizite.Id = AngajatiVizite.IdVizita) > 0 Then 1
Else 0
End As Programat
From Vizite
Order By Programat, Data
Can anyone please explain to me why the generated SQL is that complex that's even almost impossible to figure it out by simply reading the sql syntax?
Thank you
Entity Framework doesn't build handsome queries, that's a fact; and it's a nuisance sometimes, because it can be really hard to trace SQL logging back to LINQ statements.
It would be a problem though, if the query plan optimizer wouldn't know how to handle them. Fortunately, when Sql Server is concerned, the EF team has managed to make SQL better optimizable in each release since EF5. So generally you shouldn't worry about it too much and only start looking into it when performance is worse than can reasonably be expected.
There are some rules of the thumb though. One of them is to calculate computed values only once. This is where the let keyword comes in handy:
var list = (from p in ctx.Vizite
let Programat = p.DataEnd != null && p.DataStart != null
&& p.AngajatiVizite.Count > 0
order by Programat, p.Data
select new
{
p.Id,
p.Numar,
p.Data,
p.DataStart,
p.DataEnd,
Programat
}).ToList();
This works well in LINQ query syntax. In fluent (method) syntax you can do the exact same thing, but that requires two subsequent Select statements.
What happens to the complexity of your SQL statement if you do the following?
var list = ctx.Vizite
.Select(p => new
{
p.Id,
p.Numar,
p.Data,
p.DataStart,
p.DataEnd,
Programat =
p.DataEnd != null && p.DataStart != null && p.AngajatiVizite.Count > 0
}
.OrderBy(p => p.Programat)
.ThenBy(p => p.Data)
.ToList();
My hope is that by not repeating p.DataEnd != null && p.DataStart != null && p.AngajatiVizite.Count > 0 and moving the OrderBy and ThenBy after the select, you'll get a simpler query.
Edit
To potentially simplify the SQL even further, you could opt to do some of the work after obtaining the raw data from the database:
var list = ctx.Vizite
.Select(p => new
{
p.Id,
p.Numar,
p.Data,
p.DataStart,
p.DataEnd,
AngajatiViziteCount = p.AngajatiVizite.Count
}
.AsEnumerable() // do the rest of the work using LINQ to objects
.OrderBy(p => p.DataEnd != null && p.DataStart != null && p.AngajatiViziteCount > 0)
.ThenBy(p => p.Data)
.ToList();
I have a list of StudentGrades which contains TermNumber, I want to group them by TermNumber. However, when I look at the results, the ones with null TermNumbers are not returned/grouped.
IQueryable<StudentGradeDm> query = GetListQuery().Where(m => m.StudentId == studentId);
IQueryable<StudentGradeDto> groupedQuery = query.GroupBy(m => m.TermNumber)
.Select(m => new StudentGradeDto
{
TermNumber = m.Key,
StudentGrades = m.ToList()
});
return groupedQuery;
As you can see from the lovely screenshot i've taken here .. there are 3 groups, however, the first group is null because their TermNumber is null. But theoretically, who cares if its null? The StudentGrades with null TermNumber should still be a group.
I understand that one fix to this would be to call
query.ToList().GroupBy ......
But this is not an option for me, as the streamlined application will take a query such as the one above, and feed it into a generic pagination and sort function, in which only a subset of records will be fetched from the database to improve performance.
Any expert inputs would be greatly appreciated!
Update: Here is the generated SQL
{SELECT
[Project2].[C2] AS [C1],
[Project2].[C1] AS [C2],
[Project2].[C3] AS [C3],
[Project2].[Id] AS [Id],
[Project2].[StudentId] AS [StudentId],
[Project2].[Year] AS [Year],
[Project2].[TermTypeId] AS [TermTypeId],
[Project2].[TermNumber] AS [TermNumber],
[Project2].[ClassId] AS [ClassId],
[Project2].[GradeId] AS [GradeId],
[Project2].[FileId] AS [FileId],
[Project2].[CreatedById] AS [CreatedById],
[Project2].[CreatedDate] AS [CreatedDate],
[Project2].[ModifiedById] AS [ModifiedById],
[Project2].[ModifiedDate] AS [ModifiedDate],
[Project2].[Deleted] AS [Deleted]
FROM ( SELECT
[Distinct1].[C1] AS [C1],
1 AS [C2],
[Extent2].[Id] AS [Id],
[Extent2].[StudentId] AS [StudentId],
[Extent2].[Year] AS [Year],
[Extent2].[TermTypeId] AS [TermTypeId],
[Extent2].[TermNumber] AS [TermNumber],
[Extent2].[ClassId] AS [ClassId],
[Extent2].[GradeId] AS [GradeId],
[Extent2].[FileId] AS [FileId],
[Extent2].[CreatedById] AS [CreatedById],
[Extent2].[CreatedDate] AS [CreatedDate],
[Extent2].[ModifiedById] AS [ModifiedById],
[Extent2].[ModifiedDate] AS [ModifiedDate],
[Extent2].[Deleted] AS [Deleted],
CASE WHEN ([Extent2].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C3]
FROM (SELECT DISTINCT
[Extent1].[TermNumber] AS [C1]
FROM [dbo].[StudentGrade] AS [Extent1]
WHERE ([Extent1].[Deleted] <> 1) AND ([Extent1].[StudentId] = #p__linq__0) ) AS [Distinct1]
LEFT OUTER JOIN [dbo].[StudentGrade] AS [Extent2] ON ([Extent2].[Deleted] <> 1) AND ([Extent2].[StudentId] = #p__linq__0) AND ([Distinct1].[C1] = [Extent2].[TermNumber])
) AS [Project2]
ORDER BY [Project2].[C1] ASC, [Project2].[C3] ASC}
I'm trying to write a simple SQL query in LinQ, and no matter how hard I try, I always get a complex query.
Here is the SQL I am trying to achieve (this is not what I'm getting):
SELECT
ClearingAccounts.ID,
SUM(CASE WHEN Payments.StatusID = 1 THEN Payments.TotalAmount ELSE 0 END) AS Sum1,
SUM(CASE WHEN DirectDebits.StatusID = 2 THEN DirectDebits.TotalAmount ELSE 0 END) AS Sum2,
SUM(CASE WHEN Payments.StatusID = 2 THEN Payments.TotalAmount ELSE 0 END) AS Sum3,
SUM(CASE WHEN DirectDebits.StatusID = 1 THEN DirectDebits.TotalAmount ELSE 0 END) AS Sum4
FROM ClearingAccounts
LEFT JOIN Payments ON Payments.ClearingAccountID = ClearingAccounts.ID
LEFT JOIN DirectDebits ON DirectDebits.ClearingAccountID = ClearingAccounts.ID
GROUP BY ClearingAccounts.ID
Here is the code:
from clearingAccount in clearingAccounts
let payments = clearingAccount.Payments
let directDebits = clearingAccount.DirectDebits
select new
{
ID = clearingAccount.ID,
Sum1 = payments.Sum(p => p.StatusID == 1 ? p.TotalAmount : 0),
Sum2 = directDebits.Sum(p => p.StatusID == 2 ? p.TotalAmount : 0),
Sum3 = payments.Sum(p => p.StatusID == 2 ? p.TotalAmount : 0),
Sum4 = directDebits.Sum(p => p.StatusID == 1 ? p.TotalAmount : 0),
}
The generated query gets the data from the respective table for each sum, so four times. I'm not sure if it's even possible to optimize this?
EDIT Here the is generated query:
SELECT
[Project5].[ID] AS [ID],
[Project5].[C1] AS [C1],
[Project5].[C2] AS [C2],
[Project5].[C3] AS [C3],
[Project5].[C4] AS [C4]
FROM ( SELECT
[Project4].[ID] AS [ID],
[Project4].[C1] AS [C1],
[Project4].[C2] AS [C2],
[Project4].[C3] AS [C3],
(SELECT
SUM([Filter5].[A1]) AS [A1]
FROM ( SELECT
CASE WHEN (1 = [Extent5].[StatusID]) THEN [Extent5].[TotalAmount] ELSE cast(0 as decimal(18)) END AS [A1]
FROM [dbo].[DirectDebits] AS [Extent5]
WHERE [Project4].[ID] = [Extent5].[ClearingAccountID]
) AS [Filter5]) AS [C4]
FROM ( SELECT
[Project3].[ID] AS [ID],
[Project3].[C1] AS [C1],
[Project3].[C2] AS [C2],
(SELECT
SUM([Filter4].[A1]) AS [A1]
FROM ( SELECT
CASE WHEN (2 = [Extent4].[StatusID]) THEN [Extent4].[TotalAmount] ELSE cast(0 as decimal(18)) END AS [A1]
FROM [dbo].[Payments] AS [Extent4]
WHERE [Project3].[ID] = [Extent4].[ClearingAccountID]
) AS [Filter4]) AS [C3]
FROM ( SELECT
[Project2].[ID] AS [ID],
[Project2].[C1] AS [C1],
(SELECT
SUM([Filter3].[A1]) AS [A1]
FROM ( SELECT
CASE WHEN (2 = [Extent3].[StatusID]) THEN [Extent3].[TotalAmount] ELSE cast(0 as decimal(18)) END AS [A1]
FROM [dbo].[DirectDebits] AS [Extent3]
WHERE [Project2].[ID] = [Extent3].[ClearingAccountID]
) AS [Filter3]) AS [C2]
FROM ( SELECT
[Project1].[ID] AS [ID],
(SELECT
SUM([Filter2].[A1]) AS [A1]
FROM ( SELECT
CASE WHEN (1 = [Extent2].[StatusID]) THEN [Extent2].[TotalAmount] ELSE cast(0 as decimal(18)) END AS [A1]
FROM [dbo].[Payments] AS [Extent2]
WHERE [Project1].[ID] = [Extent2].[ClearingAccountID]
) AS [Filter2]) AS [C1]
FROM ( SELECT
[Extent1].[ID] AS [ID]
FROM [dbo].[ClearingAccounts] AS [Extent1]
WHERE ([Extent1].[CustomerID] = 3) AND ([Extent1].[Deleted] <> 1)
) AS [Project1]
) AS [Project2]
) AS [Project3]
) AS [Project4]
) AS [Project5]
Edit
Note that as per #usr's comment, that your original Sql Query is broken. By LEFT OUTER joining on two independent tables, and then grouping on the common join key, as soon as one of the DirectDebits or Payments tables returns more than one row, you will erroneously duplicate the TotalAmount value in the 'other' SUMmed colums (and vice versa). e.g. If a given ClearingAccount has 3 DirectDebits and 4 Payments, you will get a total of 12 rows (whereas you should be summing 3 and 4 rows independently for the two tables). A better Sql Query would be:
WITH ctePayments AS
(
SELECT
ClearingAccounts.ID,
-- Note the ELSE 0 projection isn't required as nulls are eliminated from aggregates
SUM(CASE WHEN Payments.StatusID = 1 THEN Payments.TotalAmount END) AS Sum1,
SUM(CASE WHEN Payments.StatusID = 2 THEN Payments.TotalAmount END) AS Sum3
FROM ClearingAccounts
INNER JOIN Payments ON Payments.ClearingAccountID = ClearingAccounts.ID
GROUP BY ClearingAccounts.ID
),
cteDirectDebits AS
(
SELECT
ClearingAccounts.ID,
SUM(CASE WHEN DirectDebits.StatusID = 2 THEN DirectDebits.TotalAmount END) AS Sum2,
SUM(CASE WHEN DirectDebits.StatusID = 1 THEN DirectDebits.TotalAmount END) AS Sum4
FROM ClearingAccounts
INNER JOIN DirectDebits ON DirectDebits.ClearingAccountID = ClearingAccounts.ID
GROUP BY ClearingAccounts.ID
)
SELECT ca.ID, COALESCE(p.Sum1, 0) AS Sum1, COALESCE(d.Sum2, 0) AS Sum2,
COALESCE(p.Sum3, 0) AS Sum3, COALESCE(d.Sum4, 0) AS Sum4
FROM
ClearingAccounts ca
LEFT OUTER JOIN ctePayments p
ON ca.ID = p.ID
LEFT OUTER JOIN cteDirectDebits d
ON ca.ID = d.ID;
-- GROUP BY not required, since we have already guaranteed at most one row
-- per joined table in the CTE's, assuming ClearingAccounts.ID is unique;
You'll want to fix and test this with test cases before you even contemplate conversion to LINQ.
Old Answer(s)
The Sql construct:
SELECT SUM(CASE WHEN ... THEN 1 ELSE 0 END) AS Something
when applied in a SELECT list, is a common hack 'alternative' to pivot data from the 'greater' select into columns which meet the projection criteria (and hence the zero if not matched) . It isn't really a sum at all, its a 'matched' count.
With regards to optimizing the Sql generated, another alternative would be to materialize the data after joining and grouping (and of course, if there is a predicate WHERE clause, apply that in Sql too via IQueryable), and then do the conditional summation in memory:
var result2 = Db.ClearingAccounts
.Include(c => c.Payments)
.Include(c => c.DirectDebits)
.GroupBy(c => c.Id)
.ToList() // or any other means to force materialization here.
.ToDictionary(
grp => grp.Key,
grp => new
{
PaymentsByStatus = grp.SelectMany(x => x.Payments)
.GroupBy(p => p.StatusId),
DirectDebitByStatus = grp.SelectMany(x => x.Payments)
.GroupBy(p => p.StatusId),
})
.Select(ca => new
{
ID = ca.Key,
Sum1 = ca.Value.PaymentsByStatus.Where(pbs => pbs.Key == 1)
.Select(pbs => pbs.Select(x => x.TotalAmount).Sum()),
Sum2 = ca.Value.DirectDebitByStatus.Where(pbs => pbs.Key == 2)
.Select(ddbs => ddbs.Select(x => x.TotalAmount).Sum()),
Sum3 = ca.Value.PaymentsByStatus.Where(pbs => pbs.Key == 2)
.Select(pbs => pbs.Select(x => x.TotalAmount).Sum()),
Sum4 = ca.Value.DirectDebitByStatus.Where(pbs => pbs.Key == 1)
.Select(ddbs => ddbs.Select(x => x.TotalAmount).Sum())
});
However, personally, I would leave this pivot projection directly in Sql, and then use something like SqlQuery to then deserialize the result back from Sql
directly into the final Entity type.
1)
Add AsNoTracking in EF to avoid tracking changes.
Check that you have indexes on the columns you are using for the JOINs. Especially the column that you are using to group by. Profile the query and optimize it. EF has also overhead over a stored procedure.
or
2) If you cannot find a way to make it as fast as you need, create a stored procedure and call it from EF. Even the same query will be faster.
I'm using 6.1.1 of entity framework against SQL Server 2012.
I've attached the query in question to this issue.
When run against our DB directly (i.e. from the SQL Generated) this query responds in < 15ms.
However, when processed in Entity Framework it's ~650 ms to materialization to the projection (thus no tracking causing issues)
Further, when using Web API and Odata to do a top 100 it's even slower. (i.e. if I do a .ToArray() on the results in the endpoint in Web API controller first, it's faster than the filtered top 100 query through odata.
Worse, the inlinecount query by oData which causes a groupby on a count to be generated by Entity Framework, which is odd, takes < 10ms to process on the server, but takes the same length of time for Entity Framework to process as the full select thus doubling the service time.
I can understand some overhead, but we're talking about 100 records into a projection, it shouldn't take more than half a second to process this.
Also, I would have expected that, given the complexity of the where clause (generated using predicates) that the second time this was hit, that it would be drastically faster because of dynamic caching in EF 5+ but it doesn't make any difference at all.
I can provide you with the DTO that it's projecting into if it matters but it's just strings and datetimeoffsets straight up with no computation or anything.
Also note that all tests were done in release mode, not debug mode. And timers were used to isolate the 2 separate queries in a custom formatter and time how long it took entity framework to service them.
I went down to the DB level and created a DB reader and looped through the results and manually created the DTOs to work with (all of them, not the filtered ones) using the exact same query generated, just putting in different values for the params and it completed in < 30 ms for reference.
If you'd like me to somehow provide you with the built up predicate that generated this, please let me know and give me instructions and I'll be happy to do so.
Here's the resulting query (which again ,runs in 30 ms, but Entity Framework takes 2.5 seconds to produce the result and get it out the door with WebAPI:
SELECT TOP (#p__linq__15)
[Project45].[Status] AS [Status],
[Project45].[C6] AS [C1],
[Project45].[C7] AS [C2],
[Project45].[C1] AS [C3],
[Project45].[C8] AS [C4],
[Project45].[Priority] AS [Priority],
[Project45].[C9] AS [C5],
[Project45].[Name1] AS [Name],
[Project45].[C10] AS [C6],
[Project45].[Name2] AS [Name1],
[Project45].[C11] AS [C7],
[Project45].[C12] AS [C8],
[Project45].[C4] AS [C9],
[Project45].[C13] AS [C10],
[Project45].[C5] AS [C11],
[Project45].[C14] AS [C12],
[Project45].[C3] AS [C13],
[Project45].[C15] AS [C14],
[Project45].[C2] AS [C15],
[Project45].[C16] AS [C16],
[Project45].[Identifier] AS [Identifier],
[Project45].[C17] AS [C17],
[Project45].[Name] AS [Name2],
[Project45].[C18] AS [C18],
[Project45].[ID] AS [ID]
FROM ( SELECT
[Filter1].[ID1] AS [ID],
[Filter1].[Identifier] AS [Identifier],
[Filter1].[Name1] AS [Name],
[Filter1].[Priority] AS [Priority],
[Filter1].[Status] AS [Status],
[Filter1].[Name2] AS [Name1],
[Filter1].[Name3] AS [Name2],
CAST( [Filter1].[DueDate] AS datetimeoffset) AS [C1],
CAST( CASE WHEN ([Filter1].[CancelledOn] IS NULL) THEN CASE WHEN ([Filter1].[RejectedOn1] IS NULL) THEN CASE WHEN ([Filter1].[SubmittedOn] IS NULL) THEN [Filter1].[CreatedOn] ELSE [Filter1].[SubmittedOn] END ELSE [Filter1].[RejectedOn1] END ELSE [Filter1].[CancelledOn] END AS datetimeoffset) AS [C2],
CASE WHEN ([Filter1].[CancelledByID] IS NOT NULL) THEN [Filter1].[UserName1] WHEN ([Filter1].[RejectedByID1] IS NOT NULL) THEN [Filter1].[UserName2] WHEN ([Filter1].[SubmittedByID] IS NOT NULL) THEN [Filter1].[UserName3] ELSE [Filter1].[UserName4] END AS [C3],
CASE WHEN ([Filter1].[FirstName1] IS NULL) THEN N'' ELSE [Filter1].[FirstName2] END + N' ' + CASE WHEN ([Filter1].[LastName1] IS NULL) THEN N'' ELSE [Filter1].[LastName2] END AS [C4],
CAST( [Filter1].[SubmittedOn] AS datetimeoffset) AS [C5],
N'c4eabbaf-7433-430d-bdd7-2f9180bce7cc' AS [C6],
N'DueDate' AS [C7],
N'Priority' AS [C8],
N'Project' AS [C9],
N'RequestType' AS [C10],
N'Status' AS [C11],
N'SubmittedByName' AS [C12],
N'SubmittedOn' AS [C13],
N'MajorEventBy' AS [C14],
N'MajorEventDate' AS [C15],
N'Identifier' AS [C16],
N'Name' AS [C17],
N'ID' AS [C18]
FROM ( SELECT [Extent1].[ID] AS [ID1], [Extent1].[Identifier] AS [Identifier], [Extent1].[Name] AS [Name1], [Extent1].[Priority] AS [Priority], [Extent1].[DueDate] AS [DueDate], [Extent1].[CreatedOn] AS [CreatedOn], [Extent1].[CreatedByID] AS [CreatedByID], [Extent1].[SubmittedOn] AS [SubmittedOn], [Extent1].[SubmittedByID] AS [SubmittedByID], [Extent1].[RejectedOn] AS [RejectedOn1], [Extent1].[RejectedByID] AS [RejectedByID1], [Extent1].[CancelledOn] AS [CancelledOn], [Extent1].[CancelledByID] AS [CancelledByID], [Extent1].[Private] AS [Private], [Extent1].[Status] AS [Status], [Extent2].[UserName] AS [UserName1], [Extent3].[UserName] AS [UserName2], [Extent4].[UserName] AS [UserName3], [Extent5].[UserName] AS [UserName4], [Extent6].[Name] AS [Name2], [Extent7].[Name] AS [Name3], [Extent8].[FirstName] AS [FirstName1], [Extent9].[FirstName] AS [FirstName2], [Extent10].[LastName] AS [LastName1], [Extent11].[LastName] AS [LastName2]
FROM [dbo].[Requests] AS [Extent1]
LEFT OUTER JOIN [dbo].[Users] AS [Extent2] ON [Extent1].[CancelledByID] = [Extent2].[ID]
LEFT OUTER JOIN [dbo].[Users] AS [Extent3] ON [Extent1].[RejectedByID] = [Extent3].[ID]
LEFT OUTER JOIN [dbo].[Users] AS [Extent4] ON [Extent1].[SubmittedByID] = [Extent4].[ID]
INNER JOIN [dbo].[Users] AS [Extent5] ON [Extent1].[CreatedByID] = [Extent5].[ID]
INNER JOIN [dbo].[Projects] AS [Extent6] ON [Extent1].[ProjectID] = [Extent6].[ID]
INNER JOIN [dbo].[RequestTypes] AS [Extent7] ON [Extent1].[RequestTypeID] = [Extent7].[ID]
LEFT OUTER JOIN [dbo].[Users] AS [Extent8] ON [Extent1].[SubmittedByID] = [Extent8].[ID]
LEFT OUTER JOIN [dbo].[Users] AS [Extent9] ON [Extent1].[SubmittedByID] = [Extent9].[ID]
LEFT OUTER JOIN [dbo].[Users] AS [Extent10] ON [Extent1].[SubmittedByID] = [Extent10].[ID]
LEFT OUTER JOIN [dbo].[Users] AS [Extent11] ON [Extent1].[SubmittedByID] = [Extent11].[ID]
WHERE [Extent1].[isDeleted] <> cast(1 as bit)
) AS [Filter1]
WHERE (( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[AclDataMarts] AS [Extent12]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SecurityGroupUsers] AS [Extent13]
WHERE ([Extent12].[SecurityGroupID] = [Extent13].[SecurityGroupID]) AND ([Extent13].[UserID] = #p__linq__0)
)) AND ([Extent12].[PermissionID] IN (cast('5d6dd388-7842-40a1-a27a-b9782a445e20' as uniqueidentifier), cast('a58791b5-e8af-48d0-b9cd-ed0b54e564e6' as uniqueidentifier), cast('0cabf382-93d3-4dac-aa80-2de500a5f945' as uniqueidentifier))) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[RequestDataMarts] AS [Extent14]
WHERE ([Extent12].[DataMartID] = [Extent14].[DataMartID]) AND ([Extent14].[RequestID] = [Filter1].[ID1])
))
)) OR ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[AclProjects] AS [Extent15]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SecurityGroupUsers] AS [Extent16]
WHERE ([Extent15].[SecurityGroupID] = [Extent16].[SecurityGroupID]) AND ([Extent16].[UserID] = #p__linq__1)
)) AND ([Extent15].[PermissionID] IN (cast('5d6dd388-7842-40a1-a27a-b9782a445e20' as uniqueidentifier), cast('a58791b5-e8af-48d0-b9cd-ed0b54e564e6' as uniqueidentifier), cast('0cabf382-93d3-4dac-aa80-2de500a5f945' as uniqueidentifier), cast('0549f5c8-6c0e-4491-be90-ee0f29652422' as uniqueidentifier), cast('40db7de2-eefa-4d31-b400-7e72ab34de99' as uniqueidentifier))) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Requests] AS [Extent17]
WHERE ([Extent15].[ProjectID] = [Extent17].[ProjectID]) AND ([Extent17].[ID] = [Filter1].[ID1])
))
)) OR ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[AclProjectDataMarts] AS [Extent18]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SecurityGroupUsers] AS [Extent19]
WHERE ([Extent18].[SecurityGroupID] = [Extent19].[SecurityGroupID]) AND ([Extent19].[UserID] = #p__linq__2)
)) AND ([Extent18].[PermissionID] IN (cast('5d6dd388-7842-40a1-a27a-b9782a445e20' as uniqueidentifier), cast('a58791b5-e8af-48d0-b9cd-ed0b54e564e6' as uniqueidentifier), cast('0cabf382-93d3-4dac-aa80-2de500a5f945' as uniqueidentifier))) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[RequestDataMarts] AS [Extent20]
WHERE ([Extent18].[DataMartID] = [Extent20].[DataMartID]) AND ([Extent20].[RequestID] = [Filter1].[ID1])
)) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Requests] AS [Extent21]
WHERE ([Extent18].[ProjectID] = [Extent21].[ProjectID]) AND ([Extent21].[ID] = [Filter1].[ID1])
))
)) OR ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[AclGlobal] AS [Extent22]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SecurityGroupUsers] AS [Extent23]
WHERE ([Extent22].[SecurityGroupID] = [Extent23].[SecurityGroupID]) AND ([Extent23].[UserID] = #p__linq__3)
)) AND ([Extent22].[PermissionID] IN (cast('5d6dd388-7842-40a1-a27a-b9782a445e20' as uniqueidentifier), cast('a58791b5-e8af-48d0-b9cd-ed0b54e564e6' as uniqueidentifier), cast('0cabf382-93d3-4dac-aa80-2de500a5f945' as uniqueidentifier), cast('5ccb0ec2-006d-4345-895e-5dd2c6c8c791' as uniqueidentifier), cast('0549f5c8-6c0e-4491-be90-ee0f29652422' as uniqueidentifier), cast('40db7de2-eefa-4d31-b400-7e72ab34de99' as uniqueidentifier)))
)) OR ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[AclRequestSharedFolders] AS [Extent24]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SecurityGroupUsers] AS [Extent25]
WHERE ([Extent24].[SecurityGroupID] = [Extent25].[SecurityGroupID]) AND ([Extent25].[UserID] = #p__linq__4)
)) AND ([Extent24].[PermissionID] IN (cast('5ccb0ec2-006d-4345-895e-5dd2c6c8c791' as uniqueidentifier))) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[RequestSharedFolderRequests] AS [Extent26]
WHERE ([Extent24].[RequestSharedFolderID] = [Extent26].[RequestSharedFolderID]) AND ([Extent26].[RequestID] = [Filter1].[ID1])
))
)) OR ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[AclOrganizations] AS [Extent27]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SecurityGroupUsers] AS [Extent28]
WHERE ([Extent27].[SecurityGroupID] = [Extent28].[SecurityGroupID]) AND ([Extent28].[UserID] = #p__linq__5)
)) AND ([Extent27].[PermissionID] IN (cast('0549f5c8-6c0e-4491-be90-ee0f29652422' as uniqueidentifier), cast('40db7de2-eefa-4d31-b400-7e72ab34de99' as uniqueidentifier))) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Requests] AS [Extent29]
WHERE ([Extent27].[OrganizationID] = [Extent29].[OrganizationID]) AND ([Extent29].[ID] = [Filter1].[ID1])
))
)) OR ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[AclProjectOrganizations] AS [Extent30]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SecurityGroupUsers] AS [Extent31]
WHERE ([Extent30].[SecurityGroupID] = [Extent31].[SecurityGroupID]) AND ([Extent31].[UserID] = #p__linq__6)
)) AND ([Extent30].[PermissionID] IN (cast('0549f5c8-6c0e-4491-be90-ee0f29652422' as uniqueidentifier), cast('40db7de2-eefa-4d31-b400-7e72ab34de99' as uniqueidentifier))) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Requests] AS [Extent32]
WHERE ([Extent30].[ProjectID] = [Extent32].[ProjectID]) AND ([Extent32].[ID] = [Filter1].[ID1])
)) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Requests] AS [Extent33]
WHERE ([Extent30].[OrganizationID] = [Extent33].[OrganizationID]) AND ([Extent33].[ID] = [Filter1].[ID1])
))
))) AND ( NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[AclDataMarts] AS [Extent34]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SecurityGroupUsers] AS [Extent35]
WHERE ([Extent34].[SecurityGroupID] = [Extent35].[SecurityGroupID]) AND ([Extent35].[UserID] = #p__linq__7)
)) AND ([Extent34].[PermissionID] IN (cast('5d6dd388-7842-40a1-a27a-b9782a445e20' as uniqueidentifier), cast('a58791b5-e8af-48d0-b9cd-ed0b54e564e6' as uniqueidentifier), cast('0cabf382-93d3-4dac-aa80-2de500a5f945' as uniqueidentifier))) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[RequestDataMarts] AS [Extent36]
WHERE ([Extent34].[DataMartID] = [Extent36].[DataMartID]) AND ([Extent36].[RequestID] = [Filter1].[ID1])
)) AND ([Extent34].[Allowed] <> cast(1 as bit))
)) AND ( NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[AclProjects] AS [Extent37]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SecurityGroupUsers] AS [Extent38]
WHERE ([Extent37].[SecurityGroupID] = [Extent38].[SecurityGroupID]) AND ([Extent38].[UserID] = #p__linq__8)
)) AND ([Extent37].[PermissionID] IN (cast('5d6dd388-7842-40a1-a27a-b9782a445e20' as uniqueidentifier), cast('a58791b5-e8af-48d0-b9cd-ed0b54e564e6' as uniqueidentifier), cast('0cabf382-93d3-4dac-aa80-2de500a5f945' as uniqueidentifier), cast('0549f5c8-6c0e-4491-be90-ee0f29652422' as uniqueidentifier), cast('40db7de2-eefa-4d31-b400-7e72ab34de99' as uniqueidentifier))) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Requests] AS [Extent39]
WHERE ([Extent37].[ProjectID] = [Extent39].[ProjectID]) AND ([Extent39].[ID] = [Filter1].[ID1])
)) AND ([Extent37].[Allowed] <> cast(1 as bit))
)) AND ( NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[AclProjectDataMarts] AS [Extent40]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SecurityGroupUsers] AS [Extent41]
WHERE ([Extent40].[SecurityGroupID] = [Extent41].[SecurityGroupID]) AND ([Extent41].[UserID] = #p__linq__9)
)) AND ([Extent40].[PermissionID] IN (cast('5d6dd388-7842-40a1-a27a-b9782a445e20' as uniqueidentifier), cast('a58791b5-e8af-48d0-b9cd-ed0b54e564e6' as uniqueidentifier), cast('0cabf382-93d3-4dac-aa80-2de500a5f945' as uniqueidentifier))) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[RequestDataMarts] AS [Extent42]
WHERE ([Extent40].[DataMartID] = [Extent42].[DataMartID]) AND ([Extent42].[RequestID] = [Filter1].[ID1])
)) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Requests] AS [Extent43]
WHERE ([Extent40].[ProjectID] = [Extent43].[ProjectID]) AND ([Extent43].[ID] = [Filter1].[ID1])
)) AND ([Extent40].[Allowed] <> cast(1 as bit))
)) AND ( NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[AclGlobal] AS [Extent44]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SecurityGroupUsers] AS [Extent45]
WHERE ([Extent44].[SecurityGroupID] = [Extent45].[SecurityGroupID]) AND ([Extent45].[UserID] = #p__linq__10)
)) AND ([Extent44].[PermissionID] IN (cast('5d6dd388-7842-40a1-a27a-b9782a445e20' as uniqueidentifier), cast('a58791b5-e8af-48d0-b9cd-ed0b54e564e6' as uniqueidentifier), cast('0cabf382-93d3-4dac-aa80-2de500a5f945' as uniqueidentifier), cast('5ccb0ec2-006d-4345-895e-5dd2c6c8c791' as uniqueidentifier), cast('0549f5c8-6c0e-4491-be90-ee0f29652422' as uniqueidentifier), cast('40db7de2-eefa-4d31-b400-7e72ab34de99' as uniqueidentifier))) AND ([Extent44].[Allowed] <> cast(1 as bit))
)) AND ( NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[AclRequestSharedFolders] AS [Extent46]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SecurityGroupUsers] AS [Extent47]
WHERE ([Extent46].[SecurityGroupID] = [Extent47].[SecurityGroupID]) AND ([Extent47].[UserID] = #p__linq__11)
)) AND ([Extent46].[PermissionID] IN (cast('5ccb0ec2-006d-4345-895e-5dd2c6c8c791' as uniqueidentifier))) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[RequestSharedFolderRequests] AS [Extent48]
WHERE ([Extent46].[RequestSharedFolderID] = [Extent48].[RequestSharedFolderID]) AND ([Extent48].[RequestID] = [Filter1].[ID1])
)) AND ([Extent46].[Allowed] <> cast(1 as bit))
)) AND ( NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[AclOrganizations] AS [Extent49]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SecurityGroupUsers] AS [Extent50]
WHERE ([Extent49].[SecurityGroupID] = [Extent50].[SecurityGroupID]) AND ([Extent50].[UserID] = #p__linq__12)
)) AND ([Extent49].[PermissionID] IN (cast('0549f5c8-6c0e-4491-be90-ee0f29652422' as uniqueidentifier), cast('40db7de2-eefa-4d31-b400-7e72ab34de99' as uniqueidentifier))) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Requests] AS [Extent51]
WHERE ([Extent49].[OrganizationID] = [Extent51].[OrganizationID]) AND ([Extent51].[ID] = [Filter1].[ID1])
)) AND ([Extent49].[Allowed] <> cast(1 as bit))
)) AND ( NOT EXISTS (SELECT
1 AS [C1]
FROM [dbo].[AclProjectOrganizations] AS [Extent52]
WHERE ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[SecurityGroupUsers] AS [Extent53]
WHERE ([Extent52].[SecurityGroupID] = [Extent53].[SecurityGroupID]) AND ([Extent53].[UserID] = #p__linq__13)
)) AND ([Extent52].[PermissionID] IN (cast('0549f5c8-6c0e-4491-be90-ee0f29652422' as uniqueidentifier), cast('40db7de2-eefa-4d31-b400-7e72ab34de99' as uniqueidentifier))) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Requests] AS [Extent54]
WHERE ([Extent52].[ProjectID] = [Extent54].[ProjectID]) AND ([Extent54].[ID] = [Filter1].[ID1])
)) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Requests] AS [Extent55]
WHERE ([Extent52].[OrganizationID] = [Extent55].[OrganizationID]) AND ([Extent55].[ID] = [Filter1].[ID1])
)) AND ([Extent52].[Allowed] <> cast(1 as bit))
)) AND (([Filter1].[Private] <> cast(1 as bit)) OR (([Filter1].[Private] = 1) AND ([Filter1].[CreatedByID] = #p__linq__14)))
) AS [Project45]
ORDER BY [Project45].[Identifier] DESC, [Project45].[ID] ASC
EF take a long time to convert LINQ to a SQL query.
Your query is not automaticaly cached because it use an in-memory collection as parameter (the uuid collection).
See MSDN page Performance Considerations (Entity Framework).
A solution (not really ideal) is to serialize this parameter and use the String.Contains operator.