问题
As per a previous question, I've got the following LINQ expression.
Events.Where(Function(e) e.EventDate >= Date.Today) _
.OrderByDescending(Function(e) (((e.EventVotes.Sum(Function(s) s.Vote)) * 2) + (e.Comments.Count))) _
.Skip(0) _
.Take(5)
Which converts to the following SQL
-- Region Parameters
DECLARE @p0 DateTime2 = '2011-01-17 00:00:00.0000000'
DECLARE @p1 Int = 2
DECLARE @p2 Int = 0
DECLARE @p3 Int = 5
-- EndRegion
SELECT [t3].[ID], [t3].[UserID], [t3].[RegionID], [t3].[LocationID], [t3].[Title], [t3].[Description], [t3].[EventDate], [t3].[URL], [t3].[Phone], [t3].[TicketPriceLow], [t3].[TicketPriceHigh], [t3].[DatePosted], [t3].[isHighlighted]
FROM (
SELECT ROW_NUMBER() OVER (ORDER BY (((
SELECT SUM([t1].[Vote])
FROM [dbo].[EventVotes] AS [t1]
WHERE [t1].[EventID] = [t0].[ID]
)) * @p1) + ((
SELECT COUNT(*)
FROM [dbo].[Comments] AS [t2]
WHERE [t2].[EventID] = [t0].[ID]
)) DESC) AS [ROW_NUMBER], [t0].[ID], [t0].[UserID], [t0].[RegionID], [t0].[LocationID], [t0].[Title], [t0].[Description], [t0].[EventDate], [t0].[URL], [t0].[Phone], [t0].[TicketPriceLow], [t0].[TicketPriceHigh], [t0].[DatePosted], [t0].[isHighlighted]
FROM [dbo].[Events] AS [t0]
WHERE [t0].[EventDate] >= @p0
) AS [t3]
WHERE [t3].[ROW_NUMBER] BETWEEN @p2 + 1 AND @p2 + @p3
ORDER BY [t3].[ROW_NUMBER]
My problem now is when it comes to ordering when some events don't have any votes or comments.
Here is what the EventVotes table looks like (in it's entirety)
| UserID | EventID | Vote |
| 1 | 51 | 1 |
| 1 | 52 | 1 |
| 2 | 52 | 1 |
| 1 | 53 | 1 |
| 2 | 53 | -1 |
| 3 | 53 | -1 |
The Comments table is completely empty, so since we're just doing a Count
on it, we can assume that everything comes back Null.
Now when I run the query above, the result order is as follows
52
51
53
1
2
3
When it "should" be
52
51
1
2
3
53
because event number 53 has a vote count of "-1" while event numbers 1, 2, and 3 have a vote count of "0"
Can anyone help figure out how to enhance the Linq expression to account for events that haven't been voted on?
Here's a screenshot

EDIT:
Ok, so I've simplified the query in LinqPad to this, and the result is that I only get three results.
Events.OrderByDescending(Function(e) (((e.EventVotes.Sum(Function(s) s.Vote)) * 2) + (e.Comments.Count)))
What this is telling me is that the orderby is only grabbing those three results (51, 52, 53), then it appends the rest of the results AFTER the order clause. I need to figure out a way to include the rest of the "null" results in the Lambda expression.
回答1:
Here's a C# solution. I tried to translate it into VB, but my VB skills (and desire to learn it, honestly) are nil, and I gave up. I'll try to highlight the critical points to help with the translation.
Here's the full statement:
context.Events.OrderByDescending(e => (((e.EventVotes.Sum(s => (int?)s.Vote) ?? 0) * 2) + e.Comments.Count))
What's critical is that s.Vote
is first cast to a nullable integer so that the result of Sum()
will be a nullable integer. A nullable result allows you to explicitly check for null and use zero for the rest of calculation if the result was indeed null.
For VB translation purposes, ??
is a C# null-coalesce operator where (for example) myNullableIntVar ?? 0
evaluates to the value of myNullableIntVar, or if it is null, then 0.
Here's the SQL generated by the above statement, FWIW:
exec sp_executesql N'SELECT [t0].[EventID], [t0].[Name]
FROM [dbo].[Events] AS [t0]
ORDER BY ((COALESCE((
SELECT SUM([t2].[value])
FROM (
SELECT [t1].[Vote] AS [value], [t1].[EventID]
FROM [dbo].[EventVotes] AS [t1]
) AS [t2]
WHERE [t2].[EventID] = [t0].[EventID]
),@p0)) * @p1) + ((
SELECT COUNT(*)
FROM [dbo].[Comments] AS [t3]
WHERE [t3].[EventID] = [t0].[EventID]
)) DESC',N'@p0 int,@p1 int',@p0=0,@p1=2
Edit by rockinthesixstring
I've added the VB.NET version below - This works exactly as expected. I tried to do a DirectCast(s.Vote, Integer?)
but it threw an error saying that Integer
cannot be converted to Integer?
, So I had to take this approach.
Events.OrderByDescending(Function(e) (((If(e.EventVotes.Sum(Function(s) s.Vote),
e.EventVotes.Sum(Function(s) s.Vote),
0)) * 2) + e.Comments.Count))
Which resulted in this SQL Query
-- Region Parameters
DECLARE @p0 Int = 0
DECLARE @p1 Int = 2
-- EndRegion
SELECT [t0].[ID], [t0].[UserID], [t0].[RegionID], [t0].[LocationID], [t0].[Title], [t0].[Description], [t0].[EventDate], [t0].[URL], [t0].[Phone], [t0].[TicketPriceLow], [t0].[TicketPriceHigh], [t0].[DatePosted], [t0].[isHighlighted]
FROM [dbo].[Events] AS [t0]
ORDER BY ((
(CASE
WHEN (CONVERT(Bit,(
SELECT SUM([t1].[Vote])
FROM [dbo].[EventVotes] AS [t1]
WHERE [t1].[EventID] = [t0].[ID]
))) = 1 THEN (
SELECT SUM([t2].[Vote])
FROM [dbo].[EventVotes] AS [t2]
WHERE [t2].[EventID] = [t0].[ID]
)
ELSE @p0
END)) * @p1) + ((
SELECT COUNT(*)
FROM [dbo].[Comments] AS [t3]
WHERE [t3].[EventID] = [t0].[ID]
)) DESC
来源:https://stackoverflow.com/questions/4719163/linq-how-to-orderby-the-sum-of-one-table-and-the-count-of-another