Existe uma função Max no SQL Server que leva dois valores como Math.max em .NET?
-
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?
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 ??em>", porque:
- Não requer complicar o seu código com a união de, PIVOT de, statments CASE louco longo do UNPIVOT, UDF de, e.
- Não é atormentado com o problema de lidar com valores nulos, ele manipula-los muito bem.
- É 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.
- 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.
- 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
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