Esiste una funzione Max in SQL Server che accetta due valori come Math.Max ​​in .NET?

StackOverflow https://stackoverflow.com/questions/124417

  •  02-07-2019
  •  | 
  •  

Domanda

Voglio scrivere una query come questa:

SELECT o.OrderId, MAX(o.NegotiatedPrice, o.SuggestedPrice)
FROM Order o

Ma non è così che funziona la funzione MAX, giusto? È una funzione aggregata, quindi si aspetta un singolo parametro e quindi restituisce il MAX di tutte le righe.

Qualcuno sa come fare a modo mio?

È stato utile?

Soluzione

Dovresti creare un User-Defined Function se vuoi avere una sintassi simile al tuo esempio, ma potresti fare quello che vuoi fare, in linea, abbastanza facilmente con un'istruzione CASE, come hanno detto gli altri .

Il UDF potrebbe essere qualcosa del genere:

create function dbo.InlineMax(@val1 int, @val2 int)
returns int
as
begin
  if @val1 > @val2
    return @val1
  return isnull(@val2,@val1)
end

... e lo chiameresti così ...

SELECT o.OrderId, dbo.InlineMax(o.NegotiatedPrice, o.SuggestedPrice) 
FROM Order o

Altri suggerimenti

Se si utilizza SQL Server 2008 (o versioni successive), questa è la soluzione migliore:

SELECT o.OrderId,
       (SELECT MAX(Price)
        FROM (VALUES (o.NegotiatedPrice),(o.SuggestedPrice)) AS AllPrices(Price))
FROM Order o

Tutti i crediti e i voti dovrebbero andare a La risposta di Sven a una domanda correlata, " SQL MAX di più colonne? quot;
Dico che è la " migliore risposta " perché:

  1. Non richiede complicazioni del codice con UNION, PIVOT, Dichiarazioni UNPIVOT, UDF e CASE pazzesche.
  2. Non è afflitto dal problema di gestire i null, li gestisce bene.
  3. È facile sostituire il " MAX " con " MIN " ;, " AVG " ;, oppure " SUM " ;. Puoi utilizzare qualsiasi funzione di aggregazione per trovare l'aggregazione su molte colonne diverse.
  4. Non sei limitato ai nomi che ho usato (ovvero " AllPrices " e " Price "). Puoi scegliere i tuoi nomi per rendere più semplice la lettura e la comprensione per il prossimo ragazzo.
  5. Puoi trovare più aggregati utilizzando deriv_tables di SQL Server 2008 :
    SELEZIONA MAX (a), MAX (b) DA (VALORI (1, 2), (3, 4), (5, 6), (7, 8), (9, 10)) AS MyTable ( a, b)

Può essere fatto in una riga:

-- the following expression calculates ==> max(@val1, @val2)
SELECT 0.5 * ((@val1 + @val2) + ABS(@val1 - @val2)) 

Modifica: Se hai a che fare con numeri molto grandi dovrai convertire le variabili di valore in bigint per evitare un overflow di numeri interi.

Non credo. L'ho voluto l'altro giorno. Il più vicino che ho avuto è stato:

SELECT
  o.OrderId,
  CASE WHEN o.NegotiatedPrice > o.SuggestedPrice THEN o.NegotiatedPrice 
     ELSE o.SuggestedPrice
  END
FROM Order o

Perché non provare la funzione IIF (richiede SQL Server 2012 e versioni successive)

IIF(a>b, a, b)

Questo è tutto.

(Suggerimento: fare attenzione a entrambi sarebbe null, poiché il risultato di a>b sarà falso ogni volta che uno dei due sarà nullo. Quindi b sarà il risultato in questo caso)

DECLARE @MAX INT
@MAX = (SELECT MAX(VALUE) 
               FROM (SELECT 1 AS VALUE UNION 
                     SELECT 2 AS VALUE) AS T1)

Le altre risposte sono buone, ma se devi preoccuparti di avere valori NULL, potresti voler questa variante:

SELECT o.OrderId, 
   CASE WHEN ISNULL(o.NegotiatedPrice, o.SuggestedPrice) > ISNULL(o.SuggestedPrice, o.NegotiatedPrice)
        THEN ISNULL(o.NegotiatedPrice, o.SuggestedPrice)
        ELSE ISNULL(o.SuggestedPrice, o.NegotiatedPrice)
   END
FROM Order o

