2010-12-13 188 views
22

在SQL,你可以在一个单一的数据库查询这样的表达多种聚集:多个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到-Entities?我发现一个similar question表明它不可能用于Linq-to-SQL,但我想我不需要使用四次往返数据库来完成上述操作。

+0

+1有趣的问题,我没有亲自看过它,但如果它不起作用,它会很奇怪。你不能使用'let'语法吗? – 2010-12-13 17:53:05

+0

@Thomas,据我所见,不可能使用'let'来分配一个集合,而只是集合中某个项目的一个值。我一直在使用EF几个月,今天很惊讶地发现这个障碍。我真的不认为这是可能的! – 2010-12-13 18:08:10

+0

@猎人的删除答案作品。你试过了吗?另外,'let'确实可以和集合一起工作。你尝试过吗?我有两个产品代码。 – 2010-12-15 18:14:45

回答

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) 
+0

这似乎工作得很好。感谢您使用比使用Entity SQL更加优雅和强大的解决方案来解决此问题。我发现使用'g组1到g'对我很好。再次感谢! – 2011-01-30 23:53:44

+0

建议的解决方案是使用分组,尽管在问题分组中没有提到。 。如果没有涉及分组,我只想执行这个sql:select sum(A),max(B),avg(C)from TBL? – Goran 2013-06-20 23:23:24

+0

@Goran这是由PS覆盖 - 你一个恒定的组。 (这感觉有点奇怪,但似乎是规范的答案,我也看到它在原始SQL查询中也完成了。) – starwed 2016-03-14 12:50:26

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] 

如果有人发现一个更优雅的解决这个问题,我会授予他们接受的答案。


EDIT 2

感谢科斯塔谁发现a great solution to this problem使用纯LINQ的。

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(); 
+0

Hi @Craig。我的机器上没有Northwind,但通过眼球,我认为这个查询会为每个拥有该邮政编码的客户生成一个条目。我想要做的就是为具有该邮政编码的任何客户确定最大OrderId。那可能吗?我可能太多简化了我的原始问题。我会更新它,使其更接近我的实际模式。 – 2010-12-16 03:20:42

+0

好的,查看更新后的答案。这可能可以简化,但我选择遵循第一个查询的模式。 – 2010-12-16 14:00:04

+0

万一你有兴趣,我发现了一个可行的解决方案,虽然它不是最漂亮的。 – 2011-01-12 08:17:40