Linq:如何按一个表的 SUM 和另一个表的 COUNT 进行排序

发布于 2024-10-12 15:23:19 字数 2592 浏览 1 评论 0原文

根据上一个问题,我得到了以下 LINQ 表达式。

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)

它将转换为以下 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]

我现在的问题是当某些事件没有任何投票或评论时进行排序。

以下是 EventVotes 表的样子(完整)

| UserID | EventID | Vote |    
| 1      | 51      | 1    |   
| 1      | 52      | 1    |   
| 2      | 52      | 1    |   
| 1      | 53      | 1    |   
| 2      | 53      | -1   |   
| 3      | 53      | -1   |

Comments 表完全是空的,因此由于我们只是对其进行 Count 操作,因此我们可以假设所有内容都返回 Null。

现在,当我运行上面的查询时,结果顺序如下

52
51
53
1
2
3

什么时候“应该”

52
51
1
2
3
53

因为事件编号 53 的投票计数为“-1”,而事件编号 1、2 和 3 的投票计数为“0”

任何人都可以帮助弄清楚如何增强 Linq 表达式以解释尚未发生的事件被投票通过?

这是一个屏幕截图

alt text

编辑:

好的,所以我将 LinqPad 中的查询简化为这样,结果是我只得到三个结果。

Events.OrderByDescending(Function(e) (((e.EventVotes.Sum(Function(s) s.Vote)) * 2) + (e.Comments.Count)))

这告诉我的是,orderby 仅获取这三个结果(51、52、53),然后将其余结果附加在 order 子句之后。我需要找到一种方法将其余的“null”结果包含在 Lambda 表达式中。

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

alt text

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

冰魂雪魄 2024-10-19 15:23:19

这是一个 C# 解决方案。我试图将其翻译成 VB,但我的 VB 技能(老实说,也没有学习它的愿望)为零,所以我放弃了。我将尽力强调关键点以帮助翻译。

完整的语句如下:

context.Events.OrderByDescending(e => (((e.EventVotes.Sum(s => (int?)s.Vote) ?? 0) * 2) + e.Comments.Count))

关键是 s.Vote 首先被转换为一个可为空的整数,以便 Sum() 的结果将是一个可为空的整数。可空结果允许您显式检查空值,并在结果确实为空时使用零进行其余计算。

出于 VB 转换目的,?? 是一个 C# 空合并运算符,其中(例如)myNullableIntVar ?? 0 计算为 myNullableIntVar 的值,或者如果它为 null,则为 0。

这是由上述语句生成的 SQL,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

由 rockinthesixstring 编辑

我在下面添加了 VB.NET 版本 - 其工作原理与预期的。我尝试执行 DirectCast(s.Vote, Integer?) 但它抛出一个错误,指出 Integer 无法转换为 Integer?,所以我不得不采取这种方法。

Events.OrderByDescending(Function(e) (((If(e.EventVotes.Sum(Function(s) s.Vote),
                                           e.EventVotes.Sum(Function(s) s.Vote),
                                           0)) * 2) + e.Comments.Count))

这导致了这个 SQL 查询

-- 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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文