Unterstützt SQL Server GREATEST und LEAST, wenn nicht, was ist die allgemeine Problemumgehung?

15

Nach Prüfung dieser Frage scheint dies eine Menge Arbeit zu sein, die nicht benötigt werden sollte. Sie versuchen, eine Reichweite mit einem Datum zu erweitern. In anderen Datenbanken würden Sie nur greatestund verwenden least.

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

Wenn ich versuche, diese zu verwenden, bekomme ich

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

Das würde die Ausdehnung in beide Richtungen abdecken.

Für die Zwecke der Frage müssten Sie immer noch einen exklusiven Bereichsaustausch durchführen.

Ich frage mich nur, wie SQL Server-Benutzer Abfragemuster implementieren, um Nachahmung leastund greatestFunktionalität zu erzielen .

Entrollen Sie die Bedingungen in CASEAnweisungen oder gibt es eine Erweiterung, ein Add-On eines Drittanbieters oder eine Lizenz von Microsoft, die diese Funktionalität ermöglicht?

Evan Carroll
quelle
Es ist unglaublich, dass MSSQL keine Implementierung für LEAST/ GREATESTfunctions hat - fast alle RDBMS-Konkurrenten haben mindestens Äquivalente. Die einzige Ausnahme, die ich finden konnte, ist Sybase, aber das wird auch seit vielen Jahren eingestellt.
BSPLOSION

Antworten:

32

Eine gebräuchliche Methode besteht darin, die VALUESKlausel zu verwenden und CROSS APPLYdie beiden Spalten als eine einzige Spalte zu aliasieren und dann das MINund MAXvon jeder abzurufen .

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

Es gibt andere Möglichkeiten, es zu schreiben, zum Beispiel mit 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);

Die resultierenden Abfragepläne scheinen jedoch identisch zu sein.

Erik Darling
quelle
12

Sie können die Werte auch in eine Unterabfrage einfügen. So was:

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
quelle
3

Das wäre ein guter Anfang -

CASE WHEN A > B THEN A ELSE B END
Jim Gettma
quelle
Es ist ein guter Vorschlag, aber er wurde in der Frage "Ausrollen der Bedingung in CASE-Anweisungen" erwähnt
Evan Carroll,
3

MINDESTENS Äquivalent:

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

GRÖSSTES Äquivalent:

IIF(@a > @b, @a, @b)
Elnur
quelle
3
Wie macht man das für drei oder mehr Werte, zB least(5,6,7,8,9)?
a_horse_with_no_name
@a_horse_with_no_name Verwenden Sie geschachtelte IIFs
Elnur
Das Lesen und Überprüfen dieses Ansatzes würde schnell zu einer Herausforderung werden ... Wie sieht es in Bezug auf die Leistung aus?
Dodekaphon
0

Ich erstelle benutzerdefinierte Funktionen, z

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

Obwohl dies in einfachen Fällen möglich ist, gibt es bei diesem Ansatz mehrere Probleme:

  • Ärgerlicherweise müssen Sie für jeden Datentyp separate Funktionen vornehmen.
  • Es werden nur 2 Parameter verarbeitet, sodass möglicherweise mehrere Funktionen erforderlich sind, um viele Parameter zu verarbeiten oder verschachtelte Aufrufe derselben Funktionen zu verwenden.
  • Es wäre besser (effizienter) als eine Inline-TVF als eine Skalarfunktion. Das hat im Kern mit der Implementierung von Skalarfunktionen zu tun. Es gibt viele Blogs, siehe zum Beispiel SQL 101: Parallelism Inhibitors - Scalar User Defined Functions (von John Kehayias) .
  • Wenn eines der Argumente null ist, wird null zurückgegeben. Dies entspricht dem, was der leastOperator in Oracle und MySQL tut, unterscheidet sich jedoch von Postgres. Aber diese Panzerung gegen Null macht es ausführlicher (wenn Sie wissen, dass sie nicht null sind, würde eine Ebene case when @a <= @b then @a else @b endfunktionieren).

Alles in allem kann es besser sein, die caseErklärung mit der Hand aufzuschreiben, wenn es um Leistung geht. Ich habe sogar versucht, geschachtelte caseAnweisungen auf der Clientseite zu generieren, wenn mehrere Werte verglichen werden müssen.

Ed Avis
quelle
0

Ich hatte vor, @ ed-avis zu kommentieren, konnte dies aber aufgrund mangelnder Reputation nicht tun und habe dies daher als Erweiterung seiner Antwort veröffentlicht.

Ich habe den Nachteil beseitigt, dass "ärgerlicherweise für jeden Datentyp separate Funktionen erstellt werden müssen". Verwenden von SQL_VARIANT .

Hier ist meine Implementierung:

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;

Auch diese Funktion behandelt NULL- Werte wie die postgresql-Version.

Diese Funktion könnte der Einfachheit halber zu DB hinzugefügt werden, ist jedoch zehnmal langsamer als die Verwendung der integrierten Funktion IIF. Meine Tests zeigen, dass eine solche Funktion mit genauem Typ ( datetime ) dasselbe wie die Version von sql_variant ausführt .

PS Ich führe einige Tests mit einem Datensatz von 350k Werten durch und scheine, dass die Leistung gleich ist, sql_variant ist ein bisschen schneller, aber ich glaube, es ist nur Jitter.

Aber in jedem Fall ist die IIF-Version 10- mal schneller !!!

Ich habe Inline nicht getestet, CASE WHENaber im Grunde genommen ist für t-sql IIF dasselbe wie case , und wenn es vom Optimierer in case-Ausdruck konvertiert wird.

Die Tatsache, dass IIF in CASE übersetzt wird, wirkt sich auch auf andere Aspekte des Verhaltens dieser Funktion aus.

SCHLUSSFOLGERUNG: Die Verwendung von IIF ist schneller, wenn es auf die Leistung ankommt, aber für das Prototyping oder wenn mehr Code-Klarheit erforderlich ist und keine großen Berechnungen erforderlich sind, sofern die Funktion verwendet werden kann.

Bogdan Mart
quelle
1
Sie sagen, "sqlvariant ist ein bisschen schneller" und "IIF-Version ist 10x schneller". schneller als was?
ypercubeᵀᴹ
SQL-Variante Ver ist in etwa die gleiche Geschwindigkeit wie Concreete-Version, wie von einer anderen Antwort zur Verfügung gestellt. In meinem Test war es 80ms fater (von 15sec), ich nehme an, dass nur Statistikfehler. Und die Verwendung iif(a<b, a, b) ist zehnmal schneller als jede benutzerdefinierte Funktion.
Bogdan Mart
Um es klar auszudrücken, habe ich meinen Code mit sql_variant als zweite Funktion durch datetime ersetzt. Nach Tests scheint es, dass sql_variant keinen Overhead hinzufügt, aber benutzerdefinierte Funktionen sind viel langsamer als die eingebauten
Bogdan Mart
Aber ist eine dieser Funktionen - einschließlich IIF()- schneller als die Verwendung eines CASEAusdrucks? Mein Punkt ist, dass Sie, da Sie sich mit Leistungstests befasst haben, alle vorgeschlagenen Methoden / Antworten testen sollten.
ypercubeᵀᴹ
1
@ yper-crazyhat-cubeᵀᴹ aktualisierte Antwort. Ich werde es nicht mehr bearbeiten, wollte nur einen Kommentar zu sql_variant zu ed-aviss Antwort hinzufügen, musste aber wegen fehlender Pints ​​eine erweiterte Antwort schreiben :-)
Bogdan Mart