ChatGPT解决这个技术问题 Extra ChatGPT

SQL Server 中是否有一个 Max 函数,它接受两个值,例如 .NET 中的 Math.Max?

我想写一个这样的查询:

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

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

有谁知道如何按照我的方式做?

这在大多数其他数据库中作为 GREATEST 函数实现; SQLite 通过允许 MAX 聚合中的多个列来模拟支持。
在为下面的 max(a, b) 找到解决方案时,请记住有关是否要重复“a”和/或“b”的语法或计算的问题。即,如果“b”源自涉及大量语法的复杂计算,那么您可能更喜欢“b”仅出现一次的解决方案。例如,解决方案“IIF(a>b, a, b)”意味着重复“b”——这在语法上可能很难看,但是以下解决方案意味着“b”(和“a”)只出现一次:SELECT MAX(VALUE)从(选择 a 作为值联合选择 b 作为值)作为 T1
遵循 OMG Ponies 的好建议,在我一直使用的 DBMS 中,函数是 GREATER,而不是 GREATEST。因此,请检查您的 DBMS 的帮助,如果您没有找到一个,请尝试另一个,或类似的东西。

C
Community

如果您使用的是 SQL Server 2008(或更高版本),那么这是更好的解决方案:

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

所有功劳和投票都应归于 Sven's answer to a related question, "SQL MAX of multiple columns?"
我说这是“最佳答案”,因为:

它不需要使用 UNION、PIVOT、UNPIVOT、UDF 和疯狂的 CASE 语句使您的代码复杂化。它不受处理空值问题的困扰,它处理得很好。用“MIN”、“AVG”或“SUM”替换“MAX”很容易。您可以使用任何聚合函数来查找许多不同列的聚合。您不限于我使用的名称(即“AllPrices”和“Price”)。您可以选择自己的名字,以便下一个人更容易阅读和理解。您可以使用 SQL Server 2008 的 derived_tables 找到多个聚合,如下所示:SELECT MAX(a), MAX(b) FROM (VALUES (1, 2), (3, 4), (5, 6), (7, 8), (9, 10) ) AS MyTable(a, b)


+1 唯一的答案,不需要访问创建过程/功能!
正是我正在寻找的答案类型。使用函数很慢,这也适用于日期,这是我需要的。
+1 完美,特别是要比较超过 2 列!
这比只需要计算标量的 CASE WHEN 解决方案的性能低。
虽然在确定 2 个值的 MAX 时,更简单的语法可能永远不值得性能损失,但对于更多值可能是另一回事。即使在获得 4 个值的最大值时,如果手动生成 CASE 子句会变得冗长、笨拙且容易出错,而 VALUES 子句仍然简单明了。
s
splattne

可以在一行中完成:

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

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


+1我相信您提供了最正确的方法。 "SELECT ((@val1+@val2) + ABS(@val1-@val2))/2 as MAX_OF_TWO" 还要记住,"SELECT ((@val1+@val2) - ABS(@val1-@val2))/2 as MIN_OF_TWO ”。
如果总和大于可以存储在 int 中,这种方式将产生溢出错误: declare @val1 int declare @val2 int set @val1 = 1500000000 set @val2 = 1500000000 SELECT 0.5 * ((@val1 + @val2) + ABS(@val1 - @val2)) -- => 溢出错误
这是极其“肮脏”的“伎俩”。编程时,您的代码应明确表达目标,但在您的情况下,它看起来像是来自混淆竞赛的代码。
它可能是“脏”的,但它可能是具有简单 SQL 方言的数据库的唯一选择。
我不同意玛西亚斯。代码本身并不一定需要明确表达目标,只要注释允许人们解决它。如果您在代码(或任何地方)中做任何复杂的数学方程式,有时很难使其具有自我描述性。只要把它分解成更简单、更容易理解的部分,那就是正确的编程。
S
SteveC

如果您想使用与示例类似的语法,则需要创建一个 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

我会支持你的解决方案,我唯一要添加的是对 NULL 值的支持。如果您只是将最后一行:“return @value2”修改为:“return isnull(@val2,@val1)”,那么如果其中一个值为 null,则函数将返回非 null 值,否则它将作为普通的
那么其他数据类型呢,例如我需要编写一个 HigherIntegerArgument 和一个 HigherDateTimeArgument 和一个 HigherVarcharArgument 和一个 ...?
这将非常缓慢,因为所有事物都是标量 UDF。改为使用内联 UDF
@xan 我不知道当我实际问这个问题时我的想法是什么。不会太多,很明显。无论如何感谢您的回答。
@Thomas 强制性模因图片(以任何方式无意冒犯您!)flickr.com/photos/16201371@N00/2375571206
S
Scott Langham

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

SELECT
  o.OrderId,
  CASE WHEN o.NegotiatedPrice > o.SuggestedPrice THEN o.NegotiatedPrice 
     ELSE o.SuggestedPrice
  END
FROM Order o

