SQL Server supporta GREATEST e LEAST, se non qual è la soluzione alternativa comune?


15

Rivedere questa domanda sembra che ci sia molto lavoro che non dovrebbe essere necessario. Stanno cercando di estendere un intervallo con una data. In altri database, useresti greateste least..

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

Quando provo a usare questi però, ottengo

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

Ciò coprirebbe l'estensione in entrambe le direzioni.

Ai fini della domanda, dovresti comunque effettuare la sostituzione esclusiva della gamma.

Mi sto solo chiedendo come gli utenti di SQL Server implementano modelli di query per simulare leaste greatestfunzionalità.

Annulli le condizioni in CASEdichiarazioni o esiste un'estensione, un componente aggiuntivo di terze parti o una licenza di Microsoft che abilita questa funzionalità?


È incredibile che MSSQL non abbia un'implementazione per LEAST/ GREATESTfunzioni - quasi tutti i concorrenti RDBMS hanno almeno equivalenti. L'unica eccezione che ho trovato è Sybase, ma a questo punto è stato interrotto per molti anni.
bsplosion,

Risposte:


32

Un metodo comune consiste nell'utilizzare la VALUESclausola e CROSS APPLYle due colonne aliasate come una singola colonna, quindi ottenere il MINe MAXdi ciascuno.

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

Esistono altri modi per scriverlo, ad esempio usando 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);

Tuttavia, i piani di query risultanti sembrano essere gli stessi.


12

È inoltre possibile inserire i valori in linea in una sottoquery. Come questo:

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

Sarebbe un buon inizio -

CASE WHEN A > B THEN A ELSE B END

È un buon suggerimento, ma è stato menzionato nella domanda con "srotolare la condizione nelle dichiarazioni CASE"
Evan Carroll,

3

Almeno equivalente:

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

MASSIMO equivalente:

IIF(@a > @b, @a, @b)

3
Come si fa per tre o più valori, ad esempio least(5,6,7,8,9)?
a_horse_with_no_name

@a_horse_with_no_name Usa IIF nidificati
Elnur

Questo approccio diventerebbe rapidamente difficile da leggere e verificare ... Come funziona in termini di prestazioni?
Dodecaphone

0

Creo funzioni definite dall'utente, ad es

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

Sebbene possa funzionare in casi semplici, questo approccio presenta diversi problemi:

  • È necessario creare fastidiosamente funzioni separate per ciascun tipo di dati.
  • Gestisce solo 2 parametri, quindi potrebbero essere necessarie più funzioni per gestire molti parametri o utilizzare chiamate nidificate delle stesse funzioni.
  • Sarebbe meglio (più efficiente) come un TVF in linea piuttosto che una funzione scalare. Ciò ha a che fare con l'implementazione delle funzioni scalari nel cuore. Ci sono molti blog a riguardo, vedi ad esempio SQL 101: Inibitori del parallelismo - Funzioni definite dall'utente scalare (di John Kehayias .
  • Se uno degli argomenti è null, restituisce null. Ciò corrisponde a ciò che l' leastoperatore fa in Oracle e MySQL, ma differisce da Postgres. Ma questa armatura contro null lo rende più dettagliato (se sai che non saranno null, una pianura case when @a <= @b then @a else @b endfunzionerebbe).

Tutto sommato potrebbe essere meglio scrivere a lungo la casedichiarazione se le prestazioni sono importanti. Ho persino fatto ricorso alla generazione di caseistruzioni nidificate sul lato client quando ci sono diversi valori da confrontare.


0

Avevo intenzione di aggiungere un commento alla risposta di @ ed-avis, ma non sono stato in grado di farlo, a causa della mancanza di reputazione, quindi pubblicandolo come estensione della sua risposta.

Ho eliminato lo svantaggio di "In modo fastidioso devi creare funzioni separate per ogni tipo di dati". Utilizzando SQL_VARIANT .

Ecco la mia implementazione:

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;

Anche questa funzione gestisce NULL come la versione postgresql.

Questa funzione può essere aggiunta al DB per comodità, ma è 10 volte più lenta rispetto all'utilizzo integrato IIF. I miei test mostrano che tale funzione con il tipo esatto ( datetime ) esegue lo stesso della versione sql_variant .

PS Eseguo alcuni test su set di dati con valori di 350k e sembra che le prestazioni siano le stesse, sql_variant è leggermente più veloce, ma credo che sia solo jitter.

Ma in ogni caso la versione IIF è 10 volte più veloce !!!

Non ho testato in linea, CASE WHENma fondamentalmente per t-sql IIF è lo stesso del caso , e se mi viene convertito dall'ottimizzatore in espressione del caso.

Il fatto che l'IIF sia tradotto in CASO ha anche un impatto su altri aspetti del comportamento di questa funzione.

CONCLUSIONE: è più veloce utilizzare l' IIF se le prestazioni sono importanti, ma per la prototipazione o se è necessaria una maggiore chiarezza del codice e non sono necessari grandi calcoli, purché sia ​​possibile utilizzare la funzione.


1
Dici che "sqlvariant è un po 'più veloce" e che "La versione IIF è 10 volte più veloce". più veloce di cosa?
ypercubeᵀᴹ

La variante di SQL ver ha circa la stessa velocità della versione concreete, come fornita da un'altra risposta. Nel mio test era 80ms prima (fuori da 15sec), suppongo che solo un errore di statistica. E l'utilizzo iif(a<b, a, b) è 10 volte più veloce di qualsiasi funzione definita dall'utente.
Bogdan Mart

Per essere chiari, ho usato il mio codice con sql_variant sostituito con datetime, come seconda funzione. Dopo i test sembra che sql_variant non aggiunga alcun sovraccarico, ma le funzioni definite dall'utente sono molto più lente rispetto a quelle integrate
Bogdan Mart

Ma una di queste funzioni - inclusa IIF()- è più veloce dell'uso di CASEun'espressione? Il mio punto è che, dal momento che sei andato nei guai del test delle prestazioni, dovresti testare tutti i metodi / risposte suggeriti.
ypercubeᵀᴹ

1
@ yper-crazyhat-cubeᵀᴹ risposta aggiornata. Non lo modificherò più, volevo solo aggiungere un commento relativo a sql_variant alla risposta di ed-avis, ma a causa della mancanza di pinte ho dovuto scrivere una risposta estesa :-)
Bogdan Mart
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.