Telerik's RadGridView + WCF Data Services + Entity Framework = Terrible performance - entity-framework

We have a simple LOB application that:
pulls data from EF
serves data across the wire with WCF Data Services
renders that data on Telerik's RadGridView
This works really well in the default scenario as users are able to filter data by using the built-in Telerik filter control which presents all the options they want.
The problem happens when re-constructing the query sent from WCF Data Services when the 'Contains' operator is used:
WCF Data Services adds a bunch of "IIF" lambda expressions which,
EF then expands into T-SQL CASE statements
This takes a query that should look like:
SELECT TOP (25)
[Project1].[TaskID] AS [TaskID],
[Project1].[ProductSubmissionID] AS [ProductSubmissionID],
...
FROM ( SELECT
[Extent1].[TaskID] AS [TaskID],
[Extent1].[ProductSubmissionID] AS [ProductSubmissionID],
...
FROM [dbo].[Task] AS [Extent1]
LEFT OUTER JOIN [dbo].[OperationDataProduct] AS [Extent2] ON [Extent1].[ProductID] = [Extent2].[ProductID]
LEFT OUTER JOIN [dbo].[vProductOwnership] AS [Extent3] ON [Extent1].[ProductID] = [Extent3].[ProductID]
LEFT OUTER JOIN [dbo].[User] AS [Extent4] ON [Extent2].[ChannelManagerID] = [Extent4].[UserID]
LEFT OUTER JOIN [dbo].[User] AS [Extent5] ON [Extent2].[ProductOwnerID] = [Extent5].[UserID]
WHERE [Extent1].Type IN ('Content','Concept','Financial') AND [Extent1].MarketplaceName LIKE '%prod%'
Into one that looks like this:
SELECT TOP (25)
[Project1].[TaskID] AS [TaskID],
[Project1].[ProductSubmissionID] AS [ProductSubmissionID],
...
FROM ( SELECT
[Extent1].[TaskID] AS [TaskID],
[Extent1].[ProductSubmissionID] AS [ProductSubmissionID],
...
FROM [dbo].[Task] AS [Extent1]
LEFT OUTER JOIN [dbo].[OperationDataProduct] AS [Extent2] ON [Extent1].[ProductID] = [Extent2].[ProductID]
LEFT OUTER JOIN [dbo].[vProductOwnership] AS [Extent3] ON [Extent1].[ProductID] = [Extent3].[ProductID]
LEFT OUTER JOIN [dbo].[User] AS [Extent4] ON [Extent2].[ChannelManagerID] = [Extent4].[UserID]
LEFT OUTER JOIN [dbo].[User] AS [Extent5] ON [Extent2].[ProductOwnerID] = [Extent5].[UserID]
WHERE (CASE WHEN (CASE WHEN (((CASE WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END IS NULL) THEN CAST(NULL AS bit) WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%') THEN cast(1 as bit) WHEN ( NOT (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%')) THEN cast(0 as bit) END) = 1) AND ([Extent1].[SubmissionOrTaskType] IN (N'Content',N'Concept',N'Financial'))) THEN cast(1 as bit) WHEN ( NOT (((CASE WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END IS NULL) THEN CAST(NULL AS bit) WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%') THEN cast(1 as bit) WHEN ( NOT (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%')) THEN cast(0 as bit) END) = 1) AND ([Extent1].[SubmissionOrTaskType] IN (N'Content',N'Concept',N'Financial')))) THEN cast(0 as bit) END IS NULL) THEN cast(0 as bit) WHEN (((CASE WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END IS NULL) THEN CAST(NULL AS bit) WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%') THEN cast(1 as bit) WHEN ( NOT (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%')) THEN cast(0 as bit) END) = 1) AND ([Extent1].[SubmissionOrTaskType] IN (N'Content',N'Concept',N'Financial'))) THEN cast(1 as bit) WHEN ( NOT (((CASE WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END IS NULL) THEN CAST(NULL AS bit) WHEN (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%') THEN cast(1 as bit) WHEN ( NOT (CASE WHEN ([Extent1].[MarketplaceName] IS NULL) THEN CAST(NULL AS varchar(1)) ELSE LOWER([Extent1].[MarketplaceName]) END LIKE N'%prod%')) THEN cast(0 as bit) END) = 1) AND ([Extent1].[SubmissionOrTaskType] IN (N'Content',N'Concept',N'Financial')))) THEN cast(0 as bit) END) = 1
) AS [Project1]
ORDER BY [Project1].[TaskID] ASC
My question is: has anyone bumped into this problem before and is there a low cost solution?
I can see re-writing the EF QueryProvider to be a solution, but it's not exactly low cost.
TIA

We wound up writing a QueryProvider to fix this issue. It wound up being lower cost than originally expected, but still adds quiet a bit of unnecessary complexity. The IQToolkit has some decent examples of how to begin this.

Related

Entity framework nested projections are slow

I'm running a query to fetch a users profile. The query get the users details, as well as all the reviews they have posted, as well as the comments in the reviews.
It may well be a case of I'm trying to get back too much, but as the api is getting called from mobile, I'd rather get as much as I can in one network call rather than making multiple network calls.
At the moment this is generating some really long sql, and takes around 25 seconds!
Any tips on how to improve it, or whether projections are even right way to do it
public UserVM GetUserInfo(string userId, string currentUserId)
{
var results =
from u in context.AspNetUsers
where u.Id == userId
select new UserVM
{
Name = u.UserName, Id = u.Id, ProfilePic = u.ProfilePicUrl, FollowerCount = u.Followers.Count, FollowingCount = u.Following.Count,
MemberSince = u.RegisteredDate,
RatingsCount = u.Ratings.Count(x => x.IsDeleted!=true),
FollowedByCurrentUser = currentUserId != null && u.Followers.Any(x => x.FollowedByUserId == currentUserId && x.UserId == userId),
reviews =
from r in u.Ratings
where r.IsDeleted != true
&& r.IsDraft != true
select new RatingVM()
{
ratingId = r.Id,
author_name = r.User.UserName,
userId = r.UserId,
profile_photo_url = r.User.ProfilePicUrl,
rating = r.RatingValue,
text = r.RatingComment,
created = r.Created,
likeCount = r.RatingLikes.Count(x => x.IsLiked && x.RatingId == r.Id),
likedByCurrentUser = currentUserId != null && r.RatingLikes.Any(x => x.IsLiked && x.RatingId == r.Id && x.UserId == currentUserId),
photos = from ri in r.RatingImages
select new PhotoVM { Id = ri.Id, width = 0, height = 0, photo_reference = ri.PhotoUrl, isMember = true, googlePlaceId = r.Place.GooglePlaceId, placeName = r.Place.Name },
comments = from c in r.Comments
where c.IsDeleted != true
select new CommentVM { commentId = c.Id, Created = c.Created, CommentText = c.CommentText, RatingId = r.Id , UserName = c.User.UserName, ProfilePicUrl = c.User.ProfilePicUrl, userId = c.UserId }
}
};
return results.FirstOrDefault();
}
SELECT
[Project17].[C1] AS [C1],
[Project17].[UserName] AS [UserName],
[Project17].[Id] AS [Id],
[Project17].[ProfilePicUrl] AS [ProfilePicUrl],
[Project17].[C32] AS [C2],
[Project17].[C33] AS [C3],
[Project17].[RegisteredDate] AS [RegisteredDate],
[Project17].[C34] AS [C4],
[Project17].[C2] AS [C5],
[Project17].[C31] AS [C6],
[Project17].[C4] AS [C7],
[Project17].[C5] AS [C8],
[Project17].[C6] AS [C9],
[Project17].[C7] AS [C10],
[Project17].[C8] AS [C11],
[Project17].[C9] AS [C12],
[Project17].[C10] AS [C13],
[Project17].[C11] AS [C14],
[Project17].[C12] AS [C15],
[Project17].[C13] AS [C16],
[Project17].[C14] AS [C17],
[Project17].[C3] AS [C18],
[Project17].[C15] AS [C19],
[Project17].[C16] AS [C20],
[Project17].[C17] AS [C21],
[Project17].[C18] AS [C22],
[Project17].[C19] AS [C23],
[Project17].[C20] AS [C24],
[Project17].[C21] AS [C25],
[Project17].[C22] AS [C26],
[Project17].[C23] AS [C27],
[Project17].[C24] AS [C28],
[Project17].[C25] AS [C29],
[Project17].[C26] AS [C30],
[Project17].[C27] AS [C31],
[Project17].[C28] AS [C32],
[Project17].[C29] AS [C33],
[Project17].[C30] AS [C34]
FROM ( SELECT
[Limit1].[Id] AS [Id],
[Limit1].[UserName] AS [UserName],
[Limit1].[ProfilePicUrl] AS [ProfilePicUrl],
[Limit1].[RegisteredDate] AS [RegisteredDate],
[Limit1].[C1] AS [C1],
[Limit1].[C2] AS [C2],
[UnionAll1].[C1] AS [C3],
[UnionAll1].[Id] AS [C4],
[UnionAll1].[Id1] AS [C5],
[UnionAll1].[C2] AS [C6],
[UnionAll1].[C3] AS [C7],
[UnionAll1].[UserId] AS [C8],
[UnionAll1].[C4] AS [C9],
[UnionAll1].[RatingValue] AS [C10],
[UnionAll1].[RatingComment] AS [C11],
[UnionAll1].[Created] AS [C12],
[UnionAll1].[C5] AS [C13],
[UnionAll1].[C6] AS [C14],
[UnionAll1].[Id2] AS [C15],
[UnionAll1].[Id3] AS [C16],
[UnionAll1].[C7] AS [C17],
[UnionAll1].[C8] AS [C18],
[UnionAll1].[PhotoUrl] AS [C19],
[UnionAll1].[C9] AS [C20],
[UnionAll1].[GooglePlaceId] AS [C21],
[UnionAll1].[Name] AS [C22],
[UnionAll1].[C10] AS [C23],
[UnionAll1].[C11] AS [C24],
[UnionAll1].[C12] AS [C25],
[UnionAll1].[C13] AS [C26],
[UnionAll1].[C14] AS [C27],
[UnionAll1].[C15] AS [C28],
[UnionAll1].[C16] AS [C29],
[UnionAll1].[C17] AS [C30],
CASE WHEN ([UnionAll1].[Id] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C31],
[Limit1].[C3] AS [C32],
[Limit1].[C4] AS [C33],
[Limit1].[C5] AS [C34]
FROM (SELECT TOP (1)
#p__linq__4 AS [p__linq__4],
#p__linq__5 AS [p__linq__5],
[Project3].[Id] AS [Id],
[Project3].[UserName] AS [UserName],
[Project3].[ProfilePicUrl] AS [ProfilePicUrl],
[Project3].[RegisteredDate] AS [RegisteredDate],
1 AS [C1],
CASE WHEN ((#p__linq__1 IS NOT NULL) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Followers] AS [Extent5]
WHERE ([Project3].[Id] = [Extent5].[UserId]) AND ([Extent5].[FollowedByUserId] = #p__linq__2) AND ([Extent5].[UserId] = #p__linq__3)
))) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C2],
[Project3].[C1] AS [C3],
[Project3].[C2] AS [C4],
[Project3].[C3] AS [C5]
FROM ( SELECT
[Project2].[Id] AS [Id],
[Project2].[UserName] AS [UserName],
[Project2].[ProfilePicUrl] AS [ProfilePicUrl],
[Project2].[RegisteredDate] AS [RegisteredDate],
[Project2].[C1] AS [C1],
[Project2].[C2] AS [C2],
(SELECT
COUNT(1) AS [A1]
FROM [dbo].[Ratings] AS [Extent4]
WHERE ([Project2].[Id] = [Extent4].[UserId]) AND (1 <> [Extent4].[IsDeleted])) AS [C3]
FROM ( SELECT
[Project1].[Id] AS [Id],
[Project1].[UserName] AS [UserName],
[Project1].[ProfilePicUrl] AS [ProfilePicUrl],
[Project1].[RegisteredDate] AS [RegisteredDate],
[Project1].[C1] AS [C1],
(SELECT
COUNT(1) AS [A1]
FROM [dbo].[Followers] AS [Extent3]
WHERE [Project1].[Id] = [Extent3].[FollowedByUserId]) AS [C2]
FROM ( SELECT
[Extent1].[Id] AS [Id],
[Extent1].[UserName] AS [UserName],
[Extent1].[ProfilePicUrl] AS [ProfilePicUrl],
[Extent1].[RegisteredDate] AS [RegisteredDate],
(SELECT
COUNT(1) AS [A1]
FROM [dbo].[Followers] AS [Extent2]
WHERE [Extent1].[Id] = [Extent2].[UserId]) AS [C1]
FROM [dbo].[AspNetUsers] AS [Extent1]
WHERE [Extent1].[Id] = #p__linq__0
) AS [Project1]
) AS [Project2]
) AS [Project3] ) AS [Limit1]
OUTER APPLY (SELECT
CASE WHEN ([Filter10].[Id1] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1],
[Project9].[Id] AS [Id],
[Project9].[Id] AS [Id1],
[Project9].[C1] AS [C2],
[Project9].[C2] AS [C3],
[Project9].[UserId] AS [UserId],
[Project9].[C3] AS [C4],
[Project9].[RatingValue] AS [RatingValue],
[Project9].[RatingComment] AS [RatingComment],
[Project9].[Created] AS [Created],
[Project9].[C5] AS [C5],
[Project9].[C4] AS [C6],
[Filter10].[Id1] AS [Id2],
[Filter10].[Id1] AS [Id3],
CASE WHEN ([Filter10].[Id1] IS NULL) THEN CAST(NULL AS int) ELSE 0 END AS [C7],
CASE WHEN ([Filter10].[Id1] IS NULL) THEN CAST(NULL AS int) ELSE 0 END AS [C8],
[Filter10].[PhotoUrl] AS [PhotoUrl],
CASE WHEN ([Filter10].[Id1] IS NULL) THEN CAST(NULL AS bit) ELSE cast(1 as bit) END AS [C9],
[Filter10].[GooglePlaceId] AS [GooglePlaceId],
[Filter10].[Name] AS [Name],
CAST(NULL AS int) AS [C10],
CAST(NULL AS int) AS [C11],
CAST(NULL AS datetime2) AS [C12],
CAST(NULL AS varchar(1)) AS [C13],
CAST(NULL AS int) AS [C14],
CAST(NULL AS varchar(1)) AS [C15],
CAST(NULL AS varchar(1)) AS [C16],
CAST(NULL AS varchar(1)) AS [C17]
FROM (SELECT
[Project7].[Id] AS [Id],
[Project7].[RatingValue] AS [RatingValue],
[Project7].[RatingComment] AS [RatingComment],
[Project7].[Created] AS [Created],
[Project7].[PlaceId] AS [PlaceId],
[Project7].[UserId] AS [UserId],
[Limit1].[UserName] AS [C1],
N'' AS [C2],
[Limit1].[ProfilePicUrl] AS [C3],
CASE WHEN ((#p__linq__4 IS NOT NULL) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[RatingLikes] AS [Extent8]
WHERE ([Project7].[Id] = [Extent8].[RatingId]) AND ([Extent8].[IsLiked] = 1) AND ([Extent8].[RatingId] = [Project7].[Id]) AND ([Extent8].[UserId] = #p__linq__5)
))) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C4],
[Project7].[C1] AS [C5]
FROM ( SELECT
[Project6].[Id] AS [Id],
[Project6].[RatingValue] AS [RatingValue],
[Project6].[RatingComment] AS [RatingComment],
[Project6].[Created] AS [Created],
[Project6].[PlaceId] AS [PlaceId],
[Project6].[UserId] AS [UserId],
(SELECT
COUNT(1) AS [A1]
FROM [dbo].[RatingLikes] AS [Extent7]
WHERE ([Project6].[Id] = [Extent7].[RatingId]) AND ([Extent7].[IsLiked] = 1) AND ([Extent7].[RatingId] = [Project6].[Id])) AS [C1]
FROM ( SELECT
[Extent6].[Id] AS [Id],
[Extent6].[RatingValue] AS [RatingValue],
[Extent6].[RatingComment] AS [RatingComment],
[Extent6].[Created] AS [Created],
[Extent6].[PlaceId] AS [PlaceId],
[Extent6].[UserId] AS [UserId]
FROM [dbo].[Ratings] AS [Extent6]
WHERE ([Limit1].[Id] = [Extent6].[UserId]) AND (1 <> [Extent6].[IsDeleted]) AND (1 <> [Extent6].[IsDraft])
) AS [Project6]
) AS [Project7] ) AS [Project9]
OUTER APPLY (SELECT [Extent9].[Id] AS [Id1], [Extent9].[PhotoUrl] AS [PhotoUrl], [Project10].[Name] AS [Name], [Project10].[GooglePlaceId] AS [GooglePlaceId]
FROM [dbo].[RatingImages] AS [Extent9]
LEFT OUTER JOIN (SELECT
[Extent10].[Id] AS [Id],
[Extent10].[Name] AS [Name],
[Extent10].[GooglePlaceId] AS [GooglePlaceId]
FROM [dbo].[Places] AS [Extent10]
WHERE [Project9].[PlaceId] = [Extent10].[Id] ) AS [Project10] ON 1 = 1
WHERE [Project9].[Id] = [Extent9].[RatingId] ) AS [Filter10]
UNION ALL
SELECT
2 AS [C1],
[Project15].[Id] AS [Id],
[Project15].[Id] AS [Id1],
[Project15].[C1] AS [C2],
[Project15].[C2] AS [C3],
[Project15].[UserId] AS [UserId],
[Project15].[C3] AS [C4],
[Project15].[RatingValue] AS [RatingValue],
[Project15].[RatingComment] AS [RatingComment],
[Project15].[Created] AS [Created],
[Project15].[C5] AS [C5],
[Project15].[C4] AS [C6],
CAST(NULL AS int) AS [C7],
CAST(NULL AS int) AS [C8],
CAST(NULL AS int) AS [C9],
CAST(NULL AS int) AS [C10],
CAST(NULL AS varchar(1)) AS [C11],
CAST(NULL AS bit) AS [C12],
CAST(NULL AS varchar(1)) AS [C13],
CAST(NULL AS varchar(1)) AS [C14],
[Join2].[Id2] AS [Id2],
[Join2].[Id2] AS [Id3],
[Join2].[Created] AS [Created1],
[Join2].[CommentText] AS [CommentText],
[Project15].[Id] AS [Id4],
[Join2].[UserName] AS [UserName],
[Join2].[ProfilePicUrl] AS [ProfilePicUrl],
[Join2].[UserId] AS [UserId1]
FROM (SELECT
[Project13].[Id] AS [Id],
[Project13].[RatingValue] AS [RatingValue],
[Project13].[RatingComment] AS [RatingComment],
[Project13].[Created] AS [Created],
[Project13].[UserId] AS [UserId],
[Limit1].[UserName] AS [C1],
N'' AS [C2],
[Limit1].[ProfilePicUrl] AS [C3],
CASE WHEN ((#p__linq__4 IS NOT NULL) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[RatingLikes] AS [Extent13]
WHERE ([Project13].[Id] = [Extent13].[RatingId]) AND ([Extent13].[IsLiked] = 1) AND ([Extent13].[RatingId] = [Project13].[Id]) AND ([Extent13].[UserId] = #p__linq__5)
))) THEN cast(1 as bit) ELSE cast(0 as bit) END AS [C4],
[Project13].[C1] AS [C5]
FROM ( SELECT
[Project12].[Id] AS [Id],
[Project12].[RatingValue] AS [RatingValue],
[Project12].[RatingComment] AS [RatingComment],
[Project12].[Created] AS [Created],
[Project12].[UserId] AS [UserId],
(SELECT
COUNT(1) AS [A1]
FROM [dbo].[RatingLikes] AS [Extent12]
WHERE ([Project12].[Id] = [Extent12].[RatingId]) AND ([Extent12].[IsLiked] = 1) AND ([Extent12].[RatingId] = [Project12].[Id])) AS [C1]
FROM ( SELECT
[Extent11].[Id] AS [Id],
[Extent11].[RatingValue] AS [RatingValue],
[Extent11].[RatingComment] AS [RatingComment],
[Extent11].[Created] AS [Created],
[Extent11].[UserId] AS [UserId]
FROM [dbo].[Ratings] AS [Extent11]
WHERE ([Limit1].[Id] = [Extent11].[UserId]) AND (1 <> [Extent11].[IsDeleted]) AND (1 <> [Extent11].[IsDraft])
) AS [Project12]
) AS [Project13] ) AS [Project15]
INNER JOIN (SELECT [Extent14].[Id] AS [Id2], [Extent14].[CommentText] AS [CommentText], [Extent14].[Created] AS [Created], [Extent14].[RatingId] AS [RatingId], [Extent14].[IsDeleted] AS [IsDeleted], [Extent14].[UserId] AS [UserId], [Extent15].[UserName] AS [UserName], [Extent15].[ProfilePicUrl] AS [ProfilePicUrl]
FROM [dbo].[Comments] AS [Extent14]
INNER JOIN [dbo].[AspNetUsers] AS [Extent15] ON [Extent14].[UserId] = [Extent15].[Id] ) AS [Join2] ON ([Project15].[Id] = [Join2].[RatingId]) AND (1 <> [Join2].[IsDeleted])) AS [UnionAll1]
) AS [Project17]
ORDER BY [Project17].[Id] ASC, [Project17].[C31] ASC, [Project17].[C5] ASC, [Project17].[C3] ASC
EF6's loading strategy of using one big SQL query per LINQ query is not optimal for loading complex object graphs. There are several other ways to get your graph loaded.
For instance you could load the root AspNetUser entity, and then traverse the Navigation Properties to build your graph. EF would Lazy Load as needed. And once an entity is cached in the context, subsequent navigations would not cause additional queries.
In fact if you pre-fetch some or all of the related entities, EF will "stitch" or "fix-up" your navigation properties.
So as an optimization, you can write queries that fetch into the context cache the entities that you will need using a few simple and cheap queries.
something like
var user = context.AspNetUsers.Where(u => u.Id == userId).Single();
var followers = context.Followers.Where(f => f.UserId == userId).ToList();
var ratings = context.Ratings.Where(f => f.UserId == userId).ToList();
var ratingIds = ratings.Select(r => r.Id).ToList();
var ratingLikes = context.RatingLikes.Where(x => ratingIds.Contains(x.RatingId) && x.IsLiked ).ToList();
var ratingPhotos = context.RatingPhotos.Where(x => ratingIds.Contains(x.RatingId)).ToList();
Then build your results from the loaded AspNetUser, eg
var u = user;
var results =
select new UserVM
{
Name = u.UserName,
Id = u.Id,
ProfilePic = u.ProfilePicUrl,
FollowerCount = u.Followers.Count, . . .

Using parameters of Stored Procedure with Join's "ON" area

I have parameters like these
declare #Phl1_descr varchar(50)
SET #Phl1_descr = 'Greece'
declare #Phl2_descr varchar(50)
SET #Phl2_descr = 'Coffee & Beverages'
I want to join two tables with the above parameters (if they are not null), so I tried to do something like below in the "ON" keyword of my JOIN
ON
(CASE WHEN LEN(#Phl1_descr) > 0 THEN A.Phl1_descr ELSE B.Phl1_descr END) = B.Phl1_descr AND
(CASE WHEN LEN(#Phl2_descr) > 0 THEN A.Phl2_descr ELSE B.Phl2_descr END) = B.Phl2_descr
However if I send one of the parameters like as '', it doesn't work. Any simpler idea?
Is it posible to use simpler solution? Like:
IF #Phl1_descr IS NOT NULL AND #Phl2_descr IS NOT NULL
BEGIN
SELECT *
FROM Table1 as A
LEFT JOIN Table2 as B on A.Phl1_descr=B.Phl1_descr and A.Phl2_descr=B.Phl2_descr
END
ELSE IF #Phl1_descr IS NOT NULL AND #Phl2_descr IS NULL
BEGIN
SELECT *
FROM Table1 as A
LEFT JOIN Table2 as B on A.Phl1_descr=B.Phl1_descr
END
ELSE IF #Phl1_descr IS NULL AND #Phl2_descr IS NOT NULL
BEGIN
SELECT *
FROM Table1 as A
LEFT JOIN Table2 as B on A.Phl2_descr=B.Phl2_descr
END
So you will get a simpler execution plans and simpler logic.
You can also use ... CASE WHEN #Phl1_descr IS NULL THEN ... to check NULL values
Interesting but
B.Phl1_descr = B.Phl1_descr
not working but
ISNULL(B.Phl1_descr,'-1') = ISNULL(B.Phl1_descr,'-1')
works,
So just a simple change in the below code work it out
(CASE WHEN LEN(#Phl1_descr) > 1 THEN A.Phl1_descr ELSE ISNULL(B.Phl1_descr,'-1') END) = ISNULL(B.Phl1_descr,'-1') AND
(CASE WHEN LEN(#Phl2_descr) > 1 THEN A.Phl2_descr ELSE ISNULL(B.Phl2_descr,'-1') END) = ISNULL(B.Phl2_descr,'-1') AND

PostgreSQL Select join table and a function

I got a function in my database, that returns a table. What I'm trying to do is select joining a table and that function.
I saw that it can't be done in the FROM place, but if I put the function in the select it takes too long to execute.
My SQL is:
select
"Grades"."id",
SUPRIMENTO_LOJA("Grades"."id", 30, 1, 1)
from
"Grades"
This takes more than 500s to execute
I tried this too but it returns an error
select
*
from
"Grades",
SUPRIMENTO_LOJA("Grades"."id", 30, 1, 1)
Whats the best way to make this select to work?
EDIT:
The function is this:
CREATE OR REPLACE FUNCTION suprimento_loja(grade_id integer, vendas_dias integer, lojaId integer, embalagem numeric(15, 4))
RETURNS TABLE(qtd integer, maximo integer, ultFornecedor character varying(50),
ultCompra character varying(10), ultVenda character varying(10), classeLoja character varying(1),
vendas integer, sugestao integer, giroLoja numeric(15, 4))
AS $$
DECLARE
qtde1 integer;
vendasdiae1 integer;
maximoe1 integer;
ultvendae1 timestamp;
ultcomprae1 timestamp;
ultforne1 varchar(50);
classee1 varchar(1);
vendase1 integer;
qtde2 integer;
vendasdiae2 integer;
maximoe2 integer;
ultvendae2 timestamp;
ultcomprae2 timestamp;
ultforne2 varchar(50);
vendase2 integer;
qtde3 integer;
vendasdiae3 integer;
maximoe3 integer;
ultvendae3 timestamp;
ultcomprae3 timestamp;
ultforne3 varchar(50);
vendase3 integer;
giroe1 numeric(15, 4);
giroe2 numeric(15, 4);
giroe3 numeric(15, 4);
BEGIN
with vendas as (
select
sum(coalesce("quantidade", 0)) as vendas_periodo,
"inventario_id"
from
"WVItens"
where
"WVItens"."ultimoDownload" between current_timestamp - (select to_interval(vendas_dias))
and current_timestamp and coalesce("status", '') = ''
group by
"inventario_id")
select into qtde1, vendasdiae1, maximoe1, ultvendae1, ultcomprae1, ultforne1, classee1, vendase1, giroe1
cast("quantidadeAtual" as integer),
cast("vendasPorDia" as integer),
cast("maximoEmDias" as integer),
"dataUltimaVenda",
"dataUltimaCompra",
"ultimoFornecedor",
"classe",
cast(vendas_periodo as integer),
coalesce("giro", 0)
from
"Inventarios",
"Lojas",
vendas
where
"Inventarios"."estoque_id" = "Lojas"."estoque1_id" and
coalesce("Inventarios"."status", '') = '' and
"Inventarios"."id" = vendas."inventario_id" and
"Lojas"."id" = lojaId and
"grade_id" = gradeId;
with vendas as (
select
sum(coalesce("quantidade", 0)) as vendas_periodo,
"inventario_id"
from
"WVItens"
where
"WVItens"."ultimoDownload" between current_timestamp - (select to_interval(vendas_dias))
and current_timestamp and coalesce("status", '') = ''
group by
"inventario_id")
select into qtde2, vendasdiae2, maximoe2, ultvendae2, ultcomprae2, ultforne2, vendase2, giroe2
cast("quantidadeAtual" as integer),
cast("vendasPorDia" as integer),
cast("maximoEmDias" as integer),
"dataUltimaVenda",
"dataUltimaCompra",
"ultimoFornecedor",
cast(vendas_periodo as integer),
coalesce("giro", 0)
from
"Inventarios",
"Lojas",
vendas
where
"Inventarios"."estoque_id" = "Lojas"."estoque2_id" and
coalesce("Inventarios"."status", '') = '' and
"Inventarios"."id" = vendas."inventario_id" and
"Lojas"."id" = lojaId and
"grade_id" = gradeId;
with vendas as (
select
sum(coalesce("quantidade", 0)) as vendas_periodo,
"inventario_id"
from
"WVItens"
where
"WVItens"."ultimoDownload" between current_timestamp - (select to_interval(vendas_dias))
and current_timestamp and coalesce("status", '') = ''
group by
"inventario_id")
select into qtde3, vendasdiae3, maximoe3, ultvendae3, ultcomprae3, ultforne3, vendase3, giroe3
cast("quantidadeAtual" as integer),
cast("vendasPorDia" as integer),
cast("maximoEmDias" as integer),
"dataUltimaVenda",
"dataUltimaCompra",
"ultimoFornecedor",
cast(vendas_periodo as integer),
coalesce("giro", 0)
from
"Inventarios",
"Lojas",
vendas
where
"Inventarios"."estoque_id" = "Lojas"."estoque2_id" and
coalesce("Inventarios"."status", '') = '' and
"Inventarios"."id" = vendas."inventario_id" and
"Lojas"."id" = lojaId and
"grade_id" = gradeId;
qtd := 0;
IF (qtde1 is not null) THEN
qtd := qtd + qtde1;
END IF;
IF (qtde2 is not null) THEN
qtd := qtd + qtde2;
END IF;
IF (qtde3 is not null) THEN
qtd := qtd + qtde3;
END IF;
giroLoja = (giroe1 + giroe2 + giroe3) / 3;
maximo := 0;
IF ((maximoe1 is not null) and (vendasdiae1 is not null)) THEN
maximo := maximo + (maximoe1 * vendasdiae1);
END IF;
IF ((maximoe2 is not null) and (vendasdiae2 is not null)) THEN
maximo := maximo + (maximoe2 * vendasdiae2);
END IF;
IF ((maximoe3 is not null) and (vendasdiae3 is not null)) THEN
maximo := maximo + (maximoe3 * vendasdiae3);
END IF;
IF (qtde1 is null) THEN
qtde1 := 0;
END IF;
IF (qtde2 is null) THEN
qtde2 := 0;
END IF;
IF (qtde3 is null) THEN
qtde3 := 0;
END IF;
IF (maximoe1 is null) THEN
maximoe1 := 0;
END IF;
IF (maximoe2 is null) THEN
maximoe2 := 0;
END IF;
IF (maximoe3 is null) THEN
maximoe3 := 0;
END IF;
IF (vendasdiae1 is null) THEN
vendasdiae1 := 0;
END IF;
IF (vendasdiae2 is null) THEN
vendasdiae2 := 0;
END IF;
IF (vendasdiae3 is null) THEN
vendasdiae3 := 0;
END IF;
IF (vendase1 is null) THEN
vendase1 := 0;
END IF;
IF (vendase2 is null) THEN
vendase2 := 0;
END IF;
IF (vendase3 is null) THEN
vendase3 := 0;
END IF;
ultCompra := '';
ultVenda := '';
ultFornecedor := '';
IF (ultcomprae1 is null) THEN
IF (ultcomprae2 is null) THEN
IF (ultcomprae3 is not null) THEN
ultCompra := cast(extract(day from ultcomprae3) || '/' || extract(month from ultcomprae3) || '/' || extract(year from ultcomprae3) as varchar(10));
ultFornecedor := ultforne3;
END IF;
ELSE
IF ((ultcomprae3 is null) or (ultcomprae2 > ultcomprae3)) THEN
ultCompra := cast(extract(day from ultcomprae2) || '/' || extract(month from ultcomprae2) || '/' || extract(year from ultcomprae2) as varchar(10));
ultFornecedor := ultforne2;
ELSE
ultCompra := cast(extract(day from ultcomprae3) || '/' || extract(month from ultcomprae3) || '/' || extract(year from ultcomprae3) as varchar(10));
ultFornecedor := ultforne3;
END IF;
END IF;
ELSE
IF ((ultcomprae2 is null) or (ultcomprae1 > ultcomprae2)) THEN
IF ((ultcomprae3 is null) or (ultcomprae1 > ultcomprae3)) THEN
ultCompra := cast(extract(day from ultcomprae1) || '/' || extract(month from ultcomprae1) || '/' || extract(year from ultcomprae1) as varchar(10));
ultFornecedor := ultforne1;
ELSE
IF (ultcomprae3 is not null) THEN
ultCompra := cast(extract(day from ultcomprae3) || '/' || extract(month from ultcomprae3) || '/' || extract(year from ultcomprae3) as varchar(10));
ultFornecedor := ultforne3;
END IF;
END IF;
ELSE
IF ((ultcomprae3 is null) or (ultcomprae2 > ultcomprae3)) THEN
ultCompra := cast(extract(day from ultcomprae2) || '/' || extract(month from ultcomprae2) || '/' || extract(year from ultcomprae2) as varchar(10));
ultFornecedor := ultforne2;
ELSE
ultCompra := cast(extract(day from ultcomprae3) || '/' || extract(month from ultcomprae3) || '/' || extract(year from ultcomprae3) as varchar(10));
ultFornecedor := ultforne3;
END IF;
END IF;
END IF;
IF (ultvendae1 is null) THEN
IF (ultvendae2 is null) THEN
IF (ultvendae3 is not null) THEN
ultVenda := cast(extract(day from ultvendae3) || '/' || extract(month from ultvendae3) || '/' || extract(year from ultvendae3) as varchar(10));
END IF;
ELSE
IF ((ultvendae3 is null) or (ultvendae2 > ultvendae3)) THEN
ultVenda := cast(extract(day from ultvendae2) || '/' || extract(month from ultvendae2) || '/' || extract(year from ultvendae2) as varchar(10));
ELSE
ultVenda := cast(extract(day from ultvendae3) || '/' || extract(month from ultvendae3) || '/' || extract(year from ultvendae3) as varchar(10));
END IF;
END IF;
ELSE
IF ((ultvendae2 is null) or (ultvendae1 > ultvendae2)) THEN
IF ((ultvendae3 is null) or (ultvendae1 > ultvendae3)) THEN
ultVenda := cast(extract(day from ultvendae1) || '/' || extract(month from ultvendae1) || '/' || extract(year from ultvendae1) as varchar(10));
ELSE
IF (ultvendae3 is not null) THEN
ultVenda := cast(extract(day from ultvendae3) || '/' || extract(month from ultvendae3) || '/' || extract(year from ultvendae3) as varchar(10));
END IF;
END IF;
ELSE
IF ((ultvendae3 is null) or (ultvendae2 > ultvendae3)) THEN
ultVenda := cast(extract(day from ultvendae2) || '/' || extract(month from ultvendae2) || '/' || extract(year from ultvendae2) as varchar(10));
ELSE
ultVenda := cast(extract(day from ultvendae3) || '/' || extract(month from ultvendae3) || '/' || extract(year from ultvendae3) as varchar(10));
END IF;
END IF;
END IF;
vendas = vendase1 + vendase2 + vendase3;
classeLoja := classee1;
IF ((qtde1 + qtde2 + qtde3) > ((maximoe1 * vendasdiae1) + (maximoe2 * vendasdiae2) + (maximoe3 * vendasdiae3))) THEN
sugestao := 0;
ELSE
sugestao := cast(((((maximoe1 * vendasdiae1) + (maximoe2 * vendasdiae2) + (maximoe3 * vendasdiae3)) - (qtde1 + qtde2 + qtde3)) + 0.4) as integer);
END IF;
RETURN NEXT;
END
$$ LANGUAGE plpgsql;
I tried with some query but it got too long, and it isnt even the final query that I need
with suprimento as (
with vendas as (
select
sum(coalesce("quantidade", 0)) as vendas_periodo,
"inventario_id"
from
"WVItens"
where
"WVItens"."ultimoDownload" between current_timestamp - (select to_interval(30))
and current_timestamp and coalesce("status", '') = ''
group by
"inventario_id")
select
"grade_id",
"estoque_id",
sum(cast("quantidadeAtual" as integer)) as "quantidade",
sum(cast("vendasPorDia" as integer)) as "vendasPorDia",
sum(cast("maximoEmDias" as integer)) as "maximoEmDias",
max("dataUltimaVenda") as "ultVenda",
max("dataUltimaCompra") as "ultCompra",
max("ultimoFornecedor") as "ultFornecedor",
sum(cast(vendas_periodo as integer)) as "vendasPeriodo",
max(coalesce("giro", 0)) as "giro"
from
"Inventarios"
left outer join vendas on ("Inventarios"."id" = vendas."inventario_id")
where
coalesce("Inventarios"."status", '') = ''
group by
"grade_id", "estoque_id")
NEW EDIT!!!
I tried to make one query only but it is runing until now (passed 800seconds), the query is:
WITH suprimento_loja as (
WITH suprimento as (
WITH vendas as (
SELECT "inventario_id"
, SUM(coalesce("quantidade", 0)) as vendas_periodo
FROM "WVItens" vwi
WHERE vwi."ultimoDownload" between current_timestamp - (SELECT to_interval(30))
AND current_timestamp
AND coalesce("status", '') = ''
GROUP BY "inventario_id"
)
SELECT
"estoque_id",
"grade_id",
"classe" as classe
, cast("quantidadeAtual" as integer) as qtd
, cast("vendasPorDia" as integer) as vendasDia
, cast("maximoEmDias" as integer) as maximo
, coalesce("dataUltimaVenda", timestamp'01.01.1980') as ultVenda
, "dataUltimaCompra" as ultCompra
, "ultimoFornecedor" as ultForn
, cast(vendas_periodo as integer) as vendas_periodo
, coalesce("giro", 0) as giro
FROM "Inventarios" inv
LEFT OUTER JOIN vendas ve ON inv."id" = ve."inventario_id"
)
select
lo."id" as id,
e1."grade_id" as grade_id,
e1.ultVenda,
case
when e1.ultVenda > coalesce(e2.ultVenda, timestamp'01.01.1980') and e1.ultVenda > coalesce(e3.ultVenda, timestamp'01.01.1980')
then e1.ultVenda
else
case
when coalesce(e2.ultVenda, timestamp'01.01.1980') > coalesce(e3.ultVenda, timestamp'01.01.1980')
then e2.ultVenda
else e3.ultVenda
end
end as ultVenda,
case
when e1.ultCompra > coalesce(e2.ultCompra, timestamp'01.01.1980') and e1.ultCompra > coalesce(e3.ultCompra, timestamp'01.01.1980')
then e1.ultCompra
else
case
when coalesce(e2.ultCompra, timestamp'01.01.1980') > coalesce(e3.ultCompra, timestamp'01.01.1980')
then e2.ultCompra
else e3.ultCompra
end
end as ultCompra,
case
when e1.ultCompra > coalesce(e2.ultCompra, timestamp'01.01.1980') and e1.ultCompra > coalesce(e3.ultCompra, timestamp'01.01.1980')
then e1.ultForn
else
case
when coalesce(e2.ultCompra, timestamp'01.01.1980') > coalesce(e3.ultCompra, timestamp'01.01.1980')
then e2.ultForn
else e3.ultForn
end
end as ultForn,
coalesce(e1.vendas_periodo, 0) + coalesce(e2.vendas_periodo, 0) + coalesce(e3.vendas_periodo, 0) as vendas_periodo,
e1.classe,
(coalesce(e1.maximo, 0) * coalesce(e1.vendasDia, 0)) + (coalesce(e2.maximo, 0) * coalesce(e2.vendasDia, 0)) + (coalesce(e3.maximo, 0) * coalesce(e3.vendasDia, 0)) as maximo,
coalesce(e1.giro, 0) as giro,
coalesce(e1.qtd, 0) + coalesce(e2.qtd, 0) + coalesce(e3.qtd, 0) as qtde,
case
when coalesce(e1.giro, 0) = 0
then 0
else
case
when ((coalesce(e1.qtd, 0) + coalesce(e2.qtd, 0) + coalesce(e3.qtd, 0)) > ((coalesce(e1.maximo, 0) * coalesce(e1.vendasDia, 0)) + (coalesce(e2.maximo, 0) * coalesce(e2.vendasDia, 0)) + (coalesce(e3.maximo, 0) * coalesce(e3.vendasDia, 0))))
then 0
else cast(((((coalesce(e1.maximo, 0) * coalesce(e1.vendasDia, 0)) + (coalesce(e2.maximo, 0) * coalesce(e2.vendasDia, 0)) + (coalesce(e3.maximo, 0) * coalesce(e3.vendasDia, 0)))
- (coalesce(e1.qtd, 0) + coalesce(e2.qtd, 0) + coalesce(e3.qtd, 0))) + 0.4) as integer)
end
end as sugestao
from
"Lojas" lo
LEFT OUTER JOIN suprimento e2 ON e2."estoque_id" = lo."estoque2_id"
LEFT OUTER JOIN suprimento e3 ON e3."estoque_id" = lo."estoque3_id"
JOIN suprimento e1 ON e1."estoque_id" = lo."estoque1_id")
SELECT
gr."id",
sl1.*,
sl2.*,
sl3.*,
sl4.*,
sl5.*,
sl6.*
FROM
"Grades" gr
JOIN suprimento_loja sl1 ON sl1."grade_id" = gr."id"
JOIN suprimento_loja sl2 ON sl2."grade_id" = gr."id"
JOIN suprimento_loja sl3 ON sl3."grade_id" = gr."id"
JOIN suprimento_loja sl4 ON sl4."grade_id" = gr."id"
JOIN suprimento_loja sl5 ON sl5."grade_id" = gr."id"
JOIN suprimento_loja sl6 ON sl6."grade_id" = gr."id"
WHERE
sl1."id" = 1 AND
sl2."id" = 2 AND
sl3."id" = 3 AND
sl4."id" = 4 AND
sl5."id" = 5 AND
sl6."id" = 6
I used aliases and join so you can read easly
I got it working but in other way.
If someone got some similar problem the solution I got is this:
SELECT
gr.id,
max(CASE sl.id WHEN 1 THEN sl.ultVenda ELSE timestamp'01.01.1980' END) AS ultVenda_l1,
max(CASE sl.id WHEN 2 THEN sl.ultVenda ELSE timestamp'01.01.1980' END) AS ultVenda_l2,
max(CASE sl.id WHEN 3 THEN sl.ultVenda ELSE timestamp'01.01.1980' END) AS ultVenda_l3,
max(CASE sl.id WHEN 4 THEN sl.ultVenda ELSE timestamp'01.01.1980' END) AS ultVenda_l4,
max(CASE sl.id WHEN 5 THEN sl.ultVenda ELSE timestamp'01.01.1980' END) AS ultVenda_l5,
max(CASE sl.id WHEN 6 THEN sl.ultVenda ELSE timestamp'01.01.1980' END) AS ultVenda_l6,
max(CASE sl.id WHEN 1 THEN sl.ultCompra ELSE timestamp'01.01.1980' END) AS ultCompra_l1,
max(CASE sl.id WHEN 2 THEN sl.ultCompra ELSE timestamp'01.01.1980' END) AS ultCompra_l2,
max(CASE sl.id WHEN 3 THEN sl.ultCompra ELSE timestamp'01.01.1980' END) AS ultCompra_l3,
max(CASE sl.id WHEN 4 THEN sl.ultCompra ELSE timestamp'01.01.1980' END) AS ultCompra_l4,
max(CASE sl.id WHEN 5 THEN sl.ultCompra ELSE timestamp'01.01.1980' END) AS ultCompra_l5,
max(CASE sl.id WHEN 6 THEN sl.ultCompra ELSE timestamp'01.01.1980' END) AS ultCompra_l6,
max(CASE sl.id WHEN 1 THEN sl.ultForn ELSE '' END) AS ultForn_l1,
max(CASE sl.id WHEN 2 THEN sl.ultForn ELSE '' END) AS ultForn_l2,
max(CASE sl.id WHEN 3 THEN sl.ultForn ELSE '' END) AS ultForn_l3,
max(CASE sl.id WHEN 4 THEN sl.ultForn ELSE '' END) AS ultForn_l4,
max(CASE sl.id WHEN 5 THEN sl.ultForn ELSE '' END) AS ultForn_l5,
max(CASE sl.id WHEN 6 THEN sl.ultForn ELSE '' END) AS ultForn_l6,
max(CASE sl.id WHEN 1 THEN sl.vendas_periodo ELSE 0 END) AS vendas_periodo_l1,
max(CASE sl.id WHEN 2 THEN sl.vendas_periodo ELSE 0 END) AS vendas_periodo_l2,
max(CASE sl.id WHEN 3 THEN sl.vendas_periodo ELSE 0 END) AS vendas_periodo_l3,
max(CASE sl.id WHEN 4 THEN sl.vendas_periodo ELSE 0 END) AS vendas_periodo_l4,
max(CASE sl.id WHEN 5 THEN sl.vendas_periodo ELSE 0 END) AS vendas_periodo_l5,
max(CASE sl.id WHEN 6 THEN sl.vendas_periodo ELSE 0 END) AS vendas_periodo_l6,
max(CASE sl.id WHEN 1 THEN sl.maximo ELSE 0 END) AS maximo_l1,
max(CASE sl.id WHEN 2 THEN sl.maximo ELSE 0 END) AS maximo_l2,
max(CASE sl.id WHEN 3 THEN sl.maximo ELSE 0 END) AS maximo_l3,
max(CASE sl.id WHEN 4 THEN sl.maximo ELSE 0 END) AS maximo_l4,
max(CASE sl.id WHEN 5 THEN sl.maximo ELSE 0 END) AS maximo_l5,
max(CASE sl.id WHEN 6 THEN sl.maximo ELSE 0 END) AS maximo_l6,
max(CASE sl.id WHEN 1 THEN sl.giro ELSE 0 END) AS giro_l1,
max(CASE sl.id WHEN 2 THEN sl.giro ELSE 0 END) AS giro_l2,
max(CASE sl.id WHEN 3 THEN sl.giro ELSE 0 END) AS giro_l3,
max(CASE sl.id WHEN 4 THEN sl.giro ELSE 0 END) AS giro_l4,
max(CASE sl.id WHEN 5 THEN sl.giro ELSE 0 END) AS giro_l5,
max(CASE sl.id WHEN 6 THEN sl.giro ELSE 0 END) AS giro_l6,
max(CASE sl.id WHEN 1 THEN sl.qtde ELSE 0 END) AS qtd_l1,
max(CASE sl.id WHEN 2 THEN sl.qtde ELSE 0 END) AS qtd_l2,
max(CASE sl.id WHEN 3 THEN sl.qtde ELSE 0 END) AS qtd_l3,
max(CASE sl.id WHEN 4 THEN sl.qtde ELSE 0 END) AS qtd_l4,
max(CASE sl.id WHEN 5 THEN sl.qtde ELSE 0 END) AS qtd_l5,
max(CASE sl.id WHEN 6 THEN sl.qtde ELSE 0 END) AS qtd_l6,
max(CASE sl.id WHEN 1 THEN sl.sugestao ELSE 0 END) AS sugestao_l1,
max(CASE sl.id WHEN 2 THEN sl.sugestao ELSE 0 END) AS sugestao_l2,
max(CASE sl.id WHEN 3 THEN sl.sugestao ELSE 0 END) AS sugestao_l3,
max(CASE sl.id WHEN 4 THEN sl.sugestao ELSE 0 END) AS sugestao_l4,
max(CASE sl.id WHEN 5 THEN sl.sugestao ELSE 0 END) AS sugestao_l5,
max(CASE sl.id WHEN 6 THEN sl.sugestao ELSE 0 END) AS sugestao_l6
FROM "Grades" gr
INNER JOIN suprimento_loja sl ON sl.grade_id = gr.id
group by gr.id
order by gr.id
And the view 'suprimento_loja' is that query used before in the function.
The only thing you can do now to speed up the query - remake the function to take less time.
It PostgreSQL 9.3 you will be able to use your second query variant with LATERAL, but it wont be significantly faster.
This is only the edited body (untested, hope I didn't make any typos ;-):
WITH vendas as (
SELECT "inventario_id"
, SUM(coalesce("quantidade", 0)) as vendas_periodo
FROM "WVItens" vwi
WHERE vwi."ultimoDownload" between current_timestamp - (SELECT to_interval(vendas_dias)) AND current_timestamp
AND coalesce("status", '') = ''
GROUP BY "inventario_id"
)
SELECT into qtde1, vendasdiae1, maximoe1, ultvendae1, ultcomprae1, ultforne1, classee1, vendase1, giroe1
cast("quantidadeAtual" as integer)
, cast("vendasPorDia" as integer)
, cast("maximoEmDias" as integer)
, "dataUltimaVenda"
, "dataUltimaCompra"
, "ultimoFornecedor"
, "classe"
, cast(vendas_periodo as integer)
, coalesce("giro", 0)
FROM "Inventarios" inv
JOIN "Lojas" lo ON inv."estoque_id" = lo."estoque1_id"
JOIN vendas ve ON inv."id" = ve."inventario_id"
WHERE coalesce( inv."status", '') = ''
AND lo."id" = lojaId
AND "grade_id" = gradeId
;
WITH vendas as (
SELECT "inventario_id"
, SUM(coalesce("quantidade", 0)) as vendas_periodo
FROM "WVItens" vwi
WHERE vwi."ultimoDownload" between current_timestamp - (SELECT to_interval(vendas_dias))
AND current_timestamp
AND coalesce("status", '') = ''
GROUP BY "inventario_id"
)
SELECT into qtde2, vendasdiae2, maximoe2, ultvendae2, ultcomprae2, ultforne2, vendase2, giroe2
cast("quantidadeAtual" as integer)
, cast("vendasPorDia" as integer)
, cast("maximoEmDias" as integer)
, "dataUltimaVenda"
, "dataUltimaCompra"
, "ultimoFornecedor"
, cast(vendas_periodo as integer)
, coalesce("giro", 0)
FROM "Inventarios" inv
JOIN "Lojas" lo ON inv."estoque_id" = lo."estoque2_id"
JOIN vendas ve ON inv."id" = vendas."inventario_id"
WHERE coalesce( inv."status", '') = ''
AND lo."id" = lojaId
AND "grade_id" = gradeId
;
WITH vendas as (
SELECT "inventario_id"
, SUM(coalesce("quantidade", 0)) as vendas_periodo
FROM "WVItens" vwi
WHERE vwi."ultimoDownload" between current_timestamp - (SELECT to_interval(vendas_dias))
AND current_timestamp
AND coalesce("status", '') = ''
GROUP BY "inventario_id"
)
SELECT into qtde3, vendasdiae3, maximoe3, ultvendae3, ultcomprae3, ultforne3, vendase3, giroe3
cast("quantidadeAtual" as integer)
, cast("vendasPorDia" as integer)
, cast("maximoEmDias" as integer)
, "dataUltimaVenda"
, "dataUltimaCompra"
, "ultimoFornecedor"
, cast(vendas_periodo as integer)
, coalesce("giro", 0)
FROM "Inventarios" inv
JOIN "Lojas" lo ON inv."estoque_id" = lo."estoque2_id"
JOIN vendas ve ON inv."id" = vendas."inventario_id"
WHERE coalesce( inv."status", '') = ''
AND lo."id" = lojaId
AND "grade_id" = gradeId
;
At first glance, It appears you are doing exactly the same query three times, with different constants, which to me looks subobtimate.

Entity Framework: Many-to-Many .Include() not working

I have the following table structure:
That is, a Supplier has a many-to-many relationship with People (of Person).
Supplier 1--* SupplierPerson *--1 Person
A Person has a one-to-many relationship with EmaiLAddresses, TelephoneNumbers and WebResources.
Person 1--* EmailAddress
Person 1--* TelephoneNumber
Person 1--* WebResource
I've highlighted the navigation properties.
Given the LINQ-to-Entities using .Include() to load the dependencies and shape the result-set:
ObjectQuery<Supplier> supplierQuery=((SopEntities)Context).Suppliers
.Include("People")
.Include("People.TelephoneNumbers")
.Include("People.EmailAddresses")
.Include("People.WebResources");
#if TRACE
Trace.WriteLine(string.Format("GetAll(): {0}", supplierQuery.ToTraceString()));
#endif
return supplierQuery;
I get the insanely large and rather useless SQL:
SELECT
[Project5].[ID] AS [ID],
[Project5].[Key] AS [Key],
[Project5].[CompanyName] AS [CompanyName],
[Project5].[AddressLine1] AS [AddressLine1],
[Project5].[AddressLine2] AS [AddressLine2],
[Project5].[TownCity] AS [TownCity],
[Project5].[CountyState] AS [CountyState],
[Project5].[Postcode] AS [Postcode],
[Project5].[Country] AS [Country],
[Project5].[C25] AS [C1],
[Project5].[C2] AS [C2],
[Project5].[C3] AS [C3],
[Project5].[C4] AS [C4],
[Project5].[C5] AS [C5],
[Project5].[C6] AS [C6],
[Project5].[C7] AS [C7],
[Project5].[C8] AS [C8],
[Project5].[C9] AS [C9],
[Project5].[C10] AS [C10],
[Project5].[C11] AS [C11],
[Project5].[C1] AS [C12],
[Project5].[C12] AS [C13],
[Project5].[C13] AS [C14],
[Project5].[C14] AS [C15],
[Project5].[C15] AS [C16],
[Project5].[C16] AS [C17],
[Project5].[C17] AS [C18],
[Project5].[C18] AS [C19],
[Project5].[C19] AS [C20],
[Project5].[C20] AS [C21],
[Project5].[C21] AS [C22],
[Project5].[C22] AS [C23],
[Project5].[C23] AS [C24],
[Project5].[C24] AS [C25]
FROM ( SELECT
[Extent1].[ID] AS [ID],
[Extent1].[Key] AS [Key],
[Extent1].[CompanyName] AS [CompanyName],
[Extent1].[AddressLine1] AS [AddressLine1],
[Extent1].[AddressLine2] AS [AddressLine2],
[Extent1].[TownCity] AS [TownCity],
[Extent1].[CountyState] AS [CountyState],
[Extent1].[Postcode] AS [Postcode],
[Extent1].[Country] AS [Country],
[UnionAll2].[C1] AS [C1],
[UnionAll2].[C2] AS [C2],
[UnionAll2].[C3] AS [C3],
[UnionAll2].[C4] AS [C4],
[UnionAll2].[C5] AS [C5],
[UnionAll2].[C6] AS [C6],
[UnionAll2].[C7] AS [C7],
[UnionAll2].[C8] AS [C8],
[UnionAll2].[C9] AS [C9],
[UnionAll2].[C10] AS [C10],
[UnionAll2].[C11] AS [C11],
[UnionAll2].[C12] AS [C12],
[UnionAll2].[C13] AS [C13],
[UnionAll2].[C14] AS [C14],
[UnionAll2].[C15] AS [C15],
[UnionAll2].[C16] AS [C16],
[UnionAll2].[C17] AS [C17],
[UnionAll2].[C18] AS [C18],
[UnionAll2].[C19] AS [C19],
[UnionAll2].[C20] AS [C20],
[UnionAll2].[C21] AS [C21],
[UnionAll2].[C22] AS [C22],
[UnionAll2].[C23] AS [C23],
[UnionAll2].[C24] AS [C24],
CASE WHEN ([UnionAll2].[C2] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C25]
FROM [dbo].[Supplier] AS [Extent1]
OUTER APPLY (SELECT
[UnionAll1].[C1] AS [C1],
[UnionAll1].[PersonID] AS [C2],
[UnionAll1].[SupplierID] AS [C3],
[UnionAll1].[SupplierID1] AS [C4],
[UnionAll1].[ID] AS [C5],
[UnionAll1].[Title] AS [C6],
[UnionAll1].[FirstName] AS [C7],
[UnionAll1].[Initials] AS [C8],
[UnionAll1].[LastName] AS [C9],
[UnionAll1].[Position] AS [C10],
[UnionAll1].[DepartmentID] AS [C11],
[UnionAll1].[ID1] AS [C12],
[UnionAll1].[Number] AS [C13],
[UnionAll1].[Name] AS [C14],
[UnionAll1].[PersonID1] AS [C15],
[UnionAll1].[TelephoneNumberTypeID] AS [C16],
[UnionAll1].[C2] AS [C17],
[UnionAll1].[C3] AS [C18],
[UnionAll1].[C4] AS [C19],
[UnionAll1].[C5] AS [C20],
[UnionAll1].[C6] AS [C21],
[UnionAll1].[C7] AS [C22],
[UnionAll1].[C8] AS [C23],
[UnionAll1].[C9] AS [C24]
FROM (SELECT
CASE WHEN ([Extent4].[ID] IS NULL) THEN CAST(NULL AS int) ELSE 1 END AS [C1],
[Extent2].[PersonID] AS [PersonID],
[Extent2].[SupplierID] AS [SupplierID],
[Extent2].[SupplierID] AS [SupplierID1],
[Extent3].[ID] AS [ID],
[Extent3].[Title] AS [Title],
[Extent3].[FirstName] AS [FirstName],
[Extent3].[Initials] AS [Initials],
[Extent3].[LastName] AS [LastName],
[Extent3].[Position] AS [Position],
[Extent3].[DepartmentID] AS [DepartmentID],
[Extent4].[ID] AS [ID1],
[Extent4].[Number] AS [Number],
[Extent4].[Name] AS [Name],
[Extent4].[PersonID] AS [PersonID1],
[Extent4].[TelephoneNumberTypeID] AS [TelephoneNumberTypeID],
CAST(NULL AS int) AS [C2],
CAST(NULL AS varchar(1)) AS [C3],
CAST(NULL AS varchar(1)) AS [C4],
CAST(NULL AS int) AS [C5],
CAST(NULL AS int) AS [C6],
CAST(NULL AS varchar(1)) AS [C7],
CAST(NULL AS varchar(1)) AS [C8],
CAST(NULL AS int) AS [C9]
FROM [dbo].[SupplierPerson] AS [Extent2]
INNER JOIN [dbo].[Person] AS [Extent3] ON [Extent3].[ID] = [Extent2].[PersonID]
LEFT OUTER JOIN [dbo].[TelephoneNumber] AS [Extent4] ON [Extent3].[ID] = [Extent4].[PersonID]
WHERE [Extent1].[ID] = [Extent2].[SupplierID]
UNION ALL
SELECT
2 AS [C1],
[Extent5].[PersonID] AS [PersonID],
[Extent5].[SupplierID] AS [SupplierID],
[Extent5].[SupplierID] AS [SupplierID1],
[Extent6].[ID] AS [ID],
[Extent6].[Title] AS [Title],
[Extent6].[FirstName] AS [FirstName],
[Extent6].[Initials] AS [Initials],
[Extent6].[LastName] AS [LastName],
[Extent6].[Position] AS [Position],
[Extent6].[DepartmentID] AS [DepartmentID],
CAST(NULL AS int) AS [C2],
CAST(NULL AS varchar(1)) AS [C3],
CAST(NULL AS varchar(1)) AS [C4],
CAST(NULL AS int) AS [C5],
CAST(NULL AS int) AS [C6],
[Extent7].[ID] AS [ID1],
[Extent7].[Email] AS [Email],
[Extent7].[Name] AS [Name],
[Extent7].[PersonID] AS [PersonID1],
CAST(NULL AS int) AS [C7],
CAST(NULL AS varchar(1)) AS [C8],
CAST(NULL AS varchar(1)) AS [C9],
CAST(NULL AS int) AS [C10]
FROM [dbo].[SupplierPerson] AS [Extent5]
INNER JOIN [dbo].[Person] AS [Extent6] ON [Extent6].[ID] = [Extent5].[PersonID]
INNER JOIN [dbo].[EmailAddress] AS [Extent7] ON [Extent6].[ID] = [Extent7].[PersonID]
WHERE [Extent1].[ID] = [Extent5].[SupplierID]) AS [UnionAll1]
UNION ALL
SELECT
3 AS [C1],
[Extent8].[PersonID] AS [PersonID],
[Extent8].[SupplierID] AS [SupplierID],
[Extent8].[SupplierID] AS [SupplierID1],
[Extent9].[ID] AS [ID],
[Extent9].[Title] AS [Title],
[Extent9].[FirstName] AS [FirstName],
[Extent9].[Initials] AS [Initials],
[Extent9].[LastName] AS [LastName],
[Extent9].[Position] AS [Position],
[Extent9].[DepartmentID] AS [DepartmentID],
CAST(NULL AS int) AS [C2],
CAST(NULL AS varchar(1)) AS [C3],
CAST(NULL AS varchar(1)) AS [C4],
CAST(NULL AS int) AS [C5],
CAST(NULL AS int) AS [C6],
CAST(NULL AS int) AS [C7],
CAST(NULL AS varchar(1)) AS [C8],
CAST(NULL AS varchar(1)) AS [C9],
CAST(NULL AS int) AS [C10],
[Extent10].[ID] AS [ID1],
[Extent10].[Url] AS [Url],
[Extent10].[Name] AS [Name],
[Extent10].[PersonID] AS [PersonID1]
FROM [dbo].[SupplierPerson] AS [Extent8]
INNER JOIN [dbo].[Person] AS [Extent9] ON [Extent9].[ID] = [Extent8].[PersonID]
INNER JOIN [dbo].[WebResource] AS [Extent10] ON [Extent9].[ID] = [Extent10].[PersonID]
WHERE [Extent1].[ID] = [Extent8].[SupplierID]) AS [UnionAll2]
) AS [Project5]
ORDER BY [Project5].[ID] ASC, [Project5].[C25] ASC, [Project5].[C2] ASC, [Project5].[C3] ASC, [Project5].[C5] ASC, [Project5].[C1] ASC
Which populates the Supplier with People, but not the TelephoneNumbers, EmailAddresses or WebResources.
When executed in SQL Management Studio, I get the incomplete record set:
Clearly, the .Include() is not working due to my misunderstanding of its capabilities.
I really would like to avoid having to call the People schema seperately. How can I have this generate useful SQL?

T-SQL Paging Sorting & Filtering - Filtering not Working

T-SQL Paging Sorting & Filtering
I have been working on a T-SQL stored procedure for a number of hours now that will enable me to retrieve a paged set of articles that are sorted in ASC or DESC order based on a specified column.
I am now working on getting the stored procedure to filter based on the first character of the 'Title' field and have added the lines:
#StartAlpha nvarchar(1) = null
and
WHERE ((#StartAlpha IS NULL) OR (Title Like #StartAlpha + '%'))
see below.
The stored procedure no longer returns any results. And I don't really know why.
Can anyone please help?
Regards
Walter
USE [ABC]
GO
/****** Object: StoredProcedure [dbo].[Get_MyArticles_Paged] Script Date: 08/07/2011 20:41:28 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE [dbo].[Get_MyArticles_Paged]
/*Paging Total For Output*/
#Row_Count BIGINT OUT,
/*Paging Inputs*/
#Page_Size INT = 10,
#Page_Number INT = 1,
#Sort_Column VARCHAR(100), /* ('articleid','createdate','title','subject') */
#Sort_Direction VARCHAR(4), /* ('ASC','DESC') */
#StartAlpha nvarchar(1) = null
AS
BEGIN
print #StartAlpha
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
/*========================================================================
Declare local variables
========================================================================*/
DECLARE #FirstRecord int
DECLARE #LastRecord int
-- create a temporary space for paged result set
DECLARE #PagedResults AS TABLE (
[ArticleID] INT,
[CreateDate] SMALLDATETIME,
[Title] VARCHAR(200),
[Subject] VARCHAR(500),
[Row_Number] BIGINT,
[Row_Count] BIGINT
);
/*========================
Normalize Paging Parameters
==========================*/
--Fix invalid input for Page Size
SET #Page_Size = CASE
WHEN #Page_Size IS NULL THEN 10
WHEN #Page_Size < 1 THEN 10
ELSE #Page_Size
END;
--Fix invalid input for Page Number
SET #Page_Number = CASE
WHEN #Page_Number IS NULL THEN 1
WHEN #Page_Number < 1 THEN 1
ELSE #Page_Number
END;
--starting record to use.
SET #FirstRecord = ((#Page_Number - 1) * #Page_Size) + 1
--last record to use.
SET #LastRecord = #FirstRecord + #Page_Size - 1
--ensure sort column is valid in the list
SET #Sort_Column = CASE
WHEN LOWER(#Sort_Column) IN ('articleid','createdate','title','subject')
THEN LOWER(#Sort_Column)
ELSE
'title' --default
END
--ensure sort direction is ASC or DESC
SET #Sort_Direction = CASE
WHEN LEFT(UPPER(COALESCE(#Sort_Direction, '')) + ' ', 4) = 'DESC'
THEN 'DESC' --explicit descending
WHEN #Sort_Column = 'created' AND LEFT(UPPER(COALESCE(#Sort_Direction,'')) + ' ', 3) <> 'ASC' THEN
'DESC' --default for created date
ELSE 'ASC' --default otherwise
END;
/*============
Prepare Results
==============*/
WITH [MyTempArea] AS (
SELECT TOP (#LastRecord)
[ArticleID],
[CreateDate],
[Title],
[Subject],
ROW_NUMBER() OVER (
ORDER BY
CASE WHEN(#Sort_Direction = 'ASC') THEN CASE WHEN #Sort_Column='articleid' THEN [articleid] END END ASC,
CASE WHEN(#Sort_Direction = 'ASC') THEN CASE WHEN #Sort_Column='createdate' THEN [createdate] END END ASC,
CASE WHEN(#Sort_Direction = 'ASC') THEN CASE WHEN #Sort_Column='title' THEN [title] END END ASC,
CASE WHEN(#Sort_Direction = 'ASC') THEN CASE WHEN #Sort_Column='subject' THEN [subject] END END ASC,
CASE WHEN(#Sort_Direction = 'DESC') THEN CASE WHEN #Sort_Column='articleid' THEN [articleid] END END DESC,
CASE WHEN(#Sort_Direction = 'DESC') THEN CASE WHEN #Sort_Column='createdate' THEN [createdate] END END DESC,
CASE WHEN(#Sort_Direction = 'DESC') THEN CASE WHEN #Sort_Column='title' THEN [title] END END DESC,
CASE WHEN(#Sort_Direction = 'DESC') THEN CASE WHEN #Sort_Column='subject' THEN [subject] END END DESC
) AS [Row_Number],
COUNT(*) OVER () AS [Row_Count]
FROM Articles
WHERE ((#StartAlpha IS NULL) OR (Title Like #StartAlpha + '%'))
)
INSERT INTO #PagedResults
SELECT * FROM [MyTempArea] WHERE [Row_Number] >= #FirstRecord;
/*===========
Return Results
=============*/
-- #Row_Count output param
SELECT #Row_Count = COALESCE(MAX(Row_Count), 0) FROM #PagedResults;
-- Paged results set to return
SELECT [ArticleID],[CreateDate],[Title],[Subject]
FROM #PagedResults
ORDER BY [Row_Number];
END
Thanks to everyone that made helpful suggestions.
I completely refactored the stored procedure and it now works. See below.
I'm not entirely sure why the original Stored Procedure didn't work and why this version does, but I thought I would share with the forum.
Thanks again.
Walter.
ALTER PROCEDURE [dbo].[Account_ContactGetData]
#CurrentPage int = null,
#PageSize int = null,
#SortColumn nvarchar(max) = null,
#SortDirection varchar(5),
#StartAlpha nvarchar(1) = null
WITH EXECUTE AS CALLER
AS
BEGIN
SET NOCOUNT ON;
DECLARE #FirstRecord int;
DECLARE #LastRecord int;
--starting record to use.
SET #FirstRecord = ((#CurrentPage - 1) * #PageSize) + 1;
--last record to use.
SET #LastRecord = #FirstRecord + #PageSize - 1;
with ContactCTE as
(
SELECT [ContactID], [DisplayName], [FirstName], [MiddleName], [LastName],
(ROW_NUMBER() OVER (Order By
CASE WHEN #SortColumn='ContactID' AND #SortDirection='DESC' THEN ContactID END DESC,
CASE WHEN #SortColumn='ContactID' AND #SortDirection='ASC' THEN ContactID END ASC,
CASE WHEN #SortColumn='DisplayName' AND #SortDirection='DESC' THEN DisplayName END DESC,
CASE WHEN #SortColumn='DisplayName' AND #SortDirection='ASC' THEN DisplayName END ASC,
CASE WHEN #SortColumn='FirstName' AND #SortDirection='DESC' THEN FirstName END DESC,
CASE WHEN #SortColumn='FirstName' AND #SortDirection='ASC' THEN FirstName END ASC,
CASE WHEN #SortColumn='MiddleName' AND #SortDirection='DESC' THEN MiddleName END DESC,
CASE WHEN #SortColumn='MiddleName' AND #SortDirection='ASC' THEN MiddleName END ASC,
CASE WHEN #SortColumn='LastName' AND #SortDirection='DESC' THEN LastName END DESC,
CASE WHEN #SortColumn='LastName' AND #SortDirection='ASC' THEN LastName END ASC
)) AS Row
FROM Contact
WHERE
((#StartAlpha is NULL) OR (LastName Like #StartAlpha+ '%'))
)
SELECT [ContactID], [DisplayName], [FirstName], [MiddleName], [LastName]
FROM ContactCTE
WHERE Row BETWEEN #FirstRecord AND #LastRecord
END
Based on those requirements alone, this is what I would do:
WHERE ((#StartAlpha IS NULL) OR (LEFT(Title, 1) = #StartAlpha))
What do you get when you run just this part? (Please also state which values you are using in your parameters)
WITH [MyTempArea] AS (
SELECT TOP (#LastRecord)
[ArticleID],
[CreateDate],
[Title],
[Subject],
ROW_NUMBER() OVER (
ORDER BY
CASE WHEN(#Sort_Direction = 'ASC') THEN CASE WHEN #Sort_Column='articleid' THEN [articleid] END END ASC,
CASE WHEN(#Sort_Direction = 'ASC') THEN CASE WHEN #Sort_Column='createdate' THEN [createdate] END END ASC,
CASE WHEN(#Sort_Direction = 'ASC') THEN CASE WHEN #Sort_Column='title' THEN [title] END END ASC,
CASE WHEN(#Sort_Direction = 'ASC') THEN CASE WHEN #Sort_Column='subject' THEN [subject] END END ASC,
CASE WHEN(#Sort_Direction = 'DESC') THEN CASE WHEN #Sort_Column='articleid' THEN [articleid] END END DESC,
CASE WHEN(#Sort_Direction = 'DESC') THEN CASE WHEN #Sort_Column='createdate' THEN [createdate] END END DESC,
CASE WHEN(#Sort_Direction = 'DESC') THEN CASE WHEN #Sort_Column='title' THEN [title] END END DESC,
CASE WHEN(#Sort_Direction = 'DESC') THEN CASE WHEN #Sort_Column='subject' THEN [subject] END END DESC
) AS [Row_Number],
COUNT(*) OVER () AS [Row_Count]
FROM Articles
WHERE ((#StartAlpha IS NULL) OR (Title Like #StartAlpha + '%'))
)
SELECT * FROM [MyTempArea]
#Andreas LEFT(Title, 1) syntax would perform better than the like comparison... but using the like, this should work:
WHERE (Title Like isnull(#StartAlpha, '') + '%')
(The combination of working with nulls that OR makes me edgy.)