这是我最喜欢的方法。你不会冒溢出的风险,而且它没有 splattne 的解决方案那么神秘(顺便说一句很酷),而且我没有创建 UDF 的麻烦。 case 在很多情况下都非常方便。
SELECT o.OrderId, CASE WHEN o.NegotiatedPrice > o.SuggestedPrice OR o.SuggestedPrice 为 NULL THEN o.NegotiatedPrice ELSE o.SuggestedPrice END FROM Order o
当您使用诸如“(datediff(day, convert(datetime, adr_known_since, 120), getdate())-5)*0.3”之类的术语而不是“o.NegotiatedPrice”时,您必须重复此代码。将来对该术语的任何更改都必须进行两次。 min(x, y, ...) 类型的函数会更好
这个答案似乎已经过时了:GREATEST exists in TSQL: docs.microsoft.com/en-us/sql/t-sql/functions/…
X
Xin

为什么不试试 IIF 功能(需要 SQL Server 2012 及更高版本)

IIF(a>b, a, b)

而已。

(提示:请注意任何一个都是 null,因为只要其中一个为空,a>b 的结果就会为假。所以在这种情况下,b 将是结果)


如果其中一个值是 NULL,则结果将始终是第二个。
IIF() 是 CASE 语句的语法糖。如果 CASE 条件的任一值为 NULL,则结果将是第二个 (ELSE)。
@xxyzzy 那是因为 NULL > 1234 声明是错误的
所以 IIF(a>b, a, COALESCE(b,a)) 在只存在一个时给出值
M
Martin Smith
DECLARE @MAX INT
@MAX = (SELECT MAX(VALUE) 
               FROM (SELECT 1 AS VALUE UNION 
                     SELECT 2 AS VALUE) AS T1)

我给这个解决方案一个+1,因为它符合DRY(不要重复自己)而无需编写UDF。如果您需要检查的两个值都是其他 sql 的结果,那也很好,例如,在我的情况下,我想找到 2 个 select count(*) 语句中的较大者。
我讨厌我不得不求助于这个解决方案,但这肯定是在 SQL Server 中执行此操作的最佳方法,直到他们添加对 GREATEST 或内联 MAX 的本机支持。感谢您发布它-+1 给您!
L
LukStorms

在 SQL Server 2012 或更高版本中,您可以使用 IIFISNULL(或 COALESCE)的组合来获得最多 2 个值。
即使其中 1 个为 NULL。

IIF(col1 >= col2, col1, ISNULL(col2, col1)) 

或者,如果您希望它在两者都为 NULL 时返回 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

但是如果一个人需要多列的最大值呢?然后我建议对 VALUES 的聚合进行 CROSS APPLY。

例子:

SELECT t.*
, ca.[Maximum]
, ca.[Minimum], ca.[Total], ca.[Average]
FROM SomeTable t
CROSS APPLY (
   SELECT 
    MAX(v.col) AS [Maximum], 
    MIN(v.col) AS [Minimum], 
    SUM(v.col) AS [Total], 
    AVG(v.col) AS [Average]
   FROM (VALUES (t.Col1), (t.Col2), (t.Col3), (t.Col4)) v(col)
) ca

这具有额外的好处,即可以同时计算其他事物。


佚名

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

唯一需要的 ISNULL 是在 ELSE 之后。如果其中一个值已经为空,则初始“>”比较将返回 false 并转到 ELSE。
M
Martin Smith

子查询可以访问外部查询中的列,因此您可以使用 this approach 跨列使用聚合,例如 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

好的!它可以很好地扩展。
+1 表示对那些仍在 2005 年的人的爱。我不知道我是如何忽略这个答案的。在幕后,我想它的表现和我 2 年后发布的一样好。回想起来,我应该已经意识到这一点并更新了您的答案以包括当时较新的 2008 语法。抱歉,希望我现在可以与您分享我的观点。
@MikeTeeVee - 谢谢!是的,在幕后计划将是相同的。但是 VALUES 语法更好。
很好的答案,因为它适用于所有版本,包括不支持 VALUES() 的新 Azure DW/synapse
C
Chris Rogers

尝试这个。它可以处理超过 2 个值

SELECT Max(v) FROM (VALUES (1), (2), (3)) AS value(v)

极好的!我使用在我们的 AZURE SQL Server 上运行的 GREATEST 编写了一个解决方案,但这个解决方案也在我的桌面 SQL Server Express 上运行
这是最好的解决方案。特别是如果您的值来自复杂的函数。
T
Tom Arleth
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

解释请参考这篇文章:red-gate.com/simple-talk/sql/sql-training/…
请不要仅通过链接在您的代码中包含所需的信息。想象一下,该链接将在某一天到期,届时您的答案将毫无用处。因此,请继续在您的答案中直接添加 essentiell 信息。但是您仍然可以提供该链接作为其他人查找更多信息的资源。
C
Community

我会使用 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

