Sort by minimum value of two columns
Asked Answered
A

13

51

I use SQL Server 2008 R2.

I need to sort a table by the minimal value of two columns.

The table looks like this:

ID: integer; 
Date1: datetime; 
Date2: datetime.

I want my data to be sorted by minimal of two dates.

What is the simplest way to sort this table that way?

Aperture answered 13/4, 2015 at 8:15 Comment(7)
It is embarrassing that SQL Server does not have a scalar MIN function. Please vote: connect.microsoft.com/SQLServer/feedback/details/767183/…Overtrump
the connect website is such garbage...you go there to upvote and it doesn't do anything. It doesn't upvote, it doesnt tell you an error occurred, it doesnt even tell you if you need to log in, it just uses some sort of update panel / ajax request where the screen kind of loads something but you get nothing out of it. Its no wonder even logging bugs or feature requests is barely done anymore. And by the way, who in their right mind closes a feature request because "we have high priority things to take care of now"...ok..but than what happens to that request!?!?! MS = Slowly falling apartTwi
You may be interested in many of the q/a on a related question, how to get the minimum of columns. Here are two examples: #1948253 dba.stackexchange.com/questions/21542/…Arabian
@usr: Possibly, but it might not work for this case. To me, it seems reasonable to expect that if such a function existed, it would work similar to the aggregate MIN, i.e. that it would ignore NULLs, unless all the arguments were NULL (or there were no arguments). ORDER BY, on the other hand, treats a NULL as the smallest value, for any data type. Since the OP wants to use the result for sorting, they might be interested in treating the NULL the way it's treated by ORDER BY rather than how it's treated by MIN.Transoceanic
I'm not realy sure, what do you mean by the 'sorted by minimal of two dates'. Could you give us an example data, witm more than 3 rows.Haemocyte
@Infinity - Two bounties? How much "attention" do you feel this somewhat mundane question warrants?Braunschweig
@usr, SQL Server 2022 has LEAST(), see my (updated) answer below.Egocentrism
L
60

NOT NULL columns. You need to add CASE expression into ORDER BY clause in following:

SELECT Id, Date1, Date2
FROM YourTable
ORDER BY CASE 
           WHEN Date1 < Date2 THEN Date1 
           ELSE Date2 
         END 

