250

I have two variables, one is called PaidThisMonth, and the other is called OwedPast. They are both results of some subqueries in SQL. How can I select the smaller of the two and return it as a value titled PaidForPast?

The MIN function works on columns, not variables.

2
  • 5
    If you're on Postgres or MySQL, skip down to @Gil_Margolin's answer. Commented Apr 22, 2018 at 4:38
  • And if you're using SQL Server 2022+ or Azure SQL, also skip to @Gil_Margolin's answer stackoverflow.com/a/37736853/5740181 Commented Jun 19, 2024 at 1:00

12 Answers 12

225

SQL Server 2012 and 2014 supports IIF(cont,true,false) function. Thus for minimal selection you can use it like

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

While IIF is just a shorthand for writing CASE...WHEN...ELSE, it's easier to write.

Sign up to request clarification or add additional context in comments.

5 Comments

IIF is just a syntactic sugar for CASE...WHEN...ELSE.
Possibly yes. But easier to write.
@MertGülsoy And easier to read, which should be at the top of everyone's priority list, right after correctness.
This should be the accepted answer. As a front-end developer, CASE...WHEN...ELSE is so awkward to read. IIF is way clearer and shorter.
IIF(onlyRequireSQLServerGTE2012Support, thisAnswer, theOtherAnswer)
182

The solutions using CASE, IIF, and UDF are adequate, but impractical when extending the problem to the general case using more than 2 comparison values. The generalized solution in SQL Server 2008+ utilizes a strange application of the VALUES clause:

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

Credit due to this website: http://sqlblog.com/blogs/jamie_thomson/archive/2012/01/20/use-values-clause-to-get-the-maximum-value-from-some-columns-sql-server-t-sql.aspx

7 Comments

if you want the min non-zero: MIN(x*(case x when 0 then null else 1 end))
Except MartinC gave the same answer four years earlier, and actually showed it with more than two values...
Auspex, MartinC's answer is unrelated. This answer does not use unions.
Broken link in the credit due.
Would be a nice solution, however, limited: Cannot perform an aggregate function on an expression containing an aggregate or a subquery The only thing I care about here is to do min(a,b) and it must be short, simply for cleanlyness of the code
|
148

Use Case:

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

As Inline table valued UDF

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

Usage:

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

ADDENDUM: This is probably best for when addressing only two possible values, if there are more than two, consider Craig's answer using Values clause.

2 Comments

better understandable syntax: return(select minValue = case when @@param1 < @@param2 then @@param1 else @@param2 end). Ok this may not be normalized, i don't know. But it's much more understandable and should be normalized.
Another reason to prefer @Craig's answer below is due to null handling. If the values being compared are nullable, and one of the values being compared is null, the switch-case shown might return null or the value, depending on the order of the WHEN test (unless you add use of ISNULL). Craig's approach will always prefer selection of the not-null value which seems more correct to me, at least in my current use-case in the comparison of nullable dates.
68

For SQL Server 2022+ (or MySQL or PostgreSQL 9.3+), a better way is to use the LEAST and GREATEST functions.

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

With:

  • GREATEST(value [, ...]) : Returns the largest (maximum-valued) argument from values provided
  • LEAST(value [, ...]) Returns the smallest (minimum-valued) argument from values provided

Documentation links :

6 Comments

This also works in PostgreSQL (and it's exactly what I was looking for :) See: postgresql.org/docs/9.5/static/functions-conditional.html
This is the best answer by far.
@RobertoRodriguez it would be the best if the question had MySQL or PostgreSQL tagged as part of the question. The question was specifically about tsql so this answer does not help at all.
this is not answer for MSSQL
GREATEST and LEAST are promised to be available in Sql2022, so this answer was just over 6 years too early. ;)
|
39

I just had a situation where I had to find the max of 4 complex selects within an update. With this approach you can have as many as you like!

You can also replace the numbers with aditional selects

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

More complex usage

 @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

I'm sure a UDF has better performance.

2 Comments

I like that one the most since it's basic SQL. Furthermore, UDFs are not necessarilly faster. For most column stores, each attribute (I assume you're also going to filter on the attributes) can be computed in parallel and just the qualifying set is unioned. So unions are not slow per se.
Use "union all" instead of just "union" for things like this, to avoid forcing a sort.
16

Here is a trick if you want to calculate maximum(field, 0):

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

returns 0 if field is negative, else, return field.

3 Comments

So, to calculate minimum(@a, @b), you could use: SELECT @a - ( ABS(@a-@b) + (@a-@b) ) / 2
and don't forget about type overflow ;)
Is this save from floating-point precision standpoint? Is it certain, that the result will never be something close to zero but negative?
6

Use a CASE statement.

Example B in this page should be close to what you're trying to do:
http://msdn.microsoft.com/en-us/library/ms181765.aspx

Here's the code from the page:

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

Comments

2
Select MIN(T.V) FROM (Select 1 as V UNION Select 2 as V) T

Comments

2

Building on the brilliant logic / code from mathematix and scottyc, I submit:

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;

Although the jump from scottyc's MIN function to the MAX function should have been obvious to me, it wasn't, so I've solved for it and included it here: SELECT @a + ( ABS(@b-@a) + (@b-@a) ) / 2. The randomly generated numbers, while not proof, should at least convince skeptics that both formulae are correct.

Comments

2

This works for up to 5 dates and handles nulls. Just couldn't get it to work as an Inline function.

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;

1 Comment

Just realised you don't need the WHERE Clauses as MIN will remove Nulls anyway.
1

Use a temp table to insert the range of values, then select the min/max of the temp table from within a stored procedure or UDF. This is a basic construct, so feel free to revise as needed.

For example:

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

1 Comment

What use is a procedure that gets the minimum of a set of hardcoded values? Furthermore, you have multiple syntax errors: * It seems () is not a valid parameter list. To declare a procedure with no parameters, it seems you need to not have brackets at all. * ' is not a comment marker in SQL. * You're missing the VALUES keyword. * You're missing the semicolons. It's important not to leave these off: stackoverflow.com/questions/710683/…
-1
SELECT (WHEN first > second THEN second ELSE first END) the_minimal FROM table

3 Comments

Remember that Stack Overflow isn't just intended to solve the immediate problem, but also to help future readers find solutions to similar problems, which requires understanding the underlying code. This is especially important for members of our community who are beginners, and not familiar with the syntax. Given that, can you edit your answer to include an explanation of what you're doing and why you believe it is the best approach?
Also, that's especially important here, where there's existing answers that have been validated by the community, including one with nearly 200 upvotes. What sets your answer apart? Under what conditions would your answer be preferred?
Not valid syntax. Missing CASE keyword. And if made valid syntax then repeats existing answers

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.