EDITMark 的评论后修改。正如他在 3 值逻辑 x > 中正确指出的那样。 NULL 或 x < NULL 应始终返回 NULL。换句话说,未知的结果。


空值很重要。并且始终如一地处理它们很重要。 Is NULL > x 的唯一正确答案是 NULL。
你是对的,我会修改我的答案以反映这一点,感谢您指出这一点
如果我们传递一个 int 和一个 NULL,那么我认为希望返回非空值更为常见,因此该函数充当 Max(x,y) 和 ISNULL(x,y) 的组合。因此,我个人会将最后一行更改为: return ISNULL(@val1, @val2) - 诚然,这可能是您必须开始的:)
@the-locster,见 Mark 的评论
这将非常缓慢,因为所有事物都是标量 UDF。改为使用内联 UDF
S
SetFreeByTruth

SQL Server 2012 引入了 IIF

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

建议在使用 IIF 时处理 NULL,因为 boolean_expression 两侧的 NULL 将导致 IIF 返回 false_value(而不是 NULL)。


当其他值为负时,您的解决方案将无法很好地处理 NULL,这将返回 null
M
Mark Brackett

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

C
Community

糟糕,我刚刚发布了 dupe of this question...

答案是,没有像 Oracle's 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

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

注意: GREATEST 函数实现将匹配 2 个参数的 oracle 行为,如果任何参数为 null,它将返回 null
使用 sql_variant 时应该小心。在以下情况下,您的函数将给出意外结果: SELECT dbo.greatest(CAST(0.5 AS FLOAT), 100)
@Neil 是对的(我学得很辛苦),您将如何改进此功能以防止此类问题?
U
Uri Abramson

它就像这样简单:

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;

请参阅@Neil 对先前答案的评论 SELECT dbo.InlineMax(CAST(0.5 AS FLOAT), 100) 是错误的。
L
Lukasz Szozda

就在这里。

T-SQL (SQL Server 2022 (16.x)) 现在支持 GREATEST/LEAST 函数:

MAX/MIN 作为非聚合函数 现在适用于 Azure SQL 数据库和 SQL 托管实例。它将滚动到 SQL Server 的下一个版本。

Logical Functions - GREATEST (Transact-SQL)

此函数返回一个或多个表达式列表中的最大值。最大(表达式 1 [ ,...表达式 N ] )

所以在这种情况下:

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

您不会在 SQL Server 2019 (150) 或更早版本上找到这些函数。
我认为 GREATEST 目前仅在 SQL Server Azure 上可用
P
Per Hornshøj-Schierbeck

你可以这样做:

select case when o.NegotiatedPrice > o.SuggestedPrice 
then o.NegotiatedPrice
else o.SuggestedPrice
end

W
Wayne
SELECT o.OrderID
CASE WHEN o.NegotiatedPrice > o.SuggestedPrice THEN
 o.NegotiatedPrice
ELSE
 o.SuggestedPrice
END AS Price

否则为空(oSuggestedPrice,o,NegotiatedPrice)
d
deepee1

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

选择 0.5 * ((@val1 + @val2) + ABS(@val1 - @val2))

更改为

选择@val1*0.5+@val2*0.5 + ABS(@val1*0.5 - @val2*0.5)

如果您想避免强制转换,至少是另一种选择。


s
scradam

这是一个应该处理空值并将与旧版本的 MSSQL 一起使用的案例示例。这是基于其中一个流行示例中的内联函数:

case
  when a >= b then a
  else isnull(b,a)
end

a
ashraf mohammed
 -- 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;

虽然像这样有趣地使用 VALUES 内联,但我不确定这是否比 CASEIFF 更简单。我很想看看这个解决方案的性能如何与其他选项相提并论
@ChrisSchaller 这种使用 VALUES 的有趣之处在于,它提供了一种简单的方法来检查多个列的 MAX 值。至于性能,我不知道:)
@mortb 这个答案是对 stackoverflow.com/a/52296106/1690217 早期答案的公然抄袭在内部集合上聚合。它优雅、低代码,但理解起来很复杂。
s
sth
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

m
mohghaderi

这是@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

j
jahu

这是一个带有 NULL 处理的 IIF 版本(基于 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))

e
error
select OrderId, (
    select max([Price]) from (
        select NegotiatedPrice [Price]
        union all
        select SuggestedPrice
    ) p
) from [Order]

J
Jason Plank

以最简单的形式...

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

S
Steve Ford

对于 SQL Server 2012:

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

C
Chris Porter

扩展 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

D
Desert Eagle

在 MemSQL 中执行以下操作:

-- DROP FUNCTION IF EXISTS InlineMax;
DELIMITER //
CREATE FUNCTION InlineMax(val1 INT, val2 INT) RETURNS INT AS
DECLARE
  val3 INT = 0;
BEGIN
 IF val1 > val2 THEN
   RETURN val1;
 ELSE
   RETURN val2;
 END IF; 
END //
DELIMITER ;

SELECT InlineMax(1,2) as test;