NULLABLE columns. As Zohar Peled wrote in comments if columns are nullable you could use ISNULL (but better to use COALESCE instead of ISNULL, because It's ANSI SQL standard) in following:

SELECT Id, Date1, Date2
FROM YourTable
ORDER BY CASE 
           WHEN COALESCE(Date1, '1753-01-01') < COALESCE(Date2, '1753-01-01') THEN Date1 
           ELSE Date2 
         END

You can read about ANSI standard dateformat 1753-01-01 here.

Leninakan answered 13/4, 2015 at 8:18 Comment(15)
That's assuming that the date columns are not nullable. if they are, then the correct order by clause would be CASE WHEN ISNULL(Date1, '01/01/1753') < ISNULL(Date2, '01/01/1753') THEN Date1 ELSE Date2 ENDAleksandrovsk
Sure, but OP asked for simplest way and didn't noticed about nullable columns, so in this case It's simplest way.Azucenaazure
I agree, but the OP may not be aware of this issue, so it's worth to mention. you've got my vote up anyway, since your answer contains links to the relevant parts of MSDN and therefor is better then the other answer.Aleksandrovsk
You really should use the ANSI standard dateformat. 1753-01-01Corelli
Why are you editing your answer 100 times? Adding space deleting space adding space deleting space?Dote
Just tested rollback function in edit. Nothing special.Azucenaazure
Could you please stop with the rollbacks? It's repeatedly "bumping" your answer and could be seen as an abuse of the editing function. It's also generating flags. If this continues, I will have to lock the answer.Chaos
The question linked is not about a date format it's about a date (the date of Jan 01, 1753).Transoceanic
There is no impact using case as try simple order by date1,date2 will give the same result. This is not a solution.Gleda
@qazifarhan you are wrong, If you use ORDER BY date1, date2 It will order first by date1 and if 2 or more values of date1 will have the same date then will order by date2. This question is about: how to order by minimum value of 2 columns. It should check which one is lower date1 or date2 and then order by It.Azucenaazure
I'm afraid, but this solution won't work for the nullable columns in case Date1 is NULL and Date2 isn't. You take Date1 as the minimal date when it equals to NULL, but it isn't actually true - the minimal date in the row is Date2, because another column doesn't contain datetime value.Extravert
For this is used COALESCE if date1 is null It take 01/01/1753 value.Azucenaazure
@StanislovasKalašnikovas No, I meant exactly what you're saying. Why are you taking 01/01/1753? Why don't you use the maximum datetime value? And why do you use the NULL value as the date time value? What if we use a date below 01/01/1753?Extravert
Because default minimum datetime value is 1753-01-01Azucenaazure
@StanislovasKalašnikovas Even though you use COALESCE your solution it messes up the output.Extravert
E
38

Use a CASE expression in the ORDER BY:

 ORDER BY case when date1 < date2 then date1 else date2 end

Edit:

If null values need to be considered, add coalesce():

 ORDER BY case when date1 < date2 then date1 else coalesce(date2,date1) end

Explanation:

If date1 < date2 then order by date1. (Both dates are non null here.) Works just like before.

Else use COALESCE() to order by date2 (when date2 is not null), or date1 (when date2 is null), or by null (if both dates are null.)

Edit 2:

If you're using SQL Server 2022 (or later), you can simply use the LEAST() function:

 ORDER BY LEAST(date1, date2)

(SQL Server's LEAST() implementation will return the non-null value if the other value is null. ISO/ANSI SQL's LEAST() returns null if any of the arguments is null.)

Egocentrism answered 13/4, 2015 at 8:17 Comment(3)
There is no impact using case as try simple order by date1,date2 will give the same result.Gleda
Select * from Table order by date1, date2 (100% same result)Gleda
@qazifarhan, I'm afraid, you are wrong here. Simple test: table with 3 rows: 2015-01-10, 2015-01-20; 2015-01-20, 2015-01-11; 2015-01-15, 2015-01-15. This is the order that you'll get when you sort by MIN of two dates. If you simply order by date1, date2, you'll get different result.Qualify
E
8

The simplest way is using of the VALUES keyword, like the following:

SELECT ID, Date1, Date2
FROM YourTable
ORDER BY (SELECT MIN(v) FROM (VALUES (Date1), (Date2)) AS value(v))

This code will work for all the cases, even with nullable columns.

Edit :

The solution with the COALESCE keyword is not universal. It has the important restrictions:

  • It won't work if the columns are of the Date type (if you use the dates before 01/01/1753)
  • It won't work in case one of the columns is NULL. It interprets the NULL value as the minimal datetime value. But is it actually true? It isn't even datetime, it is nothing.
  • The IF expression will be much more complicated if we use more than two columns.

According to the question:

What is the simplest way to sort this table that way?

The shortest and the simplest solution is the one which described above, because:

  • It doesn't take a lot of coding to implement it - simply add an one more line.
  • You don't need to care about whether the columns are nullable or not. You just use the code and it works.
  • You can extend the number of columns in your query simply by adding the one after a comma.
  • It works with the Date columns and you don't need to modify the code.

Edit 2 :

Zohar Peled suggested the following way of order:

I would order the rows by this rules: first, when both null, second, when date1 is null, third, when date 2 is null, fourth, min(date1, date2)

So, for this case the solution can be reached by using of the same approach, like the following:

SELECT ID, Date1, Date2
FROM YourTable
ORDER BY 
CASE WHEN Date1 IS NULL AND Date2 IS NULL THEN 0
     WHEN Date1 IS NULL THEN 1
     WHEN Date2 IS NULL THEN 2
     ELSE 3 END,
(SELECT MIN(v) FROM (VALUES ([Date1]), ([Date2])) AS value(v))

The output for this code is below:

The output result for *Zohar's* way of order

The COALESCE solution will not sort the table this way. It messes up the rows where at least one cell of the NULL value. The output of it is the following:

Weird ORDER BY of <code>COALESCE</code> solution

Hope this helps and waiting for critics.

Extravert answered 24/4, 2015 at 5:9 Comment(14)
This will not work as expected with nullable columns. The reason is that MIN aggregating function ignores null values. sqlFiddle is currently down, so I can't show you a live example, but you can take the sql from this link and try to run it.Aleksandrovsk
@ZoharPeled Yes, of course it ignores, but is it wrong?Extravert
To my opinion it's wrong because of the fact that by default, ORDER BY clause does not ignore null values. If you sort ascending by a nullable column the null values will be on the top of the result set. Unless specified otherwise, the solution should inspire to mimic this behavior. This is the reason I commented about nullable columns in the first place.Aleksandrovsk
@ZoharPeled OK, why is it wrong to place the rows where both columns are NULL to the top? If a row has at least one column with the correct value this value will be used in the ORDER BY clause.Extravert
It's not wrong, but it is wrong to place the rows that have one date null after rows that have both dates. When I ran the code I've got this result. As you can see, null dates are on the 5'th and last rows, where they clearly do not belong.Aleksandrovsk
@ZoharPeled Disagree, the task is to order by minimal value in a row which means if we have only one value in a row it will be the minimum. I don't understand why do you think that between NULL and datetime the minimal value is NULL.Extravert
I've already explained why I think that: This is the default behavior of ORDER BY clause.Aleksandrovsk
@ZoharPeled I think it is rather business-specific question. But IMHO it is weird to see rows in the beginning in the chaotic order where one cell is NULL.Extravert
I would order the rows by thess rules: first, when both null, second, when date1 is null, third, when date 2 is null, fourth, min(date1, date2). But I agree it's business-specific. for all we know the columns in the original question may not even be nullable, and this hole discussion is meaningless to the OP, but I would rather go with the most general solution and I think Stanislovas's answer provides a better solution for nullable columns.Aleksandrovsk
@ZoharPeled OK, but the COALESCE answer does it in the chaotic order anyway. Just check it. When Date1 is NULL the case is always TRUE - taking the Date1 value (NULL). When Date2 is NULL the case is always FALSE - taking the Date2 value (NULL). In all the cases we are taking the NULL value.Extravert
Well, it's still Stanislovas's answer, not mine... I don't see a significant reason to post another answer to this question just because of my personal opinion. as you wrote and I agreed: this is very business-specific, so my personal opinion might not fit with the OP's expectations.Aleksandrovsk
@ZoharPeled I wrote a solution specially for your case. Check it up.Extravert
I think you've over complicated the question. see my answer here. Anyway, the OP wanted the simplest way, not the most efficient nor the most scalable. I still think that a case is the simplest way to do it.Aleksandrovsk
@ZoharPeled You are right, I made it more efficient.Extravert
D
5

If you don't want to use Case statement in the Order By , then this is another approach, just moving the Case statement to Select

SELECT Id, Date1, Date2 FROM 
 (SELECT Id, Date1, Date2
  ,CASE WHEN Date1 < Date2 THEN Date1 ELSE Date2 END as MinDate 
FROM YourTable) as T
ORDER BY MinDate
Davisson answered 16/4, 2015 at 22:46 Comment(0)
H
5

This may be an alternate solution which does not require branching like CASE WHEN. This is based on the formula max(a,b)=1/2(a+b+|a−b|) as described here. We get the absolute values of a and b using DATEDIFF with a reference date ('1773-01-01').

ORDER BY (DATEDIFF(d,'17730101' ,isnull(Startdate,enddate)) + DATEDIFF(d,'17730101' ,isnull(EndDate,Startdate)) 
    -  ABS(DATEDIFF(d,isnull(Startdate,enddate),isnull(EndDate,Startdate))))

Test Data

Create Table #DateData(ID int Identity, Name varchar(15),Startdate datetime,EndDate DateTime)
Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-17 18:48:27','2015-04-18 18:48:27')
Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-19 18:48:27','2015-04-18 18:48:27')
Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-20 18:48:27','2015-04-18 18:48:27')
Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-11 18:48:27','2015-04-22 18:48:27')
Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-05-09 18:48:27','2015-04-18 18:48:27')
Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-17 19:07:38','2015-04-17 18:55:38')
Insert Into #DateData(Name,Startdate,EndDate) values ('myName','2015-04-17 19:07:38','2015-05-12 18:56:29')

Complete Query

select *
from #DateData order by (DATEDIFF(d,'17730101' ,isnull(Startdate,enddate)) + DATEDIFF(d,'17730101' ,isnull(EndDate,Startdate)) 
-  ABS(DATEDIFF(d,isnull(Startdate,enddate),isnull(EndDate,Startdate))))
Havener answered 17/4, 2015 at 13:51 Comment(0)
B
4

