Abrufen des Minimums von zwei Werten in SQL

179

Ich habe zwei Variablen, eine heißt PaidThisMonthund die andere heißt OwedPast. Sie sind beide Ergebnisse einiger Unterabfragen in SQL. Wie kann ich den kleineren der beiden auswählen und ihn als Wert mit dem Titel zurückgeben PaidForPast?

Die MINFunktion arbeitet mit Spalten, nicht mit Variablen.

Malfist
quelle
1
Wenn Sie Postgres oder MySQL verwenden, fahren Sie mit der Antwort von @ Gil_Margolin fort.
Noumenon

Antworten:

126

Anwendungsfall:

   Select Case When @PaidThisMonth < @OwedPast 
               Then @PaidThisMonth Else @OwedPast End PaidForPast

Als Inline-Tabelle bewertet UDF

CREATE FUNCTION Minimum
(@Param1 Integer, @Param2 Integer)
Returns Table As
Return(Select Case When @Param1 < @Param2 
                   Then @Param1 Else @Param2 End MinValue)

Verwendung:

Select MinValue as PaidforPast 
From dbo.Minimum(@PaidThisMonth, @OwedPast)

ADDENDUM: Dies ist wahrscheinlich am besten geeignet, wenn nur zwei mögliche Werte angesprochen werden. Wenn mehr als zwei vorhanden sind, sollten Sie die Antwort von Craig mithilfe der Values-Klausel berücksichtigen .

Charles Bretana
quelle
besser verständliche Syntax: return (wählen Sie minValue = case, wenn @@ param1 <@@ param2 dann @@ param1 else @@ param2 end). Ok, das kann nicht normalisiert werden, ich weiß es nicht. Aber es ist viel verständlicher und sollte normalisiert werden.
Softlion
1
Ein weiterer Grund, die unten stehende Antwort von @ Craig zu bevorzugen, ist die Nullbehandlung. Wenn die zu vergleichenden Werte nullwertfähig sind und einer der zu vergleichenden Werte null ist, gibt der angezeigte Schaltfall je nach Reihenfolge des WHEN-Tests möglicherweise null oder den Wert zurück (es sei denn, Sie fügen die Verwendung von ISNULL hinzu). Der Ansatz von Craig wird immer die Auswahl des Nicht-Null-Werts bevorzugen, was mir zumindest in meinem aktuellen Anwendungsfall beim Vergleich von Null-Daten korrekter erscheint.
Nij
146

SQL Server 2012 und 2014 unterstützen die IIF-Funktion (cont, true, false). Somit können Sie es für eine minimale Auswahl wie verwenden

SELECT IIF(first>second, second, first) the_minimal FROM table

Während IIF nur eine Abkürzung zum Schreiben ist CASE...WHEN...ELSE, ist es einfacher zu schreiben.

Mert Gülsoy
quelle
8
IIFist nur ein syntaktischer Zucker für CASE...WHEN...ELSE.
Salman A
55
Möglicherweise ja. Aber einfacher zu schreiben.
Mert Gülsoy
1
@ MertGülsoy Und einfacher zu lesen, was gleich nach der Richtigkeit ganz oben auf der Prioritätenliste aller stehen sollte.
Daniel
118

Die Lösungen mit CASE, IIF und UDF sind angemessen, aber unpraktisch, wenn das Problem mit mehr als 2 Vergleichswerten auf den allgemeinen Fall ausgedehnt wird. Die verallgemeinerte Lösung in SQL Server 2008+ verwendet eine seltsame Anwendung der VALUES-Klausel:

SELECT
PaidForPast=(SELECT MIN(x) FROM (VALUES (PaidThisMonth),(OwedPast)) AS value(x))

Gutschrift aufgrund dieser Website: http://sqlblog.com/blogs/jamie_thomson/archive/2012/01/20/use-values-clause-to-get-the-maximum-value-from-some-columns-sql- server-t-sql.aspx

