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



Я хочу написать такой запрос:



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


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



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

708   25  

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

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

вот и все.

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).

Я бы пошел с решением, предоставленным 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
end

EDIT Изменено после комментария от Марк. Как он правильно указал в 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]

в Presto вы можете использовать use

SELECT array_max(ARRAY[o.NegotiatedPrice, o.SuggestedPrice])

Comments

    Ничего не найдено.