I prefer this way to handle nullable columns:

SELECT Id, Date1, Date2
FROM YourTable
ORDER BY 
   CASE 
     WHEN Date1 < Date2 OR Date1 IS NULL THEN Date1 
     ELSE Date2 
   END 
Brandonbrandt answered 17/4, 2015 at 5:16 Comment(0)
L
4

Code for max

I'm using CROSS APPLY, I am not sure about the performance, But CROSS APPLY often has a better performance in my experience.

CREATE TABLE #Test (ID INT, Date1 DATETIME, Date2 DATETIME)
INSERT INTO #Test SELECT 1, NULL, '1/1/1';INSERT INTO #Test SELECT 2, NULL, NULL;INSERT INTO #Test SELECT 3, '2/2/2', '3/3/1';INSERT INTO #Test SELECT 4, '3/3/3', '11/1/1'

SELECT t.ID, Date1, Date2, MinDate
FROM #TEST t
    CROSS APPLY (SELECT MIN(d) MinDate FROM (VALUES (Date1), (Date2)) AS a(d)) md
ORDER BY MinDate

DROP TABLE #Test
Liscomb answered 20/4, 2015 at 15:40 Comment(0)
M
2

I think when you want to sort on both fields of date1 and date2, you should have both of them in the ORDER BY part, like this:

SELECT *
FROM aTable
ORDER BY 
    CASE WHEN date1 < date2 THEN date1 
    ELSE date2 END, 
    CASE WHEN date1 < date2 THEN date2 
    ELSE date1 END

Result can be like this:

date1      | date2      
-----------+------------
2015-04-25 | 2015-04-21
2015-04-26 | 2015-04-21
2015-04-25 | 2015-04-22
2015-04-22 | 2015-04-26

To have a prefect result with Null values use:

SELECT *
FROM aTable
ORDER BY 
    CASE 
        WHEN date1 IS NULL THEN NULL
        WHEN date1 < date2 THEN date1 
    ELSE date2 END 
    ,CASE 
        WHEN date2 IS NULL THEN date1
        WHEN date1 IS NULL THEN date2
        WHEN date1 < date2 THEN date2 
    ELSE date1 END

Results will be like this:

date1      | date2      
-----------+------------
NULL       | NULL
NULL       | 2015-04-22
2015-04-26 | NULL
2015-04-25 | 2015-04-21
2015-04-26 | 2015-04-21
2015-04-25 | 2015-04-22
Milks answered 25/4, 2015 at 12:1 Comment(0)
S
2

I'd shift focus from how to do this to why you need this - and propose to change the schema instead. The rule of thumb is: if you need to pull stunts to access your data, there is a bad design decision.

As you've seen, this task is very untypical for SQL so, though it's possible, all the proposed methods are painfully slow in comparison to an ordinary ORDER BY.

  • If you need to do this often then the minimum of the two dates must have some independent physical meaning for your application.
    • Which justifies a separate column (or maybe a column replacing one of the two) - maintained by a trigger or even manually if the meaning is independent enough for the column to possibly be neither in some cases.