Craig
quelle
12
Dies ist die beste Antwort
FindOutIslamNow
wenn Sie die min ungleich Null wollen:MIN(x*(case x when 0 then null else 1 end))
mpag
Nur dass MartinC vier Jahre zuvor dieselbe Antwort gegeben und sie tatsächlich mit mehr als zwei Werten gezeigt hat ...
Auspex
4
Auspex, die Antwort von MartinC hat nichts damit zu tun. Diese Antwort verwendet keine Gewerkschaften.
Craig
29

Ich hatte gerade eine Situation, in der ich maximal 4 komplexe Auswahlen innerhalb eines Updates finden musste. Mit diesem Ansatz können Sie so viele haben, wie Sie möchten!

Sie können die Zahlen auch durch zusätzliche Auswahlen ersetzen

select max(x)
 from (
 select 1 as 'x' union
 select 4 as 'x' union
 select 3 as 'x' union
 select 2 as 'x' 
 ) a

Komplexere Verwendung

 @answer = select Max(x)
           from (
                select @NumberA as 'x' union
                select @NumberB as 'x' union
                select @NumberC as 'x' union
                select (
                       Select Max(score) from TopScores
                       ) as 'x' 
     ) a

Ich bin sicher, dass eine UDF eine bessere Leistung hat.

MartinC
quelle
Ich mag das am meisten, da es einfaches SQL ist. Darüber hinaus sind UDFs nicht unbedingt schneller. Bei den meisten Spaltenspeichern kann jedes Attribut (ich gehe davon aus, dass Sie auch nach den Attributen filtern) parallel berechnet werden, und nur der qualifizierende Satz wird zusammengefasst. Gewerkschaften sind also an sich nicht langsam.
Bouncner
einfach und super.
Ashleedawg
21

Für MySQL oder PostgreSQL 9.3+ ist es besser, die Funktionen LEASTund zu GREATESTverwenden.

SELECT GREATEST(A.date0, B.date0) AS date0, 
       LEAST(A.date1, B.date1, B.date2) AS date1
FROM A, B
WHERE B.x = A.x

Mit:

  • GREATEST(value [, ...]): Gibt den größten zurück Argument (maximaler Wert) aus den angegebenen Werten zurück
  • LEAST(value [, ...])Gibt das kleinste (minimalwertige) Argument aus den angegebenen Werten zurück

Dokumentationslinks:

Gil Margolin
quelle
Dies funktioniert auch in PostgreSQL (und genau das habe ich gesucht :) Siehe: postgresql.org/docs/9.5/static/functions-conditional.html
Albert Vaca Cintora
1
Dies ist bei weitem die beste Antwort.
Roberto Rodriguez
2
@RobertoRodriguez Es wäre am besten, wenn die Frage MySQL oder PostgreSQL als Teil der Frage markiert hätte. Die Frage betraf speziell tsql, daher hilft diese Antwort überhaupt nicht.
Jmaurier
Dies ist keine Antwort für MSSQL
Mujah Maskey
13

Hier ist ein Trick, wenn Sie das Maximum berechnen möchten (Feld, 0):

SELECT (ABS(field) + field)/2 FROM Table

Gibt 0 zurück, wenn dies fieldnegativ ist. Andernfalls wird zurückgegeben field.

mathematix
quelle
3
Um das Minimum (@a, @b) zu berechnen, könnten Sie SELECT @a - ( ABS(@a-@b) + (@a-@b) ) / 2
Folgendes
1
und vergessen Sie nicht über
Typüberlauf
Ist dies vom Standpunkt der Gleitkommapräzision aus sicher? Ist es sicher, dass das Ergebnis niemals nahe Null sein wird, sondern negativ?
Zuraff
6

Verwenden Sie eine CASE-Anweisung.

Beispiel B auf dieser Seite sollte genau dem entsprechen, was Sie versuchen:
http://msdn.microsoft.com/en-us/library/ms181765.aspx

Hier ist der Code von der Seite:

