Несколько агрегатных функций SQL в одном запросе Linq-to-Entities - PullRequest
22 голосов
/ 13 декабря 2010

В 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?Я нашел аналогичный вопрос , который предполагает, что это невозможно для Linq-to-SQL, но я хотел бы подумать, что мне не придется делать это выше, используя четыре обращения к базе данных.

Ответы [ 3 ]

24 голосов
/ 21 января 2011

Предположим, у вас есть следующий оператор 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, по одному для каждой агрегатной функции, то есть он будет выполнятьсяв несколько проходов.Первый синтаксис дает один (довольно сложный, но эффективный) оператор 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)
2 голосов
/ 16 декабря 2010

У меня нет вашей БД, но это (с использованием модели по умолчанию EDMX Northwind.mdb - без изменений после запуска мастера новой модели) выполняется как один запрос в 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 голосов
/ 15 декабря 2010

К сожалению, ответ, кажется, нет .

Если кто-то может доказать обратное, я с радостью предоставлю им принятый ответ.


РЕДАКТИРОВАТЬ

Я нашел способ сделать это, используя 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

Спасибо Костасу, который нашел отличное решение этой проблемы , использующее чистый Linq.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...