Santonin answered 26/4, 2015 at 10:29 Comment(0)
A
1

I would order the rows by this rules:

  1. when both null
  2. when date1 is null
  3. when date 2 is null
  4. min(date1, date2)

To do this a nested case will be simple and efficient (unless the table is very large) according to this post.

SELECT ID, Date1, Date2
FROM YourTable
ORDER BY 
CASE 
  WHEN Date1 IS NULL AND Date2 IS NULL THEN 0
  WHEN Date1 IS NULL THEN 1
  WHEN Date2 IS NULL THEN 2
  ELSE 3 END,
CASE 
  WHEN Date1 < Date2 THEN Date1
  ELSE Date2
END
Aleksandrovsk answered 26/4, 2015 at 10:11 Comment(10)
It doesn't work Operand type clash: int is incompatible with dateExtravert
It did work for me, but I've updated my answer so that it will not be an issue for any sql server version.Aleksandrovsk
Still, It doesn't work. Date1 > Date2 is always false in case one of them is NULL.Extravert
Cases in ORDER BY is not the simplest solution for the question.Extravert
@Extravert I really don't know what you want. Both of the queries work fine on sql server 2012, I don't have a way to test them on other versions but I think they should be OK there as well. meanwhile, seems that most voters do think that cases in order by is the simplest solution to this question, and I happen to be one of them.Aleksandrovsk
Here is the proof: gyazo.com/385ea999676eedee155f9eaa05334899. Try to add more values with empty Date2.Extravert
This looks great. both columns null on the top of the list, column1 nulls right after that, column3 nulls after that, and finally, min value of column1 and column 3 (that is not included for some reason in your image). It's just was I was going for, don't see a problem here.Aleksandrovsk
But the order of dates in the first column? 2023-04-01 -> 2011-04-01 -> 1750-02-01. Here should be the opposite order. Like this: gyazo.com/c8ef6007a8800b70f8dfbd9e776b3608Extravert
I think the mix of your and mine approach for this task is actually the right answer. Thank you for participation! I broke my mind thinking about that:)Extravert
doesn't matter. the OP specifically wrote "I want my data to be sorted by minimal of two dates.". this all boils down to whether or not you should treat null values as smaller then non-null values.Aleksandrovsk
D
1

There's an another option. You can calculate the result column by needed logic and cover the select by external one with ordering by your column. In this case the code will be the following:

select ID, x.Date1, x.Date2
from
(
    select
        ID,
        Date1,
        Date2, 
        SortColumn = case when Date1 < Date2 then Date1 else Date2 end
    from YourTable
) x
order by x.SortColumn

The benefit of this solution is that you can add necessary filtering queries (in the inner select) and still the indexes will be useful.

Deerdre answered 26/4, 2015 at 10:49 Comment(0)
E
0

You can use min function in order by clause:

select * 
from [table] d
order by ( select min(q.t) from (
           select d.date1 t union select d.date2) q
         )

You can also use case statement in order by clause but as you know the result of comparing (> and <) any value (null or none null) with null is not true even if you have setted ansi_nulls to off. so for guaranteeing the sort you wanted, you need to handle nulls, as you know in case clause if the result of a when is true then further when statements are not evaluated so you can say:

select * from [table]
order by case 
           when date1 is null then date2
           when date2 is null then date1 
           when date1<date2 then date1 -- surely date1 and date2 are not null here
           else date2 
         end

Also here are some other solutions if your scenario be different maybe maybe you evaluate the result of comparing multiple columns(or a calculation) inside a separated field and finally order by that calculated field without using any condition inside your order by clause.

Eratosthenes answered 26/4, 2015 at 3:56 Comment(0)
C
0
SELECT ID, Date1, Date2
FROM YourTable
ORDER BY (SELECT TOP(1) v FROM (VALUES (Date1), (Date2)) AS value(v) ORDER BY v)

Very similar to the @dyatchenko answer but without NULL issue

Czechoslovak answered 27/4, 2015 at 8:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.