Каков наилучший способ получить значение Max из запроса LINQ, который может не возвращать строки? Если я просто сделаю
Dim x = (From y In context.MyTable _
Where y.MyField = value _
Select y.MyCounter).Max
Я получаю сообщение об ошибке, когда запрос не возвращает строк. я мог бы сделать
Dim x = (From y In context.MyTable _
Where y.MyField = value _
Select y.MyCounter _
Order By MyCounter Descending).FirstOrDefault
но это выглядит немного глупо для такого простого запроса. Я пропускаю лучший способ сделать это?
ОБНОВЛЕНИЕ: вот предыстория: я пытаюсь получить следующий счетчик правомочности из дочерней таблицы (устаревшая система, не заводите меня ...). Первая строка приемлемости для каждого пациента всегда равна 1, вторая - 2 и т. Д. (Очевидно, это не первичный ключ дочерней таблицы). Итак, я выбираю максимальное существующее значение счетчика для пациента, а затем добавляю к нему 1, чтобы создать новую строку. Когда нет дочерних значений, мне нужно, чтобы запрос возвратил 0 (поэтому добавление 1 даст мне значение счетчика 1). Обратите внимание, что я не хочу полагаться на необработанный счетчик дочерних строк в случае, если унаследованное приложение вводит пробелы в значениях счетчика (возможно). Моя плохая попытка сделать вопрос слишком общим.
источник
Max
aDateTime
.Max(x => (DateTime?)x.TimeStamp)
до сих пор единственный путь ..У меня просто была похожая проблема, но я использовал в списке методы расширения LINQ, а не синтаксис запроса. Там также работает приведение к трюку Nullable:
источник
Звучит как случай для
DefaultIfEmpty
(непроверенный код следует):источник
var colCount = RowsEnumerable.Select(row => row.Cols.Count).DefaultIfEmpty().Max()
Подумай о том, что ты спрашиваешь!
Максимум {1, 2, 3, -1, -2, -3}, очевидно, равен 3. Максимум {2}, очевидно, равен 2. Но каков максимум пустого множества {}? Очевидно, что это бессмысленный вопрос. Максимум пустого набора просто не определен. Попытка получить ответ - математическая ошибка. Максимум любого набора сам должен быть элементом этого набора. Пустое множество не имеет элементов, поэтому утверждение о том, что какое-то конкретное число является максимумом этого набора, не входя в этот набор, является математическим противоречием.
Точно так же, как правильное поведение компьютера - генерировать исключение, когда программист просит его делить на ноль, так и корректное поведение компьютера - генерировать исключение, когда программист просит его принять максимум пустого набора. Деление на ноль, взятие максимума пустого сета, вигер spacklerorke и езда летающего единорога в Neverland - все это бессмысленно, невозможно, неопределенно.
Теперь, что вы на самом деле хотите сделать?
источник
Вы всегда можете добавить
Double.MinValue
к последовательности. Это обеспечит наличие хотя бы одного элемента иMax
будет возвращать его только в том случае, если оно действительно минимально. Для того, чтобы определить , какой вариант является более эффективным (Concat
,FirstOrDefault
илиTake(1)
), вы должны выполнить адекватный сравнительный анализ.источник
Если в списке есть какие-либо элементы (т. Е. Не пустые), он займет максимум поля MyCounter, иначе вернет 0.
источник
Начиная с .Net 3.5 вы можете использовать DefaultIfEmpty (), передавая значение по умолчанию в качестве аргумента. Что-то вроде одного из следующих способов:
Первый допускается, когда вы запрашиваете столбец NOT NULL, а второй - способ его использования для запроса столбца NULLABLE. Если вы используете DefaultIfEmpty () без аргументов, значением по умолчанию будет то, что определено для типа вашего вывода, как вы можете видеть в Таблице значений по умолчанию .
Полученный SELECT не будет таким элегантным, но приемлемым.
Надеюсь, поможет.
источник
Я думаю, что проблема в том, что вы хотите, чтобы произошло, когда запрос не дал результатов. Если это исключительный случай, я бы обернул запрос в блок try / catch и обработал исключение, которое генерирует стандартный запрос. Если нормально, чтобы запрос не возвращал результатов, вам нужно выяснить, каким должен быть результат в этом случае. Это может быть ответ @ Дэвида (или что-то подобное будет работать). То есть, если MAX всегда будет положительным, тогда может быть достаточно вставить известное «плохое» значение в список, который будет выбран только при отсутствии результатов. Как правило, я ожидал бы, что запрос, который извлекает максимум, будет иметь какие-то данные для работы, и я бы пошел по пути try / catch, иначе вы всегда будете вынуждены проверять правильность полученного значения или нет. Я'
источник
Другая возможность - группировка, похожая на то, как вы можете подходить к ней в сыром SQL:
Единственное (переключение снова в LINQPad) при переключении на VB LINQ дает синтаксические ошибки в предложении группировки. Я уверен, что концептуальный эквивалент достаточно легко найти, я просто не знаю, как отразить его в VB.
Сгенерированный SQL будет выглядеть примерно так:
Вложенный SELECT выглядит неприглядно, как будто выполнение запроса извлечет все строки, а затем выберет соответствующую из извлеченного набора ... вопрос в том, оптимизирует ли SQL Server запрос в нечто сравнимое с применением условия where к внутреннему SELECT. Я смотрю на это сейчас ...
Я не очень хорошо разбираюсь в интерпретации планов выполнения в SQL Server, но похоже, что когда предложение WHERE находится во внешнем SELECT, число фактических строк, приводящих к этому шагу, равно всем строкам таблицы, а не только совпадающим строкам. когда предложение WHERE находится во внутреннем SELECT. Тем не менее, похоже, что только 1% затрат переносится на следующий шаг, когда рассматриваются все строки, и в любом случае с SQL Server возвращается только одна строка, так что, возможно, это не так уж много различий в общей схеме вещей ,
источник
немного поздно, но у меня была такая же проблема ...
Перефразируя ваш код из исходного поста, вы хотите, чтобы максимум набора S определялся как
Принимая во внимание ваш последний комментарий
Я могу перефразировать вашу проблему как: Вы хотите максимум {0 + S}. И похоже, что предложенное решение с concat семантически правильное :-)
источник
Почему не что-то более прямое, как:
источник
Интересное отличие, которое, по-видимому, стоит отметить, заключается в том, что хотя FirstOrDefault и Take (1) генерируют один и тот же SQL (согласно LINQPad, в любом случае), FirstOrDefault возвращает значение - значение по умолчанию - при отсутствии совпадающих строк и Take (1) возвращает нет результатов ... по крайней мере, в LINQPad.
источник
Просто чтобы все знали, что с помощью Linq to Entities вышеописанные методы не будут работать ...
Если вы попытаетесь сделать что-то вроде
Это выдаст исключение:
Я бы предложил просто сделать
И
FirstOrDefault
вернет 0, если ваш список пуст.источник
источник
Я выбил
MaxOrDefault
метод расширения. В этом нет ничего особенного, но его присутствие в Intellisense является полезным напоминанием о том, чтоMax
пустая последовательность вызовет исключение. Кроме того, метод позволяет указать значение по умолчанию, если требуется.источник
Для Entity Framework и Linq to SQL мы можем достичь этого, определив метод расширения, который изменяет
Expression
передаваемыйIQueryable<T>.Max(...)
метод:Использование:
Сгенерированный запрос идентичен, он работает так же, как обычный вызов
IQueryable<T>.Max(...)
метода, но если нет записей, он возвращает значение по умолчанию типа T вместо того, чтобы выдавать исключениеисточник
У меня просто была похожая проблема, мои модульные тесты прошли с использованием Max (), но потерпели неудачу при запуске с действующей базой данных.
Моим решением было отделить запрос от выполняемой логики, а не объединять их в одном запросе.
Мне нужно было решение для работы в модульных тестах с использованием Linq-объектов (в Linq-objects Max () работает с нулями) и Linq-sql при выполнении в реальной среде.
(Я высмеиваю Select () в моих тестах)
Менее эффективны? Наверное.
Мне все равно, пока мое приложение не упадет в следующий раз? Нет.
источник