USE AdventureWorks;
GO
SELECT   ProductNumber, Name, 'Price Range' = 
      CASE 
         WHEN ListPrice =  0 THEN 'Mfg item - not for resale'
         WHEN ListPrice < 50 THEN 'Under $50'
         WHEN ListPrice >= 50 and ListPrice < 250 THEN 'Under $250'
         WHEN ListPrice >= 250 and ListPrice < 1000 THEN 'Under $1000'
         ELSE 'Over $1000'
      END
FROM Production.Product
ORDER BY ProductNumber ;
GO
Mike Cole
quelle
2

Verwenden Sie eine temporäre Tabelle, um den Wertebereich einzufügen, und wählen Sie dann das Min / Max der temporären Tabelle aus einer gespeicherten Prozedur oder UDF aus. Dies ist ein grundlegendes Konstrukt. Sie können es also nach Bedarf überarbeiten.

Beispielsweise:

CREATE PROCEDURE GetMinSpeed() AS
BEGIN

    CREATE TABLE #speed (Driver NVARCHAR(10), SPEED INT);
    '
    ' Insert any number of data you need to sort and pull from
    '
    INSERT INTO #speed (N'Petty', 165)
    INSERT INTO #speed (N'Earnhardt', 172)
    INSERT INTO #speed (N'Patrick', 174)

    SELECT MIN(SPEED) FROM #speed

    DROP TABLE #speed

END
user1970604
quelle
2

Dies funktioniert für bis zu 5 Daten und behandelt Nullen. Ich konnte es einfach nicht als Inline-Funktion zum Laufen bringen.

CREATE FUNCTION dbo.MinDate(@Date1 datetime = Null,
                            @Date2 datetime = Null,
                            @Date3 datetime = Null,
                            @Date4 datetime = Null,
                            @Date5 datetime = Null)
RETURNS Datetime AS
BEGIN
--USAGE select dbo.MinDate('20120405',null,null,'20110305',null)
DECLARE @Output datetime;

WITH Datelist_CTE(DT)
AS (
        SELECT @Date1 AS DT WHERE @Date1 is not NULL UNION
        SELECT @Date2 AS DT WHERE @Date2 is not NULL UNION
        SELECT @Date3 AS DT WHERE @Date3 is not NULL UNION
        SELECT @Date4 AS DT WHERE @Date4 is not NULL UNION
        SELECT @Date5 AS DT WHERE @Date5 is not NULL
   )
Select @Output=Min(DT) FROM Datelist_CTE

RETURN @Output
END
Lawrence
quelle
Ich habe gerade festgestellt, dass Sie die WHERE-Klauseln nicht benötigen, da MIN ohnehin Nullen entfernt.
Lawrence
2

Aufbauend auf der brillanten Logik / dem Code von Mathematix und Scottyc reiche ich ein:

DECLARE @a INT, @b INT, @c INT = 0

WHILE @c < 100
    BEGIN
        SET @c += 1
        SET @a = ROUND(RAND()*100,0)-50
        SET @b = ROUND(RAND()*100,0)-50
        SELECT @a AS a, @b AS b,
            @a - ( ABS(@a-@b) + (@a-@b) ) / 2 AS MINab,
            @a + ( ABS(@b-@a) + (@b-@a) ) / 2 AS MAXab,
            CASE WHEN (@a <= @b AND @a = @a - ( ABS(@a-@b) + (@a-@b) ) / 2)
            OR (@a >= @b AND @a = @a + ( ABS(@b-@a) + (@b-@a) ) / 2)
            THEN 'Success' ELSE 'Failure' END AS Status
    END

Obwohl der Sprung von der MIN-Funktion von Scottyc zur MAX-Funktion für mich offensichtlich gewesen sein sollte, war dies nicht der Fall. Deshalb habe ich ihn gelöst und hier eingefügt: SELECT @a + (ABS (@ b- @ a) + ( @ b- @ a)) / 2. Die zufällig generierten Zahlen sind zwar kein Beweis, sollten aber zumindest Skeptiker davon überzeugen, dass beide Formeln korrekt sind.

DaveX
quelle