Поддерживает ли SQL Server GREATEST и LEAST, если нет, то каков общий обходной путь?

16

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

least(extendDate,min), greatest(extendDate,max)

Когда я пытаюсь использовать их, я получаю

'least' is not a recognized built-in function name.
'greatest' is not a recognized built-in function name.

Это будет охватывать расширение в любом направлении.

Для целей вопроса вам все равно придется делать исключительную замену диапазона.

Мне просто интересно, как пользователи SQL Server реализуют шаблоны запросов для имитации leastи greatestфункциональности.

Развертываете ли вы условия в CASEзаявлениях или есть расширение, сторонняя надстройка или лицензия от Microsoft, которая включает эту функцию?

Эван Кэрролл
источник
Невероятно, что MSSQL не имеет реализации для LEAST/ GREATESTфункций - почти все конкуренты RDBMS имеют как минимум эквиваленты. Единственное исключение, которое я смог найти, - это Sybase, но это также было прекращено в течение многих лет.
bsplosion
1
feedback.azure.com/forums/908035-sql-server/suggestions/… если вы хотите проголосовать за это
J Brune

Ответы:

32

Один из распространенных методов - использовать VALUESпредложение, и CROSS APPLYдва столбца с псевдонимами будут представлены как один столбец, а затем получить MINи MAXкаждого.

SELECT MIN(x.CombinedDate) AS least, MAX(x.CombinedDate) AS greatest
FROM   dbo.Users AS u
CROSS APPLY ( VALUES ( u.CreationDate ), ( u.LastAccessDate )) AS x ( CombinedDate );

Есть и другие способы написания, например, используя UNION ALL

SELECT MIN(x.CombinedDate) AS least, MAX(x.CombinedDate) AS greatest
FROM   dbo.Users AS u
CROSS APPLY ( SELECT u.CreationDate UNION ALL SELECT u.LastAccessDate ) AS x(CombinedDate);

Тем не менее, итоговые планы запросов выглядят одинаково.

Эрик Дарлинг
источник
13

Вы также можете поместить значения встроенными в подзапрос. Как это:

select (select max(i) from (values (1), (2), (5), (1), (6)) AS T(i)) greatest,
       (select min(i) from (values (1), (2), (5), (1), (6)) AS T(i)) least
Дэвид Браун - Microsoft
источник
3

Это было бы хорошим началом -

CASE WHEN A > B THEN A ELSE B END
Джим Гетма
источник
Это хорошее предложение, но оно было упомянуто в вопросе с «развертыванием условия в заявлениях CASE»
Эван Кэрролл
3

Меньше всего эквивалент:

IIF(@a < @b, @a, @b)

САМЫЙ БОЛЬШОЙ эквивалент

IIF(@a > @b, @a, @b)
Эльнур
источник
3
Как вы делаете это для трех или более значений, например least(5,6,7,8,9)?
a_horse_with_no_name
@a_horse_with_no_name Использовать вложенные ИИФ
Эльнур
Такой подход быстро станет сложным для чтения и проверки ... Как это с точки зрения производительности?
Додекафон
0

Я создаю пользовательские функции, например

create function dbo.udf_LeastInt(@a int, @b int)
returns int
with schemabinding
as
begin
  return case when @a <= @b then @a 
              when @b < @a  then @b
              else null
         end
end

Хотя это может работать в простых случаях, однако есть несколько проблем с этим подходом:

  • Досадно, что вы должны сделать отдельные функции для каждого типа данных.
  • Он обрабатывает только 2 параметра, поэтому может потребоваться больше функций для обработки многих параметров или использования вложенных вызовов тех же функций.
  • Было бы лучше (более эффективно) использовать встроенный TVF, а не скалярную функцию. Это связано с реализацией скалярных функций в глубине души. Об этом много блогов, см., Например, SQL 101: Ингибиторы параллелизма - скалярные пользовательские функции (автор Джон Кехайяс .
  • Если один из аргументов равен нулю, он возвращает ноль. Это соответствует тому, что leastоператор делает в Oracle и MySQL, но отличается от Postgres. Но эта защита от нуля делает его более многословным (если вы знаете, что они не будут нулевыми, case when @a <= @b then @a else @b endсработает простая ).

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

Эд Авис
источник
0

Я собирался добавить комментарий к ответу @ ed-avis, но не смог этого сделать из-за отсутствия репутации, поэтому разместил это как продолжение своего ответа.

Я устранил недостаток: «Досадно, что вы должны создавать отдельные функции для каждого типа данных». Использование SQL_VARIANT .

Вот моя реализация:

CREATE OR ALTER FUNCTION my_least(@a SQL_VARIANT, @b SQL_VARIANT)
returns SQL_VARIANT
with schemabinding
as
begin
  return case when @a <= @b then @a 
              when @b < @a  then @b
              WHEN @a IS NULL THEN @b
              WHEN @b IS NULL THEN @a
              else null
         end
END;

Также эта функция обрабатывает NULL как версия postgresql.

Эту функцию можно добавить в БД для удобства, но она в 10 раз медленнее , чем встроенная IIF. Мои тесты показывают, что такая функция с точным типом ( datetime ) выполняет так же, как версия sql_variant .

PS Я запускаю несколько тестов для набора данных со значениями в 350 тыс., И кажется, что производительность одинакова, sql_variant чуть-чуть быстрее, но я считаю, что это просто дрожь.

Но в любом случае версия IIF в 10 раз быстрее !!!

Я не тестировал inline, CASE WHENно в основном для t-sql IIF такой же, как case , и iif get конвертируется оптимизатором в case case.

Тот факт, что IIF переведен на CASE, также влияет на другие аспекты поведения этой функции.

ЗАКЛЮЧЕНИЕ: быстрее использовать IIF, если производительность имеет значение, но для прототипирования или если ясность кода более необходима и не требуются большие вычисления, при условии, что можно использовать функцию.

Богдан Март
источник
1
Вы говорите, что «sqlvariant чуть-чуть быстрее» и «версия IIF в 10 раз быстрее». быстрее чем что?
ypercubeᵀᴹ
Sql вариант ver примерно такой же скорости, как конкретная версия, как и в другом ответе. В моем тесте это было 80 мс (из 15 с), я предполагаю, что просто ошибка статистики. И использование iif(a<b, a, b) в 10 раз быстрее, чем любая пользовательская функция.
Богдан Март
Чтобы было ясно, я использовал мой код с заменой sql_variant на datetime в качестве второй функции. После тестов кажется, что sql_variant не добавляет никаких накладных расходов, но пользовательские функции работают намного медленнее, чем встроенные
Богдан Март
Но является ли какая-либо из этих функций - в том числе IIF()- быстрее, чем использование CASEвыражения? Я хочу сказать, что, поскольку вы столкнулись с проблемой тестирования производительности, вы должны протестировать все предложенные методы / ответы.
ypercubeᵀᴹ
1
@ yper-crazyhat-cubeᵀᴹ обновленный ответ. Больше редактировать его не буду, просто хотел добавить комментарий относительно sql_variant к ответу ed-avis, но из-за отсутствия пинты пришлось написать расширенный ответ :-)
Богдан Март