Используя Linq to SQL, как найти минимальные и максимальные значения столбца в таблице? - PullRequest
27 голосов
/ 15 февраля 2010

Я хочу найти самый быстрый способ получить минимальное и максимальное значения столбца в таблице с помощью одного обращения Linq к SQL. Так что я знаю, что это будет работать в два этапа:

int min = MyTable.Min(row => row.FavoriteNumber);
int max = MyTable.Max(row => row.FavoriteNumber);

Я знаю, что могу использовать group, но у меня нет предложения group by, я хочу агрегировать по всей таблице! И я не могу использовать .Min без группировки в первую очередь. Я попробовал это:

from row in MyTable 
group row by true into r 
select new { 
    min = r.Min(z => z.FavoriteNumber), 
    max = r.Max(z => z.FavoriteNumber) 
}

Но это сумасшедшее предложение группы кажется глупым, и SQL, который он делает, более сложен, чем должен быть.

Итак, есть ли способ просто вывести правильный SQL?

РЕДАКТИРОВАТЬ: Эти ребята тоже не удалось: Linq to SQL: как агрегировать без группы с помощью? ... хромает надзор за дизайнерами LINQ, если на самом деле нет ответа.

РЕДАКТИРОВАТЬ 2: Я рассмотрел свое собственное решение (с бессмысленной константой group by by) в анализе плана выполнения SQL Server Management Studio, и мне кажется, что оно идентично плану, сгенерированному:

SELECT MIN(FavoriteNumber), MAX(FavoriteNumber)
FROM MyTable

Поэтому, если кто-то не может придумать более простой или столь же хороший ответ, я думаю, что я должен пометить его как отвеченный самостоятельно. Мысли

Ответы [ 5 ]

30 голосов
/ 23 февраля 2010

Как указано в вопросе, этот метод, по-видимому, фактически генерирует оптимальный код SQL, поэтому, хотя он выглядит немного неуклюже в LINQ, он должен быть оптимальным с точки зрения производительности.

from row in MyTable  
group row by true into r  
select new {  
    min = r.Min(z => z.FavoriteNumber),  
    max = r.Max(z => z.FavoriteNumber)  
} 
6 голосов
/ 16 февраля 2010

Я мог бы найти только тот, который производит несколько чистый SQL, но не очень эффективный по сравнению с выбором min (val), max (val) из таблицы:

var r =
  (from min in items.OrderBy(i => i.Value)
   from max in items.OrderByDescending(i => i.Value)
   select new {min, max}).First();

sql

SELECT TOP (1)
    [t0].[Value],
    [t1].[Value] AS [Value2]
FROM
    [TestTable] AS [t0],
    [TestTable] AS [t1]
ORDER BY
    [t0].[Value],
    [t1].[Value] DESC

Тем не менее есть еще один вариант использования одного подключения для минимального и максимального запросов (см. Несколько активных наборов результатов (MARS) )

или хранимая процедура ..

2 голосов
/ 15 февраля 2010

Я пока не знаю, как перевести его на C # (я работаю над этим)

Это версия на Haskell

minAndMax :: Ord a => [a] -> (a,a)
minAndMax [x]    = (x,x)
minAndMax (x:xs) = (min a x, max b x)
                   where (a,b) = minAndMax xs

Версия C # должна включать Aggregate кое-как (я думаю).

1 голос
/ 15 февраля 2010

LINQ to SQL запрос - это одно выражение. Таким образом, если вы не можете выразить свой запрос в одном выражении (или вам не понравится один раз, когда вы это делаете), тогда вам придется искать другие варианты.

Хранимые процедуры, поскольку они могут иметь выписки, позволяют выполнить это за один раз. У вас будет два выходных параметра или вы выберете результирующий набор из двух строк. В любом случае вам понадобится специальный код для чтения результата хранимой процедуры.

(Я лично не вижу необходимости избегать двух обходов здесь. Это кажется преждевременной оптимизацией, тем более, что вам, вероятно, придется прыгать через обручи, чтобы это заработало. Не говоря уже о времени, которое вы потратите обоснование этого решения и объяснение решения другим разработчикам.)

Другими словами, вы уже ответили на свой вопрос. «Я не могу использовать .Min без группирования сначала», а затем «это сумасшедшее предложение группы кажется глупым, а SQL, который он делает, является более сложным, чем нужно», - это подсказки, что простой и понятный Решение в обе стороны - лучшее выражение вашего намерения (если вы не пишете пользовательский SQL).

0 голосов
/ 15 февраля 2010

Вы можете выбрать всю таблицу и выполнить свои минимальные и максимальные операции в памяти:

var cache = // select *

var min = cache.Min(...);
var max = cache.Max(...);

В зависимости от того, насколько велик ваш набор данных, это может быть способом избежать попадания в вашу базу данных более одного раза.

...