Max или значение по умолчанию?

171
задан Winger Sendon 10 November 2015 в 21:12
поделиться

10 ответов

С тех пор DefaultIfEmpty не реализован в LINQ к SQL, я сделал поиск на ошибке, которую это возвратило и нашло захватывающая статья , которая имеет дело с пустыми множествами в агрегатных функциях. Для суммирования, что я нашел можно обойти это ограничение путем кастинга к nullable в выборе. Мой VB немного ржав, но я думаю , он прошел бы примерно так:

Dim x = (From y In context.MyTable _
         Where y.MyField = value _
         Select CType(y.MyCounter, Integer?)).Max

Или в C#:

var x = (from y in context.MyTable
         where y.MyField == value
         select (int?)y.MyCounter).Max();
205
ответ дан BartoszKP 23 November 2019 в 20:41
поделиться

Одно интересное различие, которое кажется стоящим замечания, - то, что, в то время как FirstOrDefault и Берут (1), генерируют тот же SQL (согласно LINQPad, так или иначе), FirstOrDefault возвращает значение - значение по умолчанию - когда нет никаких строк соответствия и Не Берут (1) возвраты никакие результаты..., по крайней мере, в LINQPad.

1
ответ дан Rex Miller 23 November 2019 в 20:41
поделиться

Я думаю, что проблема - то, что делает Вы хотите произойти, когда запрос не имеет никаких результатов. Если бы это - исключительный случай тогда, я обернул бы запрос в блок попытки/выгоды и обработал бы исключение, которое генерирует стандартный запрос. Если нормально иметь возврат запроса никакие результаты, то необходимо выяснить то, чем Вы хотите результат быть в этом случае. Может случиться так, что ответ @David (или что-то подобное будет работать). Таким образом, если МАКС всегда будет положителен, то может быть достаточно вставить известное, "плохо" оценивают в список, который будет только выбран, если не будет никаких результатов. Обычно я ожидал бы запрос, который получает максимум для имения некоторых данных, чтобы продолжить работать, и я пошел бы путем попытки/выгоды как иначе, Вы всегда вынуждаетесь проверить, корректно ли значение, которое Вы получили, или нет. Я быть бы, что неисключительный случай просто смог использовать полученное значение.

Try
   Dim x = (From y In context.MyTable _
            Where y.MyField = value _
            Select y.MyCounter).Max
   ... continue working with x ...
Catch ex As SqlException
       ... do error processing ...
End Try
7
ответ дан tvanfosson 23 November 2019 в 20:41
поделиться

Вы могли всегда добавлять Double.MinValue к последовательности. Это гарантировало бы, что существует по крайней мере один элемент, и Max возвратил бы его, только если это - на самом деле минимум. Для определения, какая опция более эффективна (Concat, FirstOrDefault или Take(1)), необходимо выполнить соответствующее сравнительное тестирование.

double x = context.MyTable
    .Where(y => y.MyField == value)
    .Select(y => y.MyCounter)
    .Concat(new double[]{Double.MinValue})
    .Max();
24
ответ дан David Schmitt 23 November 2019 в 20:41
поделиться

Думайте о том, что Вы спрашиваете!

макс. из {1, 2, 3,-1,-2,-3} является, очевидно, 3. Макс. из {2} является, очевидно, 2. Но каково макс. из пустого множества {}? Очевидно, это - бессмысленный вопрос. Макс. из пустого множества просто не определяется. Попытка получить ответ является математической ошибкой. Макс. из любого набора должен самостоятельно быть элемент в том наборе. Пустое множество не имеет никаких элементов, так утверждая, что некоторое конкретное число является макс. из того набора, не будучи в том наборе, математическое противоречие.

Так же, как это - корректное поведение для компьютера для выдачи исключения, когда программист просит, чтобы он разделился на нуль, таким образом, это - корректное поведение для компьютера для выдачи исключения, когда программист просит, чтобы он взял макс. из пустого множества. Деление на нуль, беря макс. из пустого множества, wiggering spacklerorke, и ездя на летающем единороге в Неверленд все бессмысленно, невозможно, не определено.

Теперь, что Вы на самом деле хотите сделать?

