Чи підтримує SQL Server GREATEST та LEAST, якщо ні, що є загальним способом вирішення?


20

Переглядаючи це питання, здається, що це велика робота, яка не повинна бути потрібною. Вони намагаються розширити діапазон з датою. В інших базах даних, ви б просто використовувати 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, яка дозволяє цю функцію?


2
feedback.azure.com/forums/908035-sql-server/suggestions/…, якщо ви хочете проголосувати за це
J Brune

Відповіді:


33

Один загальний метод полягає у використанні 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);

Однак, результати запитів, схоже, однакові.


14

Ви також можете розмістити значення вбудовані в підзапит. Подобається це:

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

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 Використовувати вкладені IIF
Elnur

Цей підхід швидко став би складним для читання та перевірки ... Як він оцінюється з точки зору продуктивності?
Додекафон

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: Інгібітори паралелізму - скалярні функції, визначені користувачем (Джон Кехаяс .
  • Якщо один з аргументів є null, він повертає null. Це відповідає тому, що leastробить оператор в Oracle і MySQL, але відрізняється від Postgres. Але таке озброєння проти null робить його більш детальним (якщо ви знаєте, що вони не будуть нульовими, звичайна 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 Я запускаю кілька тестів на набір даних зі значеннями 350k, і здається, що продуктивність однакова, sql_variant крихітний трохи швидше, але я вважаю, що це просто тремтіння.

Але будь-який варіант версії IIF у 10 разів швидший !!!

Я не перевіряв вбудований, CASE WHENале в основному для t-sql IIF такий же, як у випадку , і iif перетворюється оптимізатором у вираз регістру.

Те, що IIF перекладається на CASE, також впливає на інші аспекти поведінки цієї функції.

ВИСНОВОК: Більш швидке використання IIF, якщо продуктивність має значення, але для прототипування, або якщо чіткість коду потрібна більше, і великі обчислення не задіяні, за умови, що функція може бути використана.


LEAST () та GREATEST (), якщо вони є в діалекті SQL, дозволяють порівнювати рядки між n колонками; Я взяв ОП за рішення, яке дасть рівноцінні результати. Відповідь тут (разом з кількома іншими) підтримує лише 2.
Додекафон
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.