带 EF Core 6.0 和 SQL 服务器的 GroupBy
GroupBy with EF Core 6.0 and SQL Server
我有一个 Blazor Web 应用程序,它已经在现场工作了几个月。我想将数据库查询扩展到一组类似的“检测”。
它是从 .NET 5 开始编写的,就在今天更新到 .NET 6 试图让它工作。
我想知道如何获取按 TimeStamp
(日期时间 属性)排序的结果。我有一个内存数据库的工作示例,但生产将在 SQL 服务器中。我在 SQL 方面不是很好,但我在 Management Studio 中玩了一段时间但运气不好。
注释掉 OrderByDescending()
正确分组,但结果顺序不正确。似乎 EF 翻译过程完全删除了该行,它对生成的查询或结果集没有影响。
var results = context.Detections
//Line below makes no change ignored by SQL Server. Works when using in memory DB.
//.OrderByDescending(det => det.TimeStamp)
.GroupBy(det => new
{
Year = det.TimeStamp.Year,
Month = det.TimeStamp.Month,
Day = det.TimeStamp.Day,
Hour = det.TimeStamp.Hour,
})
.Select(grp => new
{
Count = grp.Count(),
Detection = grp.OrderByDescending(det => det.TimeStamp).First(),
})
//The following line will not translate
//.OrderByDescending(det => det.Detection.TimeStamp)
.ToList();
如果有任何问题:
- Visual Studio 2022 (4.8.04084)
- .Net 6.0
- SQL 服务器 2019 (15.0.2080.9)
- *所有与 EF 相关的 NuGet 包已更新至 6.0
编辑澄清
上面的代码段产生以下 SQL 查询。
SELECT [t].[c], [t0].[Id], [t0].[TimeStamp]
FROM (
SELECT COUNT(*) AS [c], DATEPART(year, [d].[TimeStamp]) AS [c0], DATEPART(month, [d].[TimeStamp]) AS [c1], DATEPART(day, [d].[TimeStamp]) AS [c2], DATEPART(hour, [d].[TimeStamp]) AS [c3]
FROM [Detections] AS [d]
WHERE [d].[TimeStamp] > DATEADD(day, CAST(-16.0E0 AS int), GETUTCDATE())
GROUP BY DATEPART(year, [d].[TimeStamp]), DATEPART(month, [d].[TimeStamp]), DATEPART(day, [d].[TimeStamp]), DATEPART(hour, [d].[TimeStamp])
) AS [t]
OUTER APPLY (
SELECT TOP(1) [d0].[Id], [d0].[TimeStamp]
FROM [Detections] AS [d0]
WHERE ([d0].[TimeStamp] > DATEADD(day, CAST(-30.0E0 AS int), GETUTCDATE())) AND (((([t].[c0] = DATEPART(year, [d0].[TimeStamp])) AND ([t].[c1] = DATEPART(month, [d0].[TimeStamp]))) AND ([t].[c2] = DATEPART(day, [d0].[TimeStamp]))) AND ([t].[c3] = DATEPART(hour, [d0].[TimeStamp])))
ORDER BY [d0].[TimeStamp] DESC
) AS [t0]
它产生类似于以下的结果。注意没有按时间排序。
1 628591 2021-11-02 14:34:06.0442966
10 628601 2021-11-12 05:43:27.7015291
150 628821 2021-11-12 21:59:27.6444236
20 628621 2021-11-12 06:17:13.7798282
50 628671 2021-11-12 15:17:23.8893856
如果我在 Management Studio 中的那个 SQL 查询的末尾添加 ORDER BY [t0].TimeStamp DESC
,我会得到我正在寻找的结果(见下文)。我只需要知道如何在 LINQ 中编写它。
150 628821 2021-11-12 21:59:27.6444236
50 628671 2021-11-12 15:17:23.8893856
20 628621 2021-11-12 06:17:13.7798282
10 628601 2021-11-12 05:43:27.7015291
1 628591 2021-11-02 14:34:06.0442966
在 ToList()
之前的末尾添加 .OrderByDescending(det => det.Detection.TimeStamp)
是我的第一个想法,但“无法翻译”。我需要对这些结果进行分页,所以我真的很想在 SQL.
中进行排序
GroupBy 必须自己进行排序,这样 'ignores' 才不会完全出乎意料。
将其移至分组下方:
var results = context.Detections
//.OrderByDescending(det => det.TimeStamp)
.GroupBy(det => new
{
Year = det.TimeStamp.Year,
Month = det.TimeStamp.Month,
Day = det.TimeStamp.Day,
Hour = det.TimeStamp.Hour,
})
// .OrderByDescending(grp => grp.Key) // may have to split into y/m/d/h again
.OrderByDescending(grp => grp.Key.Year)
.ThenByDescending( grp => grp.Key.Month)
.ThenByDescending( grp => grp.Key.Day)
.ThenByDescending( grp => grp.Key.Hour)
.Select(grp => new
{
Count = grp.Count(),
Detection = grp.OrderByDescending(det => det.TimeStamp).First(),
})
.ToList();
当 EF 支持时,
的排序和分组可能会变得更容易一些
.GroupBy(det => new
{
Date = det.TimeStamp.Date,
Hour = det.TimeStamp.Hour,
})
对于以后查看此内容的任何人。
我能够通过声明和填充 TimeStamp
属性 并在末尾使用 OrderByDescending()
来完成这项工作。我不确定这是否是最佳解决方案,但它确实解决了我的问题。
var results = context.Detections
.GroupBy(det => new
{
Year = det.TimeStamp.Year,
Month = det.TimeStamp.Month,
Day = det.TimeStamp.Day,
Hour = det.TimeStamp.Hour,
})
.Select(grp => new
{
Count = grp.Count(),
TimeStamp = grp.OrderByDescending(det => det.TimeStamp).First().TimeStamp,
Detection = grp.OrderByDescending(det => det.TimeStamp).First(),
})
.OrderByDescending(det => det.TimeStamp)
.ToList();
我有一个 Blazor Web 应用程序,它已经在现场工作了几个月。我想将数据库查询扩展到一组类似的“检测”。
它是从 .NET 5 开始编写的,就在今天更新到 .NET 6 试图让它工作。
我想知道如何获取按 TimeStamp
(日期时间 属性)排序的结果。我有一个内存数据库的工作示例,但生产将在 SQL 服务器中。我在 SQL 方面不是很好,但我在 Management Studio 中玩了一段时间但运气不好。
注释掉 OrderByDescending()
正确分组,但结果顺序不正确。似乎 EF 翻译过程完全删除了该行,它对生成的查询或结果集没有影响。
var results = context.Detections
//Line below makes no change ignored by SQL Server. Works when using in memory DB.
//.OrderByDescending(det => det.TimeStamp)
.GroupBy(det => new
{
Year = det.TimeStamp.Year,
Month = det.TimeStamp.Month,
Day = det.TimeStamp.Day,
Hour = det.TimeStamp.Hour,
})
.Select(grp => new
{
Count = grp.Count(),
Detection = grp.OrderByDescending(det => det.TimeStamp).First(),
})
//The following line will not translate
//.OrderByDescending(det => det.Detection.TimeStamp)
.ToList();
如果有任何问题:
- Visual Studio 2022 (4.8.04084)
- .Net 6.0
- SQL 服务器 2019 (15.0.2080.9)
- *所有与 EF 相关的 NuGet 包已更新至 6.0
编辑澄清
上面的代码段产生以下 SQL 查询。
SELECT [t].[c], [t0].[Id], [t0].[TimeStamp]
FROM (
SELECT COUNT(*) AS [c], DATEPART(year, [d].[TimeStamp]) AS [c0], DATEPART(month, [d].[TimeStamp]) AS [c1], DATEPART(day, [d].[TimeStamp]) AS [c2], DATEPART(hour, [d].[TimeStamp]) AS [c3]
FROM [Detections] AS [d]
WHERE [d].[TimeStamp] > DATEADD(day, CAST(-16.0E0 AS int), GETUTCDATE())
GROUP BY DATEPART(year, [d].[TimeStamp]), DATEPART(month, [d].[TimeStamp]), DATEPART(day, [d].[TimeStamp]), DATEPART(hour, [d].[TimeStamp])
) AS [t]
OUTER APPLY (
SELECT TOP(1) [d0].[Id], [d0].[TimeStamp]
FROM [Detections] AS [d0]
WHERE ([d0].[TimeStamp] > DATEADD(day, CAST(-30.0E0 AS int), GETUTCDATE())) AND (((([t].[c0] = DATEPART(year, [d0].[TimeStamp])) AND ([t].[c1] = DATEPART(month, [d0].[TimeStamp]))) AND ([t].[c2] = DATEPART(day, [d0].[TimeStamp]))) AND ([t].[c3] = DATEPART(hour, [d0].[TimeStamp])))
ORDER BY [d0].[TimeStamp] DESC
) AS [t0]
它产生类似于以下的结果。注意没有按时间排序。
1 628591 2021-11-02 14:34:06.0442966
10 628601 2021-11-12 05:43:27.7015291
150 628821 2021-11-12 21:59:27.6444236
20 628621 2021-11-12 06:17:13.7798282
50 628671 2021-11-12 15:17:23.8893856
如果我在 Management Studio 中的那个 SQL 查询的末尾添加 ORDER BY [t0].TimeStamp DESC
,我会得到我正在寻找的结果(见下文)。我只需要知道如何在 LINQ 中编写它。
150 628821 2021-11-12 21:59:27.6444236
50 628671 2021-11-12 15:17:23.8893856
20 628621 2021-11-12 06:17:13.7798282
10 628601 2021-11-12 05:43:27.7015291
1 628591 2021-11-02 14:34:06.0442966
在 ToList()
之前的末尾添加 .OrderByDescending(det => det.Detection.TimeStamp)
是我的第一个想法,但“无法翻译”。我需要对这些结果进行分页,所以我真的很想在 SQL.
GroupBy 必须自己进行排序,这样 'ignores' 才不会完全出乎意料。
将其移至分组下方:
var results = context.Detections
//.OrderByDescending(det => det.TimeStamp)
.GroupBy(det => new
{
Year = det.TimeStamp.Year,
Month = det.TimeStamp.Month,
Day = det.TimeStamp.Day,
Hour = det.TimeStamp.Hour,
})
// .OrderByDescending(grp => grp.Key) // may have to split into y/m/d/h again
.OrderByDescending(grp => grp.Key.Year)
.ThenByDescending( grp => grp.Key.Month)
.ThenByDescending( grp => grp.Key.Day)
.ThenByDescending( grp => grp.Key.Hour)
.Select(grp => new
{
Count = grp.Count(),
Detection = grp.OrderByDescending(det => det.TimeStamp).First(),
})
.ToList();
当 EF 支持时,
的排序和分组可能会变得更容易一些.GroupBy(det => new
{
Date = det.TimeStamp.Date,
Hour = det.TimeStamp.Hour,
})
对于以后查看此内容的任何人。
我能够通过声明和填充 TimeStamp
属性 并在末尾使用 OrderByDescending()
来完成这项工作。我不确定这是否是最佳解决方案,但它确实解决了我的问题。
var results = context.Detections
.GroupBy(det => new
{
Year = det.TimeStamp.Year,
Month = det.TimeStamp.Month,
Day = det.TimeStamp.Day,
Hour = det.TimeStamp.Hour,
})
.Select(grp => new
{
Count = grp.Count(),
TimeStamp = grp.OrderByDescending(det => det.TimeStamp).First().TimeStamp,
Detection = grp.OrderByDescending(det => det.TimeStamp).First(),
})
.OrderByDescending(det => det.TimeStamp)
.ToList();