35
ответ дан yfeldblum 23 November 2019 в 20:41
поделиться

Походит на случай для DefaultIfEmpty (непротестированный код следует):

Dim x = (From y In context.MyTable _
         Where y.MyField = value _
         Select y.MyCounter).DefaultIfEmpty.Max
47
ответ дан BartoszKP 23 November 2019 в 20:41
поделиться

Другая возможность сгруппировалась бы, подобный тому, как Вы могли бы приблизиться к ней в необработанном SQL:

from y in context.MyTable
group y.MyCounter by y.MyField into GrpByMyField
where GrpByMyField.Key == value
select GrpByMyField.Max()

единственная вещь (тестирующий снова в LINQPad) переключающийся на разновидность VB LINQ, дает синтаксические ошибки на группирующемся пункте. Я уверен, что концептуальный эквивалент достаточно легко найти, я просто не знаю, как отразить его в VB.

сгенерированный SQL был бы чем-то вроде:

SELECT [t1].[MaxValue]
FROM (
    SELECT MAX([t0].[MyCounter) AS [MaxValue], [t0].[MyField]
    FROM [MyTable] AS [t0]
    GROUP BY [t0].[MyField]
    ) AS [t1]
WHERE [t1].[MyField] = @p0

вложенные неприглядные взгляды ВЫБОРА, как выполнение запросов получили бы все строки, тогда выбирают соответствующий из полученного набора..., вопрос состоит в том, оптимизирует ли SQL Server запрос во что-то сопоставимое с применением где пункт к внутреннему ВЫБОРУ. Я изучаю это теперь...

я не являюсь сведущим в интерпретации планов выполнения в SQL Server, но похоже, когда оператор Where находится на внешнем ВЫБОРЕ, количество фактических строк, приводящих к тому шагу, является всеми строками в таблице, только по сравнению со строками соответствия, когда оператор Where находится на внутреннем ВЫБОРЕ. Однако похоже, что только 1%-я стоимость смещается к следующему шагу, когда все строки рассматривают, и так или иначе только одна строка когда-либо возвращается из SQL Server поэтому, возможно, дело не в этом большой из различия в главной схеме вещей.

6
ответ дан Rex Miller 23 November 2019 в 20:41
поделиться

У меня была аналогичная проблема, но я использовал методы расширения LINQ для списка, а не синтаксиса запроса. Здесь также работает приведение к обнуляемому трюку:

int max = list.Max(i => (int?)i.MyCounter) ?? 0;
99
ответ дан 23 November 2019 в 20:41
поделиться

немного поздно, но у меня было такое же беспокойство ...

Перефразируя код из исходного сообщения, вы хотите получить максимум set S определяется

(From y In context.MyTable _
 Where y.MyField = value _
 Select y.MyCounter)

Принимая во внимание ваш последний комментарий

Достаточно сказать, что я знаю, что хочу 0 когда нет записей для выбора из, что определенно имеет влияние о возможном решении

Я могу перефразировать вашу проблему следующим образом: Вам нужно максимум {0 + S}. И похоже, что предложенное решение с concat семантически является правильным: -)

var max = new[]{0}
          .Concat((From y In context.MyTable _
                   Where y.MyField = value _
                   Select y.MyCounter))
          .Max();
6
ответ дан 23 November 2019 в 20:41
поделиться

Чтобы все знали, что использует Linq to Entities, приведенные выше методы не будут работать ...

Если вы попытаетесь сделать что-то вроде

var max = new[]{0}
      .Concat((From y In context.MyTable _
               Where y.MyField = value _
               Select y.MyCounter))
      .Max();

Это вызовет исключение:

System.NotSupportedException: тип узла выражения LINQ 'NewArrayInit' не поддерживается в LINQ to Entities.

Я бы посоветовал просто выполнить

(From y In context.MyTable _
                   Where y.MyField = value _
                   Select y.MyCounter))
          .OrderByDescending(x=>x).FirstOrDefault());

И FirstOrDefault вернет 0, если ваш список пуст.

2
ответ дан 23 November 2019 в 20:41
поделиться
Другие вопросы по тегам:

Похожие вопросы: