在SQL中获取两个值中的最小值


179

我有两个变量,一个称为PaidThisMonth,另一个称为OwedPast。它们都是SQL中某些子查询的结果。如何选择两者中较小的一个并将其作为标题值返回PaidForPast

MIN函数适用于列,而不适用于变量。


1
如果您使用的是Postgres或MySQL,请跳至@Gil_Margolin的答案。
Noumenon

Answers:


127

用例:

   Select Case When @PaidThisMonth < @OwedPast 
               Then @PaidThisMonth Else @OwedPast End PaidForPast

作为内联表值UDF

CREATE FUNCTION Minimum
(@Param1 Integer, @Param2 Integer)
Returns Table As
Return(Select Case When @Param1 < @Param2 
                   Then @Param1 Else @Param2 End MinValue)

用法:

Select MinValue as PaidforPast 
From dbo.Minimum(@PaidThisMonth, @OwedPast)

附录:这可能是只处理两个可能值的最佳方法,如果有两个以上,请考虑使用Values子句的Craig答案


更好理解的语法:return(选择minValue =当@@ param1 <@@ param2然后@@ param1否则@@ param2结尾的情况)。好的,这可能不规范,我不知道。但这更容易理解,应该规范化。
Softlion 2012年

1
首选@Craig答案的另一个原因是由于处理了空值。如果要比较的值是可为空的,并且要比较的值之一为空,则显示的切换条件可能会返回空值或该值,具体取决于WHEN测试的顺序(除非添加ISNULL的使用)。克雷格(Craig)的方法将始终选择非空值,这对我来说似乎更正确,至少在我当前的用例中,比较可空日期。

146

SQL Server 2012和2014支持IIF(cont,true,false)函数。因此,对于最少的选择,您可以像

SELECT IIF(first>second, second, first) the_minimal FROM table

虽然IIF只是编写的简写,但编写CASE...WHEN...ELSE起来更容易。


8
IIF只是语法糖CASE...WHEN...ELSE
Salman

55
可能是的。但是更容易写。
MertGülsoy2015年

1
@MertGülsoy而且更容易阅读,在正确之后,应该放在每个人的优先列表的顶部。
丹尼尔

118

使用CASE,IIF和UDF的解决方案是足够的,但是当使用两个以上的比较值将问题扩展到一般情况时,这是不切实际的。SQL Server 2008+中的通用解决方案利用了VALUES子句的奇怪应用程序:

SELECT
PaidForPast=(SELECT MIN(x) FROM (VALUES (PaidThisMonth),(OwedPast)) AS value(x))

归功于该网站:http : //sqlblog.com/blogs/jamie_thomson/archive/2012/01/20/use-values-clause-to-get-the-maximum-value-from-some-columns-sql-服务器t-sql.aspx


12
这是最好的答案
FindOutIslamNow

如果您希望最小值非零:MIN(x*(case x when 0 then null else 1 end))
mpag

除了MartinC在四年前给出了相同的答案外,实际上它给出了两个以上的值……
Auspex

4
Auspex,MartinC的答案无关。此答案不使用联合。
Craig

29

我只是遇到一种情况,我必须在一次更新中找到最多4个复杂选择。通过这种方法,您可以拥有任意数量的商品!

您也可以将数字替换为常规选择

select max(x)
 from (
 select 1 as 'x' union
 select 4 as 'x' union
 select 3 as 'x' union
 select 2 as 'x' 
 ) a

更复杂的用法

 @answer = select Max(x)
           from (
                select @NumberA as 'x' union
                select @NumberB as 'x' union
                select @NumberC as 'x' union
                select (
                       Select Max(score) from TopScores
                       ) as 'x' 
     ) a

我敢肯定,UDF的性能更好。


我最喜欢它,因为它是基本的SQL。此外,UDF不必更快。对于大多数列存储,可以并行计算每个属性(我假设您还将对属性进行过滤),并且仅合并限定集。因此,工会本身并不慢。
Bouncner '16

简单而出色。
ashleedawg

21

对于MySQL或PostgreSQL 9.3+,更好的方法是使用LEASTGREATEST函数。

SELECT GREATEST(A.date0, B.date0) AS date0, 
       LEAST(A.date1, B.date1, B.date2) AS date1
FROM A, B
WHERE B.x = A.x

带有:

  • GREATEST(value [, ...]):从提供的值中返回最大(最大值)的参数
  • LEAST(value [, ...])从提供的值中返回最小(最小值)的参数

文档链接:


这也可以在PostgreSQL中使用(这正是我在寻找的东西:)请参阅:postgresql.org/docs/9.5/static/functions-conditional.html
Albert Vaca Cintora

1
到目前为止,这是最好的答案。
罗伯托·罗德里格斯

2
@RobertoRodriguez,如果问题中带有MySQL或PostgreSQL标记,那将是最好的选择。问题是关于tsql的,所以这个答案根本没有帮助。
Jmaurier

这不是MSSQL的答案
Mujah Maskey

13

如果您要计算最大值(field,0),这是一个技巧:

SELECT (ABS(field) + field)/2 FROM Table

如果field为负,则返回0 ,否则返回field


3
因此,要计算最小值(@a,@b),可以使用:SELECT @a - ( ABS(@a-@b) + (@a-@b) ) / 2
scottyc

1
并且不要忘记类型溢出;)
pkuderov

从浮点精度的角度来看,这是节省吗?可以肯定的是,结果永远不会接近零,而是负数?
zuraff

6

使用CASE语句。

此页中的示例B应该与您要执行的操作很接近:http :
//msdn.microsoft.com/en-us/library/ms181765.aspx

这是页面中的代码:

USE AdventureWorks;
GO
SELECT   ProductNumber, Name, 'Price Range' = 
      CASE 
         WHEN ListPrice =  0 THEN 'Mfg item - not for resale'
         WHEN ListPrice < 50 THEN 'Under $50'
         WHEN ListPrice >= 50 and ListPrice < 250 THEN 'Under $250'
         WHEN ListPrice >= 250 and ListPrice < 1000 THEN 'Under $1000'
         ELSE 'Over $1000'
      END
FROM Production.Product
ORDER BY ProductNumber ;
GO

2

使用临时表插入值的范围,然后从存储过程或UDF中选择临时表的最小值/最大值。这是一个基本结构,因此可以根据需要随时进行修改。

例如:

CREATE PROCEDURE GetMinSpeed() AS
BEGIN

    CREATE TABLE #speed (Driver NVARCHAR(10), SPEED INT);
    '
    ' Insert any number of data you need to sort and pull from
    '
    INSERT INTO #speed (N'Petty', 165)
    INSERT INTO #speed (N'Earnhardt', 172)
    INSERT INTO #speed (N'Patrick', 174)

    SELECT MIN(SPEED) FROM #speed

    DROP TABLE #speed

END

2

这最多可用于5个日期并处理null。只是无法使其用作内联函数。

CREATE FUNCTION dbo.MinDate(@Date1 datetime = Null,
                            @Date2 datetime = Null,
                            @Date3 datetime = Null,
                            @Date4 datetime = Null,
                            @Date5 datetime = Null)
RETURNS Datetime AS
BEGIN
--USAGE select dbo.MinDate('20120405',null,null,'20110305',null)
DECLARE @Output datetime;

WITH Datelist_CTE(DT)
AS (
        SELECT @Date1 AS DT WHERE @Date1 is not NULL UNION
        SELECT @Date2 AS DT WHERE @Date2 is not NULL UNION
        SELECT @Date3 AS DT WHERE @Date3 is not NULL UNION
        SELECT @Date4 AS DT WHERE @Date4 is not NULL UNION
        SELECT @Date5 AS DT WHERE @Date5 is not NULL
   )
Select @Output=Min(DT) FROM Datelist_CTE

RETURN @Output
END

只是意识到您不需要WHERE子句,因为MIN仍然会删除Null。
劳伦斯

2

我基于mathematix和scottyc出色的逻辑/代码,提出:

DECLARE @a INT, @b INT, @c INT = 0

WHILE @c < 100
    BEGIN
        SET @c += 1
        SET @a = ROUND(RAND()*100,0)-50
        SET @b = ROUND(RAND()*100,0)-50
        SELECT @a AS a, @b AS b,
            @a - ( ABS(@a-@b) + (@a-@b) ) / 2 AS MINab,
            @a + ( ABS(@b-@a) + (@b-@a) ) / 2 AS MAXab,
            CASE WHEN (@a <= @b AND @a = @a - ( ABS(@a-@b) + (@a-@b) ) / 2)
            OR (@a >= @b AND @a = @a + ( ABS(@b-@a) + (@b-@a) ) / 2)
            THEN 'Success' ELSE 'Failure' END AS Status
    END

尽管从scottyc的MIN函数到MAX函数的跳转对我来说应该是显而易见的,但事实并非如此,因此我为它解决了并将其包括在这里:SELECT @a +(ABS(@ b- @ a)+( @ b- @ a))/ 2.随机生成的数字虽然不能证明,但至少应使怀疑论者相信这两个公式都是正确的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.