选择 LINQ 到实体查询中记录的聚合(分组)统计信息

发布于 2024-09-30 20:35:32 字数 1455 浏览 8 评论 0原文

我有一个查询正在尝试从 SQL (T-SQL) 移植到 LINQ-to-Entities 4.0 (C#)。结果集包含标准“详细信息行”以及聚合“统计”信息的组合。

原始 SQL 使用标准 select 左连接到聚合信息,类似于:

SELECT 
    UserId, 
    Name, 
    Email, 
    ISNULL(Stats.TotalPosts, 0) as TotalPosts,
    Stats.LastPost
FROM Users
LEFT OUTER JOIN
(
    SELECT UserId, COUNT(*) as TotalPosts, MAX(DatePosted) as LastPost
    FROM Articles
    GROUP BY UserId
) as Stats ON Stats.UserId = Users.UserID

出于性能原因,在 SELECT 语句中使用左连接而不是子查询 - 返回多个聚合统计信息(帖子总数和发布日期)最后一篇文章)

我已经部分成功地将其转换为 C# 4.0 中的 LINQ-to-Entities 查询,但我不完全确定联接应如何与组语句结合起来。我想我正在从 SQL 角度考虑这个问题,而不是正确使用 LINQ。

我成功地将统计信息分解为单独的查询:

var stats =
(
    from a in entities.Articles
    group a by a.UserId into g
    select new
    {
        UserId = g.Key,
        TotalPosts = g.Count(),
        LastUpdated = g.Max(i => i.DatePosted)
    }
);

var query =
(
    from u in entities.Users
    join s in stats on u.UserId equals s.UserId
    orderby u.Name 
    select new UserListing()
    {
        UserId = u.UserId,
        Name = u.Name,
        Email = u.Email,
        TotalPosts = s.TotalPosts,
        LastUpdated = s.LastUpdated
    }
);

不幸的是,LINQ 查询中使用的联接过滤掉了所有未提交任何文章的用户。

通过包含 DefaultIfEmpty 切换到等效的外连接会导致其他问题 - 我只能为 TotalPosts 返回“null”而不是 0。即使在“TotalPosts = (s.TotalPosts == null) ? 0 : s.TotalPosts”中select,除非 TotalPosts 属性可为空,否则将引发异常。

以这种方式组合详细信息行和聚合信息的最佳实践是什么?

谢谢!

I have a query I'm attempting to port from SQL (T-SQL) to LINQ-to-Entities 4.0 (C#). The result set contains a combination of standard "detail rows" as well as aggregate "statistic" information.

The original SQL used a standard select left-joined to the aggregate information, similar to this:

SELECT 
    UserId, 
    Name, 
    Email, 
    ISNULL(Stats.TotalPosts, 0) as TotalPosts,
    Stats.LastPost
FROM Users
LEFT OUTER JOIN
(
    SELECT UserId, COUNT(*) as TotalPosts, MAX(DatePosted) as LastPost
    FROM Articles
    GROUP BY UserId
) as Stats ON Stats.UserId = Users.UserID

A left join is used rather than subqueries in the SELECT statement for performance reasons - more than one aggregate statistic is returned (total posts and the date of the last post)

I've had some partial success converting it to a LINQ-to-Entities query in C# 4.0, but I'm not entirely sure how the join should tie in with the group statement. I imagine I'm thinking about this in terms of SQL and not using LINQ correctly.

I had some success breaking out the statistics into a separate query:

var stats =
(
    from a in entities.Articles
    group a by a.UserId into g
    select new
    {
        UserId = g.Key,
        TotalPosts = g.Count(),
        LastUpdated = g.Max(i => i.DatePosted)
    }
);

var query =
(
    from u in entities.Users
    join s in stats on u.UserId equals s.UserId
    orderby u.Name 
    select new UserListing()
    {
        UserId = u.UserId,
        Name = u.Name,
        Email = u.Email,
        TotalPosts = s.TotalPosts,
        LastUpdated = s.LastUpdated
    }
);

