Есть ли функция Max в SQL Server, которая принимает два значения, например Math.Макс in.NET?
Я хочу написать такой запрос:
SELECT o.OrderId, MAX(o.NegotiatedPrice, o.SuggestedPrice)
FROM Order o
но это не как MAX функция работает, верно? Это агрегатная функция, поэтому она ожидает один параметр, а затем возвращает максимум всех строк.
кто-нибудь знает, как это сделать по-моему?
25 ответов:
вам нужно будет сделать
User-Defined FunctionЕсли вы хотите иметь синтаксис, подобный вашему примеру, но не могли бы вы сделать то, что вы хотите сделать, встроенный, довольно легко сCASEзаявление, как сказали другие.The
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, PIVOT, Оператор unpivot, в ОДС, и невероятно длинные случае заявления.
- он не страдает от проблемы обработка нулей, он обрабатывает их просто отлично.
- легко поменять "MAX "на" MIN"," AVG "или"SUM". Вы можете использовать любую агрегатную функцию, чтобы найти агрегат по многим различным столбцам.
- вы не ограничены именами, которые я использовал (т. е. "AllPrices" и "Price"). Вы можете выбрать свои собственные имена, чтобы сделать его легче читать и понимать для следующего парня.
- вы можете найти несколько агрегатов с помощью SQL Server 2008 в derived_tables как Итак:
выберите MAX (a), MAX(b) FROM (значения (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, потому что aNULLпо обе стороны от вашегоboolean_expressionвызываетIIFвернутьfalse_value(вместоNULL).
Я бы пошел с решением, предоставленным kcrumley Просто измените его немного, чтобы обрабатывать нули
create function dbo.HigherArgumentOrNull(@val1 int, @val2 int) returns int as begin if @val1 >= @val2 return @val1 if @val1 < @val2 return @val2 return NULL endEDIT Изменено после комментария от Марк. Как он правильно указал в 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 с нулевой обработкой (на основе ответа 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]
Comments