Есть ли в SQL Server функция Max, которая принимает два значения, такие как Math.Max ​​в .NET? - PullRequest
434 голосов
/ 24 сентября 2008

Я хочу написать запрос, подобный этому:

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

Но это не так, как работает функция MAX, верно? Это агрегатная функция, поэтому она ожидает один параметр, а затем возвращает MAX всех строк.

Кто-нибудь знает, как это сделать по-моему?

Ответы [ 28 ]

412 голосов
/ 26 февраля 2012

Если вы используете SQL Server 2008 (или выше), то это лучшее решение:

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

Все кредиты и голоса должны идти на Ответ Свена на связанный вопрос: «SQL MAX из нескольких столбцов?»
Я говорю, что это " лучший ответ ", потому что:

  1. Это не требует усложнения вашего кода с помощью UNION, PIVOT, UNPIVOT, UDF и безумно длинные CASE.
  2. Проблема не связана с обработкой нулей, она отлично справляется с ними.
  3. Легко поменять "MAX" на "MIN", "AVG" или "SUM". Вы можете использовать любую функцию агрегирования, чтобы найти агрегат по множеству различных столбцов.
  4. Вы не ограничены именами, которые я использовал (т.е. "AllPrices" и "Price"). Вы можете выбрать свои собственные имена, чтобы было легче читать и понимать следующего парня.
  5. Вы можете найти несколько агрегатов, используя SQL Server 2008 производные_таблицы , например:
    ВЫБРАТЬ МАКС. (А), МАКС. (B) ОТ (ЗНАЧЕНИЯ (1, 2), (3, 4) , (5, 6), (7, 8), (9, 10)) AS MyTable (a, b)
208 голосов
/ 16 ноября 2008

Можно сделать в одну строку:

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

Редактировать: Если вы работаете с очень большими числами, вам придется преобразовать переменные-значения в bigint, чтобы избежать целочисленного переполнения.

142 голосов
/ 24 сентября 2008

Вам нужно было бы сделать 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
121 голосов
/ 24 сентября 2008

Я так не думаю. Я хотел этого на днях. Самое близкое, что у меня было:

SELECT
  o.OrderId,
  CASE WHEN o.NegotiatedPrice > o.SuggestedPrice THEN o.NegotiatedPrice 
     ELSE o.SuggestedPrice
  END
FROM Order o
63 голосов
/ 21 июня 2016

Почему бы не попробовать функцию IIF (требуется SQL Server 2012 и более поздние версии)

IIF(a>b, a, b)

Вот и все.

(Подсказка: будьте осторожны с любым из них, будет null, так как результат a>b будет ложным, когда любой из них равен нулю. Поэтому b будет результатом в этом случае)

32 голосов
/ 20 мая 2010
DECLARE @MAX INT
@MAX = (SELECT MAX(VALUE) 
               FROM (SELECT 1 AS VALUE UNION 
                     SELECT 2 AS VALUE) AS T1)
11 голосов
/ 24 сентября 2008

Другие ответы хороши, но если вам нужно беспокоиться о значениях NULL, вам может потребоваться этот вариант:

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
8 голосов
/ 21 октября 2010

Подзапросы могут обращаться к столбцам из запроса Outer, поэтому вы можете использовать этот подход для использования таких агрегатов, как 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
6 голосов
/ 26 июня 2014

SQL Server 2012 представлен IIF:

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

Обработка NULL рекомендуется при использовании IIF, потому что NULL по обе стороны от boolean_expression заставит IIF вернуть false_value (в отличие от NULL).

5 голосов
/ 24 сентября 2008

Я бы пошел с решением, предоставленным kcrumley Просто слегка измените его для обработки значений 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

EDIT Изменено после комментария от Mark . Как он правильно указал в трехзначной логике, x> NULL или x

...