Existe uma função Max no SQL Server que leva dois valores como Math.max em .NET?

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

  •  02-07-2019
  •  | 
  •  

Pergunta

Eu quero escrever uma consulta como esta:

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

Mas não é assim que a função funciona MAX, certo? É uma função de agregação por isso espera que um único parâmetro e, em seguida, retorna o MAX de todas as linhas.

Alguém sabe como fazer isso do meu jeito?

Foi útil?

Solução

Você precisaria fazer uma User-Defined Function se você queria ter sintaxe semelhante ao seu exemplo, mas você pode fazer o que você quer fazer, inline, com bastante facilidade com uma declaração CASE, como os outros disseram.

O UDF poderia ser algo como isto:

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

... e você o chamaria assim ...

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

Outras dicas

Se você estiver usando SQL Server 2008 (ou superior), então esta é a melhor solução:

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

Todo o crédito e os votos devem ir para a resposta de Sven a uma pergunta relacionada, "SQL MAX de várias colunas?"
eu digo que é o " melhor resposta ", porque:

  1. Não requer complicar o seu código com a união de, PIVOT de, statments CASE louco longo do UNPIVOT, UDF de, e.
  2. Não é atormentado com o problema de lidar com valores nulos, ele manipula-los muito bem.
  3. É fácil trocar o "MAX" com "MIN", "AVG", ou "SUM". Você pode usar qualquer função de agregação para encontrar o agregado ao longo de muitas colunas diferentes.
  4. Você não está limitado aos nomes que eu usei (ou seja, "AllPrices" e "Preço"). Você pode escolher seus próprios nomes para torná-lo mais fácil de ler e entender para o próximo cara.
  5. Você pode encontrar vários agregados usando SQL Server 2008 derived_tables como assim :
    select max (a), MAX (b) a partir de (VALORES (1, 2), (3, 4), (5, 6), (7, 8), (9, 10)) AS AMinhaTabela ( a, b)

Pode ser feito em uma única linha:

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

Editar:. Se você está lidando com um número muito grande você terá que converter as variáveis ??de valor em bigint, a fim de evitar um excesso de número inteiro

Eu não penso assim. Eu queria isso outro dia. O mais próximo que eu consegui foi:

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

Por que não tentar IIF função (requer o SQL Server 2012 e posterior)

IIF(a>b, a, b)

É isso.

(Dica:. Ter cuidado com qualquer um seria null, já que o resultado de a>b será falso sempre quer é nulo Então b será o resultado, neste caso)

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

As outras respostas são bons, mas se você tem que se preocupar em ter valores NULL, você pode querer esta 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

Sub As consultas podem acessar as colunas da consulta externa de modo que você pode usar esta abordagem para agregados uso, tais como MAX em colunas. (Provavelmente mais útil quando existe um número maior de colunas envolvidas embora)

;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 introduziu IIF :

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

Manipulação nulos é recomendada quando o IIF, porque um NULL em ambos os lados de sua boolean_expression fará IIF para retornar o false_value (em oposição a NULL).

Eu iria com a solução fornecida por kcrumley Apenas modificá-lo ligeiramente para NULLs punho

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

Editar Modificado após comentário de Mark . Como ele apontou corretamente em 3 valorizado lógica x> NULL ou x

É tão simples como isto:

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;

Opa, eu só colocaram um dupe desta questão ...

A resposta é, não há construído em função de como Maior da Oracle, mas você pode conseguir um resultado semelhante para 2 colunas com uma UDF, nota, o uso de sql_variant é muito importante aqui.

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

Postado esta resposta:

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

Aqui está um exemplo de caso que deve lidar com valores nulos e vai trabalhar com versões mais antigas do MSSQL. Isto é baseado na função inline em um um dos exemplos populares:

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

Eu provavelmente não iria fazê-lo desta maneira, porque é menos eficiente do que as construções caso já mencionado - a não ser, talvez, você tivesse cobrindo índices para ambas as consultas. De qualquer maneira, é uma técnica útil para problemas semelhantes:

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

Aqui está uma versão IIF com manipulação NULL (baseado em da resposta de Xin):

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

A lógica é a seguinte, se qualquer um dos valores é NULL, devolver o que não é NULL (se ambos forem NULL, um NULL é retornado). Caso contrário, retornará o maior.

O mesmo pode ser feito para 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

Você pode fazer algo como isto:

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

Para a resposta acima sobre grandes números, você poderia fazer a multiplicação antes da adição / subtração. É um pouco mais volumoso, mas não requer elenco. (Eu não posso falar para a velocidade, mas eu suponho que ainda é muito rápido)

SELECT 0.5 * ((@ val1 + @ val2) + ABS (@ val1 - @ val2))

Alterações

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

pelo menos uma alternativa se você quer evitar casting.

Na sua forma mais simples ...

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

Para o SQL Server 2012:

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

No SQL Server 2012 ou superior, você pode usar uma combinação de IIF e ISNULL (ou COALESCE) para obter o máximo de 2 valores.
Mesmo quando um deles é NULL.

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

Ou se você quer que ele retornar 0 quando ambos estão NULL

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

Exemplo trecho:

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

Resultado:

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

Aqui está a resposta de @ Scott Langham com manuseio simples 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]

Em Presto você poderia usar uso

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;

Expandindo resposta de Xin e assumindo o tipo de valor de comparação é INT, esta abordagem funciona também:

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

Este é um teste completo com valores de exemplo:

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top