Есть ли функция Max в SQL Server, которая принимает два значения, например Math.Max в .NET?
-
02-07-2019 - |
Вопрос
Я хочу написать запрос, подобный этому:
SELECT o.OrderId, MAX(o.NegotiatedPrice, o.SuggestedPrice)
FROM Order o
Но это не то, как MAX
функция работает, верно?Это агрегатная функция, поэтому она ожидает один параметр, а затем возвращает МАКСИМАЛЬНОЕ количество всех строк.
Кто-нибудь знает, как сделать это по-моему?
Решение
Вам нужно было бы сделать 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
Все заслуги и голоса должны достаться Ответ Sven на связанный с этим вопрос: "SQL MAX из нескольких столбцов?"
Я говорю, что это "лучший ответ" потому что:
- Для этого не требуется усложнять ваш код статьями UNION, PIVOT, UNPIVOT, UDF и безумно длинными регистрами.
- Он не сталкивается с проблемой обработки нулей, он справляется с ними просто отлично.
- Легко заменить "MAX" на "MIN", "AVG" или "SUM".Вы можете использовать любую агрегатную функцию для поиска агрегата по множеству различных столбцов.
- Вы не ограничены именами, которые я использовал (т.е."Все цены" и "Прайс").Вы можете выбрать свои собственные имена, чтобы их было легче прочесть и понять следующему парню.
- Вы можете найти несколько агрегированных данных, используя SQL Server 2008 производные таблицы вот так:
ВЫБЕРИТЕ 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))
Редактировать: Если вы имеете дело с очень большими числами, вам придется преобразовать переменные значения в 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)
Вот и все.
(Подсказка:будьте осторожны с любым из них. null
, поскольку в результате a>b
будет иметь значение false всякий раз, когда любое из них равно null.Итак b
будет ли результат в этом случае)
DECLARE @MAX INT
@MAX = (SELECT MAX(VALUE)
FROM (SELECT 1 AS VALUE UNION
SELECT 2 AS VALUE) AS T1)
Другие ответы хороши, но если вам нужно беспокоиться о наличии нулевых значений, вам может понадобиться этот вариант:
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
).
Я бы предпочел решение, предоставленное крамли Просто слегка измените его, чтобы обрабатывать нули
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 < NULL всегда должен возвращать значение NULL.Другими словами, неизвестный результат.
Это так просто, как это:
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
Вот пример, который должен обрабатывать null и будет работать со старыми версиями 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).В противном случае верните больший.
То же самое можно сделать для 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
Вы можете сделать что-то вроде этого:
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
В SQL Server 2012 или более поздней версии вы можете использовать комбинацию IIF
и ISNULL
(или COALESCE
), чтобы получить максимум 2 значения.
Даже если 1 из них равен НУЛЮ.
IIF(col1 >= col2, col1, ISNULL(col2, col1))
Или, если вы хотите, чтобы он возвращал 0, когда оба значения равны НУЛЮ
IIF(col1 >= col2, col1, COALESCE(col2, col1, 0))
Пример фрагмента текста:
-- 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
Результат:
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
Вот ответ @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]
В Presto вы могли бы использовать 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;
Расширяя ответ Xin и предполагая, что тип значения сравнения равен INT, этот подход тоже работает:
SELECT IIF(ISNULL(@A, -2147483648) > ISNULL(@B, -2147483648), @A, @B)
Это полный тест с примерами значений:
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