单个Linq-to-Entities查询中的多个SQL聚合函数

时间:2010-12-13 17:47:23

标签: .net entity-framework linq-to-entities

在SQL中,您可以在单个数据库查询中表达多个聚合,如下所示:

SELECT MIN(p.x), MAX(p.x), MIN(p.y), MAX(p.y)
FROM   Places p JOIN Logs l on p.Id = l.PlaceId
WHERE  l.OwnerId = @OwnerId

是否可以使用Linq-to-Entities做同等的事情?我发现similar question表示Linq-to-SQL不可能,但我想我不需要四次往返数据库就可以完成上述操作。

3 个答案:

答案 0 :(得分:24)

假设您有以下SQL语句:

  select sum(A), max(B), avg(C) from TBL group by D

在C#中尝试:

  from t in table
  group t by D
  into g
  select new {
     s = g.Sum(x => x.A),
     m = g.Max(x => x.B),
     a = g.Average(x => x.C)
  }

- 或在VB中: -

  from t in TBL
  group t by key = D
  into g = group
  select s = g.Sum(function(x) x.A),
       m = g.Max(function(x) x.B),
       a = g.Average(function(x) x.C)

显而易见的,在VB中将是:

  aggregate t in TBL into s = Sum(t.A), m = Max(t.B), a = Average(t.C)

虽然它会给出相同的结果,但它会发出更高的成本,因为它发出多个SQL select语句,每个聚合函数一个,即它将在多个传递中运行。第一种语法提供了一个(相当复杂但有效)的SQL语句,它对数据库进行单次传递。

PS。如果您没有要分组的键(即,您需要一行作为结果,覆盖整个数据集),请使用常量,如下所示:

  from t in TBL
  group t by key = 0
  into g = group
  select s = g.Sum(function(x) x.A),
       m = g.Max(function(x) x.B),
       a = g.Average(function(x) x.C)

答案 1 :(得分:2)

我没有你的数据库,但是这个(使用Northwind.mdb的“默认”EDMX模型 - 运行新模型向导后没有变化)在LINQPad中作为一个查询运行:

var one = from c in Customers 
          where c.PostalCode == "12209"
          select new
          {
              Id = c.Orders.Max(o => o.OrderID),
              Country = c.Orders.Min(o => o.ShipCountry)
          };          

one.Dump();

根据您的评论更新:

var two = from c in Customers 
      where c.PostalCode == "12209"
      from o in c.Orders
      group o by o.Customer.PostalCode into g
      select new
      {
          PostalCode = g.Key,
          Id = g.Max(o => o.OrderID),
          Country = g.Min(o => o.ShipCountry)
      };          

two.Dump();

答案 2 :(得分:0)

不幸的是答案似乎是

如果有人能证明不然,我会很乐意给他们接受的答案。


编辑

我找到了一种使用Entity SQL执行此操作的方法。我不确定它是最好的方式,但因为它似乎只是 方式,所以默认情况下它可能是最好的:)

var cmdText = "SELECT MIN(p.x), MAX(p.x), MIN(p.y), MAX(p.y) " +
              "FROM Places AS p JOIN Logs AS l ON p.Id = l.PlaceId " +
              "WHERE l.OwnerId==123";
var results = CreateQuery<DbDataRecord>(cmdText)
var row = results.First();
var minX = (double)row[0];
var maxX = (double)row[1];
var minY = (double)row[2];
var maxY = (double)row[3];

以上并不完全是我正在使用的代码。对于没有连接的更简单的情况,这里是生成的SQL,显示只有一次数据库之旅:

SELECT 
1 AS [C1], 
[GroupBy1].[A1] AS [C2], 
[GroupBy1].[A2] AS [C3], 
[GroupBy1].[A3] AS [C4], 
[GroupBy1].[A4] AS [C5]
FROM ( SELECT 
    MIN([Extent1].[X1]) AS [A1], 
    MAX([Extent1].[X1]) AS [A2], 
    MAX([Extent1].[Y1]) AS [A3], 
    MIN([Extent1].[Y1]) AS [A4]
    FROM [dbo].[Edges] AS [Extent1]
    WHERE [Extent1].[PlaceId] = 123
)  AS [GroupBy1]

如果有人找到更优雅的解决方案,我会给他们接受的答案。


编辑2

感谢Costas发现使用纯Linq的a great solution to this problem