Le query secondarie possono accedere alle colonne dalla query esterna in modo da poter utilizzare questo approccio per utilizzare aggregati come MAX su più colonne. (Probabilmente più utile quando c'è un maggior numero di colonne coinvolte)

;WITH [Order] AS
(
SELECT 1 AS OrderId, 100 AS NegotiatedPrice, 110 AS SuggestedPrice UNION ALL
SELECT 2 AS OrderId, 1000 AS NegotiatedPrice, 50 AS SuggestedPrice
)
SELECT
       o.OrderId, 
       (SELECT MAX(price)FROM 
           (SELECT o.NegotiatedPrice AS price 
            UNION ALL SELECT o.SuggestedPrice) d) 
        AS MaxPrice 
FROM  [Order]  o

SQL Server 2012 ha introdotto IIF :

SELECT 
    o.OrderId, 
    IIF( ISNULL( o.NegotiatedPrice, 0 ) > ISNULL( o.SuggestedPrice, 0 ),
         o.NegotiatedPrice, 
         o.SuggestedPrice 
    )
FROM 
    Order o

Si consiglia di gestire i NULL quando si utilizza NULL, poiché un boolean_expression su entrambi i lati del false_value farà sì che <=> restituisca <=> (anziché <=>).

Andrei con la soluzione fornita da kcrumley Modificalo leggermente per gestire i NULL

create function dbo.HigherArgumentOrNull(@val1 int, @val2 int)
returns int
as
begin
  if @val1 >= @val2
    return @val1
  if @val1 < @val2
    return @val2

 return NULL
end

Modifica Modificato dopo il commento di Mark . Come ha giustamente sottolineato in 3 valori logici x & Gt; NULL o x & Lt; NULL dovrebbe sempre restituire NULL. In altre parole, risultato sconosciuto.

È semplice come questo:

CREATE FUNCTION InlineMax
(
    @p1 sql_variant,
    @p2 sql_variant
)  RETURNS sql_variant
AS
BEGIN
    RETURN CASE 
        WHEN @p1 IS NULL AND @p2 IS NOT NULL THEN @p2 
        WHEN @p2 IS NULL AND @p1 IS NOT NULL THEN @p1
        WHEN @p1 > @p2 THEN @p1
        ELSE @p2 END
END;

Oops, ho appena pubblicato un dupe di questa domanda ...

La risposta è che non esiste una funzione integrata come Oracle's Greatest , ma puoi ottenere un risultato simile per 2 colonne con un UDF, nota, l'uso di sql_variant è abbastanza importante qui.

create table #t (a int, b int) 

insert #t
select 1,2 union all 
select 3,4 union all
select 5,2

-- option 1 - A case statement
select case when a > b then a else b end
from #t

-- option 2 - A union statement 
select a from #t where a >= b 
union all 
select b from #t where b > a 

-- option 3 - A udf
create function dbo.GREATEST
( 
    @a as sql_variant,
    @b as sql_variant
)
returns sql_variant
begin   
    declare @max sql_variant 
    if @a is null or @b is null return null
    if @b > @a return @b  
    return @a 
end


select dbo.GREATEST(a,b)
from #t

kristof

Inserita questa risposta:

create table #t (id int IDENTITY(1,1), a int, b int)
insert #t
select 1,2 union all
select 3,4 union all
select 5,2

select id, max(val)
from #t
    unpivot (val for col in (a, b)) as unpvt
group by id

Ecco un esempio di caso che dovrebbe gestire valori null e funzionerà con le versioni precedenti di MSSQL. Questo si basa sulla funzione incorporata in uno degli esempi popolari:

case
  when a >= b then a
  else isnull(b,a)
end

Probabilmente non lo farei in questo modo, poiché è meno efficiente dei già citati costrutti CASE - a meno che, forse, non avessi indici di copertura per entrambe le query. Ad ogni modo, è una tecnica utile per problemi simili:

SELECT OrderId, MAX(Price) as Price FROM (
   SELECT o.OrderId, o.NegotiatedPrice as Price FROM Order o
   UNION ALL
   SELECT o.OrderId, o.SuggestedPrice as Price FROM Order o
) as A
GROUP BY OrderId

Ecco una versione IIF con gestione NULL (basata sulla risposta di Xin):

IIF(a IS NULL OR b IS NULL, ISNULL(a,b), IIF(a > b, a, b))

La logica è la seguente, se uno dei valori è NULL, restituisce quello che non è NULL (se entrambi sono NULL, viene restituito un NULL). Altrimenti restituisci quello più grande.

Lo stesso può essere fatto per MIN.

IIF(a IS NULL OR b IS NULL, ISNULL(a,b), IIF(a < b, a, b))
SELECT o.OrderId,   
--MAX(o.NegotiatedPrice, o.SuggestedPrice)  
(SELECT MAX(v) FROM (VALUES (o.NegotiatedPrice), (o.SuggestedPrice)) AS value(v)) as ChoosenPrice  
FROM Order o

Puoi fare qualcosa del genere:

select case when o.NegotiatedPrice > o.SuggestedPrice 
then o.NegotiatedPrice
else o.SuggestedPrice
end
SELECT o.OrderID
CASE WHEN o.NegotiatedPrice > o.SuggestedPrice THEN
 o.NegotiatedPrice
ELSE
 o.SuggestedPrice
END AS Price
CREATE FUNCTION [dbo].[fnMax] (@p1 INT, @p2 INT)
RETURNS INT
AS BEGIN

    DECLARE @Result INT

    SET @p2 = COALESCE(@p2, @p1)

    SELECT
        @Result = (
                   SELECT
                    CASE WHEN @p1 > @p2 THEN @p1
                         ELSE @p2
                    END
                  )

    RETURN @Result

END

Per la risposta sopra riguardante numeri di grandi dimensioni, è possibile eseguire la moltiplicazione prima dell'addizione / sottrazione. È un po 'più ingombrante ma non richiede cast. (Non posso parlare per velocità ma presumo sia ancora piuttosto veloce)

  

SELEZIONA 0,5 * ((@ val1 + @ val2) +   ABS (@ val1 - @ val2))

Modifiche a

  

SELEZIONA @ val1 * 0,5 + @ val2 * 0,5 +   ABS (@ val1 * 0,5 - @ val2 * 0,5)

almeno un'alternativa se vuoi evitare il casting.

Nella sua forma più semplice ...

CREATE FUNCTION fnGreatestInt (@Int1 int, @Int2 int )
RETURNS int
AS
BEGIN

    IF @Int1 >= ISNULL(@Int2,@Int1)
        RETURN @Int1
    ELSE
        RETURN @Int2

    RETURN NULL --Never Hit

END

Per SQL Server 2012:

SELECT 
    o.OrderId, 
    IIF( o.NegotiatedPrice >= o.SuggestedPrice,
         o.NegotiatedPrice, 
         ISNULL(o.SuggestedPrice, o.NegiatedPrice) 
    )
FROM 
    Order o

In SQL Server 2012 o versioni successive, è possibile utilizzare una combinazione di IIF e ISNULL (o COALESCE) per ottenere un massimo di 2 valori.
Anche quando 1 di questi è NULL.

IIF(col1 >= col2, col1, ISNULL(col2, col1)) 

O se vuoi che ritorni 0 quando entrambi sono NULL

IIF(col1 >= col2, col1, COALESCE(col2, col1, 0)) 

Snippet di esempio:

-- use table variable for testing purposes
declare @Order table 
(
  OrderId int primary key identity(1,1),
  NegotiatedPrice decimal(10,2),
  SuggestedPrice decimal(10,2)
);

-- Sample data
insert into @Order (NegotiatedPrice, SuggestedPrice) values
(0, 1),
(2, 1),
(3, null),
(null, 4);

-- Query
SELECT 
     o.OrderId, o.NegotiatedPrice, o.SuggestedPrice, 
     IIF(o.NegotiatedPrice >= o.SuggestedPrice, o.NegotiatedPrice, ISNULL(o.SuggestedPrice, o.NegotiatedPrice)) AS MaxPrice
FROM @Order o

Risultato:

OrderId NegotiatedPrice SuggestedPrice  MaxPrice
1       0,00            1,00            1,00
2       2,00            1,00            2,00
3       3,00            NULL            3,00
4       NULL            4,00            4,00

Ecco la risposta di @Scott Langham con una semplice gestione NULL:

SELECT
      o.OrderId,
      CASE WHEN (o.NegotiatedPrice > o.SuggestedPrice OR o.SuggestedPrice IS NULL) 
         THEN o.NegotiatedPrice 
         ELSE o.SuggestedPrice
      END As MaxPrice
FROM Order o
select OrderId, (
    select max([Price]) from (
        select NegotiatedPrice [Price]
        union all
        select SuggestedPrice
    ) p
) from [Order]

In Presto puoi usare use

SELECT array_max(ARRAY[o.NegotiatedPrice, o.SuggestedPrice])
 -- Simple way without "functions" or "IF" or "CASE"
 -- Query to select maximum value
 SELECT o.OrderId
  ,(SELECT MAX(v)
   FROM (VALUES (o.NegotiatedPrice), (o.SuggestedPrice)) AS value(v)) AS MaxValue
  FROM Order o;

Espandendo la risposta di Xin e assumendo che il tipo di valore di confronto sia INT, anche questo approccio funziona:

SELECT IIF(ISNULL(@A, -2147483648) > ISNULL(@B, -2147483648), @A, @B)

Questo è un test completo con valori di esempio:

DECLARE @A AS INT
DECLARE @B AS INT

SELECT  @A = 2, @B = 1
SELECT  IIF(ISNULL(@A, -2147483648) > ISNULL(@B, -2147483648), @A, @B)
-- 2

SELECT  @A = 2, @B = 3
SELECT  IIF(ISNULL(@A, -2147483648) > ISNULL(@B, -2147483648), @A, @B)
-- 3

SELECT  @A = 2, @B = NULL
SELECT  IIF(ISNULL(@A, -2147483648) > ISNULL(@B, -2147483648), @A, @B)
-- 2    

SELECT  @A = NULL, @B = 1
SELECT  IIF(ISNULL(@A, -2147483648) > ISNULL(@B, -2147483648), @A, @B)
-- 1
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top