Есть ли функция Max в SQL Server, которая принимает два значения, такие как Math.Макс in.NET?
Я хочу написать такой запрос:
SELECT o.OrderId, MAX(o.NegotiatedPrice, o.SuggestedPrice)
FROM Order o
но это не как MAX
функция работает, верно? Это агрегатная функция, поэтому она ожидает один параметр, а затем возвращает максимум всех строк.
кто-нибудь знает, как это сделать по-моему?
25 ответов
вам нужно сделать User-Defined Function
Если вы хотите иметь синтаксис, подобный вашему примеру, но могли бы вы сделать то, что хотите сделать, встроенный, довольно легко с CASE
заявление, как сказали другие.
на UDF
может быть что-то вроде этого:
create function dbo.InlineMax(@val1 int, @val2 int)
returns int
as
begin
if @val1 > @val2
return @val1
return isnull(@val2,@val1)
end
... и вы бы назвали это так ...
SELECT o.OrderId, dbo.InlineMax(o.NegotiatedPrice, o.SuggestedPrice)
FROM Order o
Если вы используете SQL Server 2008 (или выше), то это лучшее решение:
SELECT o.OrderId,
(SELECT MAX(Price)
FROM (VALUES (o.NegotiatedPrice),(o.SuggestedPrice)) AS AllPrices(Price))
FROM Order o
все кредиты и голоса должны идти в ответ Свена на связанный вопрос: "SQL MAX из нескольких столбцов?"
Я говорю, что это "лучший ответ" потому что:
- это не требует усложнения вашего кода с помощью UNION's, PIVOT's, UNPIVOT's, UDF и crazy - Long CASE statments.
- он не страдает от проблемы управляя нулями, он справляется с ними просто отлично.
- легко поменять "MAX" на "MIN", "AVG"или " SUM". Вы можете использовать любую агрегатную функцию для поиска агрегата по многим различным столбцам.
- вы не ограничены именами, которые я использовал (т. е. "AllPrices" и "Price"). Вы можете выбрать свои собственные имена, чтобы облегчить чтение и понимание для следующего парня.
- вы можете найти несколько агрегатов с помощью SQL Server 2008 в derived_tables как Итак:
выберите MAX (a), MAX(b) из (значения (1, 2), (3, 4), (5, 6), (7, 8), (9, 10) ) как MyTable (a, b)
можно сделать в одну строку:
-- the following expression calculates ==> max(@val1, @val2)
SELECT 0.5 * ((@val1 + @val2) + ABS(@val1 - @val2))
Edit: если вы имеете дело с очень большими числами, вам придется преобразовать переменные значения в bigint, чтобы избежать переполнения целого числа.
Я так не думаю. Я хотел этого на днях. Самое близкое, что я получил, было:
SELECT
o.OrderId,
CASE WHEN o.NegotiatedPrice > o.SuggestedPrice THEN o.NegotiatedPrice
ELSE o.SuggestedPrice
END
FROM Order o
почему бы не попробовать IIF функция (требуется SQL Server 2012 и более поздних версий)
IIF(a>b, a, b)
вот и все.
другие ответы хороши, но если вам нужно беспокоиться о наличии нулевых значений, вам может понадобиться этот вариант:
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
подзапросы могут получить доступ к столбцам из внешнего запроса, поэтому вы можете использовать этот подход использовать агрегаты, такие как MAX
по столбцам. (Вероятно, более полезно, когда есть большее количество столбцов, участвующих)
;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 введен IIF
:
SELECT
o.OrderId,
IIF( ISNULL( o.NegotiatedPrice, 0 ) > ISNULL( o.SuggestedPrice, 0 ),
o.NegotiatedPrice,
o.SuggestedPrice
)
FROM
Order o
обработка нулей рекомендуется при использовании IIF
, потому что a NULL
по обе стороны от вашего boolean_expression
вызывает IIF
вернуть false_value
(вместо NULL
).
Я бы пошел с решением, предоставленным kcrumley Просто измените его немного, чтобы обрабатывать NULLs
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
редактировать Изменено после комментария от Марк. Как он правильно указал в 3-значной логике x > NULL или x
Это так просто:
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;
Ой, я только что опубликовал обман этого вопроса...
ответ: нет встроенная функция как Оракул, но вы можете достичь аналогичного результата для 2 столбцов с UDF, обратите внимание, что использование sql_variant здесь очень важно.
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
выложил этот ответ:
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
вот пример случая, который должен обрабатывать нули и будет работать со старыми версиями MSSQL. Это основано на встроенной функции в одном из популярных примеров:
case
when a >= b then a
else isnull(b,a)
end
Я, вероятно, не сделал бы этого таким образом, поскольку это менее эффективно, чем уже упомянутые конструкции CASE - если, возможно, у вас не было индексов покрытия для обоих запросов. В любом случае, это полезная техника для подобных проблем:
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
вот версия IIF с обработкой NULL (на основе ответа Xin):
IIF(a IS NULL OR b IS NULL, ISNULL(a,b), IIF(a > b, a, b))
логика выглядит следующим образом: если любое из значений равно NULL, верните значение, которое не равно NULL (если оба значения равны NULL, возвращается NULL). В противном случае верните большую.
то же самое можно сделать за минут.
IIF(a IS NULL OR b IS NULL, ISNULL(a,b), IIF(a < b, a, b))
вы можете сделать что-то вроде этого:
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
для ответа выше относительно больших чисел вы можете сделать умножение перед сложением / вычитанием. Он немного громоздкий, но не требует гипса. (Я не могу говорить о скорости, но я предполагаю, что это все еще довольно быстро)
выберите 0.5 * ((@val1 + @val2) + ABS (@val1 - @ val2))
изменения
выберите @val1*0.5+@val2*0.5 + ABS (@val1*0.5 - @ val2*0.5)
по крайней мере альтернатива, если вы хотите избежать литья.
в простейшей форме...
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
для SQL Server 2012:
SELECT
o.OrderId,
IIF( o.NegotiatedPrice >= o.SuggestedPrice,
o.NegotiatedPrice,
ISNULL(o.SuggestedPrice, o.NegiatedPrice)
)
FROM
Order o
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
вот ответ @Scott Langham с простой обработкой 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]