SQL Server中是否有一个Max函数采用两个值,如.NET中的Math.Max?

时间:2020-03-06 14:38:01  来源:igfitidea点击:

我想写这样的查询:

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

但这不是MAX函数的工作原理,对吗?它是一个聚合函数,因此需要一个参数,然后返回所有行的MAX。

有人知道我该怎么做吗?

解决方案

我不这么认为。前几天我想要这个。我最接近的是:

SELECT
  o.OrderId,
  CASE WHEN o.NegotiatedPrice > o.SuggestedPrice THEN o.NegotiatedPrice 
     ELSE o.SuggestedPrice
  END
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

我可能不会这样做,因为它的效率比前面提到的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

如果我们想使用与示例类似的语法,则需要创建一个"用户定义的函数",但是我们是否可以像其他人一样使用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

其他答案很好,但是如果我们不得不担心具有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

我会选择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

编辑
在Mark评论后修改。正如他在3个值逻辑中正确指出的那样,x> NULL或者x <NULL应该总是返回NULL。换句话说,结果未知。

糟糕,我刚刚发布了此问题的虚假信息...

答案是,没有像Oracle的Greatest这样的内置函数,但是我们可以使用UDF对2列实现类似的结果,请注意,在这里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

可以在一行中完成:

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

编辑:如果要处理非常大的数字,则必须将值变量转换为bigint,以避免整数溢出。

DECLARE @MAX INT
@MAX = (SELECT MAX(VALUE) 
               FROM (SELECT 1 AS VALUE UNION 
                     SELECT 2 AS VALUE) AS T1)

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

对于上面关于大数的答案,我们可以在加法/减法之前进行乘法。它有点笨重,但是不需要演员。 (我不能代表速度,但我认为它仍然相当快)

SELECT 0.5 * ((@val1 + @val2) +
  ABS(@val1 - @val2))

更改为

SELECT @val1*0.5+@val2*0.5 +
  ABS(@val1*0.5 - @val2*0.5)

如果我们要避免投射,至少可以选择一种方法。

子查询可以访问外部查询中的列,因此我们可以使用此方法跨列使用汇总(例如" 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