Есть ли функция Max в SQL Server, которая принимает два значения, например Math.Max в .NET?

StackOverflow https://stackoverflow.com/questions/124417

  •  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 из нескольких столбцов?"
Я говорю, что это "лучший ответ" потому что:

  1. Для этого не требуется усложнять ваш код статьями UNION, PIVOT, UNPIVOT, UDF и безумно длинными регистрами.
  2. Он не сталкивается с проблемой обработки нулей, он справляется с ними просто отлично.
  3. Легко заменить "MAX" на "MIN", "AVG" или "SUM".Вы можете использовать любую агрегатную функцию для поиска агрегата по множеству различных столбцов.
  4. Вы не ограничены именами, которые я использовал (т.е."Все цены" и "Прайс").Вы можете выбрать свои собственные имена, чтобы их было легче прочесть и понять следующему парню.
  5. Вы можете найти несколько агрегированных данных, используя 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

kristof

Опубликовал этот ответ:

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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top