Unfortunately, the join used in the LINQ query filters out all users who have not submitted any articles.

Switching to the equivalent of an outer join by including DefaultIfEmpty causes other problems - I can only return "null" for TotalPosts instead of 0. Even with "TotalPosts = (s.TotalPosts == null) ? 0 : s.TotalPosts" in the select, an exception is thrown unless the TotalPosts property is nullable.

What are the best practices for combining detail rows and aggregate information in this way?

Thanks!

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

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

发布评论

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

评论(3

允世 2024-10-07 20:35:32

试试这个:

var query =
(
    from u in entities.Users
    join s in stats on u.UserId equals s.UserId into g
    from a in g.DefaultIfEmpty()
    orderby u.Name 
    select new UserListing()
    {
        UserId = u.UserId,
        Name = u.Name,
        Email = u.Email,
        TotalPosts = a.TotalPosts,
        LastUpdated = a.LastUpdated
    }
);

Try this:

var query =
(
    from u in entities.Users
    join s in stats on u.UserId equals s.UserId into g
    from a in g.DefaultIfEmpty()
    orderby u.Name 
    select new UserListing()
    {
        UserId = u.UserId,
        Name = u.Name,
        Email = u.Email,
        TotalPosts = a.TotalPosts,
        LastUpdated = a.LastUpdated
    }
);
七禾 2024-10-07 20:35:32

您可以选择的一种方法是确保 stats 查询中的相应属性可以为空。如果可能的话,LINQ-to-entities 将进行必要的调整以使这项工作正常进行。然后照常执行左外连接。

var stats =
(
    from a in entities.Articles
    group a by a.UserId into g
    select new
    {
        UserId = g.Key,
        TotalPosts = (int?)g.Count(),
        LastUpdated = g.Max(i => i.DatePosted)
    }
);

var query =
(
    from u in entities.Users
    join s in stats on u.UserId equals s.UserId into joinedStats
    from s in joinedStats.DefaultIfEmpty() // do left outer join
    orderby u.Name 
    select new UserListing()
    {
        UserId = u.UserId,
        Name = u.Name,
        Email = u.Email,
        TotalPosts = s.TotalPosts,  // null if doesn't contain stats
        LastUpdated = s.LastUpdated // default DateTime if doesn't contain stats
    }
);

One option you have is to make sure that the appropriate properties in the stats query are nullable. LINQ-to-entities will make the necessary adjustments to make this work if at all possible. Then perform the left outer join as usual.

var stats =
(
    from a in entities.Articles
    group a by a.UserId into g
    select new
    {
        UserId = g.Key,
        TotalPosts = (int?)g.Count(),
        LastUpdated = g.Max(i => i.DatePosted)
    }
);

var query =
(
    from u in entities.Users
    join s in stats on u.UserId equals s.UserId into joinedStats
    from s in joinedStats.DefaultIfEmpty() // do left outer join
    orderby u.Name 
    select new UserListing()
    {
        UserId = u.UserId,
        Name = u.Name,
        Email = u.Email,
        TotalPosts = s.TotalPosts,  // null if doesn't contain stats
        LastUpdated = s.LastUpdated // default DateTime if doesn't contain stats
    }
);
狼亦尘 2024-10-07 20:35:32

要获得外部联接,您需要使用 DefaultIfEmpty
要解决 null 问题,您可以尝试

TotalPosts = s.TotalPosts.GetValueOrDefault(),

,或者如果 s.TotalPosts 不知何故没有显示为 int? 您可以尝试像

TotalPosts = ((int?)s.TotalPosts).GetValueOrDefault(0),

To get an outer join you need to use DefaultIfEmpty.
To solve the null issue you might try

TotalPosts = s.TotalPosts.GetValueOrDefault(),

or if s.TotalPosts somehow does not turn up as int? you might try hacks like

TotalPosts = ((int?)s.TotalPosts).GetValueOrDefault(0),
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文