Czy SQL Server obsługuje NAJWIĘKSZE i NAJMNIEJSZE, jeśli nie, jakie jest powszechne obejście?

20

Przeglądając to pytanie , wydaje się, że to dużo pracy, która nie powinna być potrzebna. Próbują rozszerzyć zakres o datę. W innych bazach danych wystarczy użyć greatesti least...

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

Kiedy próbuję ich użyć, rozumiem

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

Obejmowałoby to rozszerzenie w obu kierunkach.

Na potrzeby tego pytania nadal musisz dokonać wyłącznej wymiany zakresu.

Zastanawiam się tylko, jak użytkownicy SQL Server implementują wzorce zapytań naśladując leasti greatestfunkcjonalność.

Czy rozwijasz warunki w CASEzestawieniach, czy istnieje rozszerzenie, dodatek strony trzeciej lub licencja od Microsoft, która umożliwia tę funkcjonalność?

Evan Carroll
źródło
2
feedback.azure.com/forums/908035-sql-server/suggestions/... jeśli chcesz zagłosować na to
J Brune

Odpowiedzi:

33

Jedną z powszechnych metod jest użycie VALUESklauzuli i CROSS APPLYdwóch kolumn aliasowanych jako pojedyncza kolumna, a następnie pobranie MINi MAXdla każdej z nich.

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 );

Istnieją inne sposoby pisania, na przykład przy użyciu 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);

Jednak powstałe plany zapytań wydają się takie same.

Erik Darling
źródło
14

Możesz również wstawić wartości w wierszu podzapytania. Lubię to:

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
David Browne - Microsoft
źródło
3

To byłby dobry początek -

CASE WHEN A > B THEN A ELSE B END
Jim Gettma
źródło
To dobra sugestia, ale została wspomniana w pytaniu z „rozwinięciem warunku do instrukcji CASE”
Evan Carroll
3

NAJMNIEJ równoważny:

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

NAJWIĘKSZY odpowiednik:

IIF(@a > @b, @a, @b)
Elnur
źródło
3
Jak to zrobić dla trzech lub więcej wartości, np. least(5,6,7,8,9)?
a_horse_w_no_name
@ a_horse_w_na_nazwie Użyj zagnieżdżonych IIF
Elnur
Takie podejście szybko stałoby się trudne do przeczytania i zweryfikowania ... Jak sobie radzi pod względem wydajności?
Dodekafon
0

Tworzę funkcje zdefiniowane przez użytkownika, np

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

Chociaż może to działać w prostych przypadkach, z tym podejściem wiąże się kilka problemów:

  • Irytujące jest tworzenie osobnych funkcji dla każdego typu danych.
  • Obsługuje tylko 2 parametry, więc może być potrzebne więcej funkcji do obsługi wielu parametrów lub użycia zagnieżdżonych wywołań tych samych funkcji.
  • Byłoby to lepsze (bardziej wydajne) jako wbudowany TVF niż funkcja skalarna. Ma to związek z realizacją funkcji skalarnych w sercu. Istnieje wiele blogów na ten temat, patrz na przykład SQL 101: Inhibitory równoległości - skalarne funkcje zdefiniowane przez użytkownika (autor: John Kehayias .
  • Jeśli jeden z argumentów ma wartość NULL, zwraca NULL. To pasuje do tego, co leastrobi operator w Oracle i MySQL, ale różni się od Postgres. Ale to zbrojenie przeciw zeru sprawia, że ​​jest bardziej gadatliwy (jeśli wiesz, że nie będą zerowe, zwykły case when @a <= @b then @a else @b enddziałałby).

Podsumowując, może być lepiej napisać casewyciąg z instrukcji, jeśli wydajność ma znaczenie. Korzystałem nawet z generowania zagnieżdżonych caseinstrukcji po stronie klienta, gdy istnieje kilka wartości do porównania.

Ed Avis
źródło
0

Zamierzałem dodać komentarz do odpowiedzi @ ed-avis, ale nie mogłem tego zrobić z powodu braku reputacji, dlatego opublikowałem to jako rozszerzenie jego odpowiedzi.

Wyeliminowałem wadę „Irytująco musisz tworzyć osobne funkcje dla każdego typu danych”. Korzystanie z SQL_VARIANT .

Oto moja implementacja:

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;

Również ta funkcja obsługuje NULL- y jak wersja postgresql.

Dla wygody można tę funkcję dodać do DB, ale jest ona 10 razy wolniejsza niż przy użyciu wbudowanego IIF. Moje testy pokazują, że taka funkcja z dokładnym typem ( datetime ) działa tak samo jak wersja sql_variant .

PS I przeprowadzić kilka testów na zestaw danych o wartości 350k, a wydaje się, że wydajność jest taka sama, sql_variant jest odrobinę szybciej, ale wierzę, że to tylko trema.

Ale w każdym razie wersja IIF jest 10 razy szybsza !!!

Nie testowałem inline, CASE WHENale w zasadzie dla t-sql IIF jest taki sam jak wielkość liter , a iif get jest konwertowany przez optymalizator do wyrażenia wielkości liter.

Fakt, że IIF jest tłumaczony na CASE, ma również wpływ na inne aspekty zachowania tej funkcji.

WNIOSEK: Szybsze jest korzystanie z IIF, jeśli wydajność ma znaczenie, ale do prototypowania lub jeśli wymagana jest klarowność kodu i nie są wymagane duże obliczenia, pod warunkiem, że można użyć funkcji.

Bogdan Mart
źródło
LEAST () i GREATEST (), gdy są obecne w dialekcie SQL, pozwalają na porównanie w wierszu między n kolumnami; Wziąłem PO za rozwiązaniem, które zapewniłoby równoważne wyniki. Odpowiedź tutaj (wraz z kilkoma innymi) obsługuje tylko 2.
Dodecaphone