3

I have a query that currently brings up data like this:

ID | Name | Arrival  | Departure
-----------------------------------
1  | John | NULL     |  2:30:00
2  | John | NULL     | 11:00:00
3  | John | NULL     | 14:00:00
4  | John | 10:30:00 | 11:00:00
5  | John | 12:00:00 | 13:00:00

This is when I do: order by Name, Arrival, Departure.

However, what I would like instead is the following:

  1. If the Arrival entry is NULL, then rows should be ordered by the Departure column.
  2. If the Arrival entry is NULL AND there are multiple rows with the same Departure data, then it should be ordered after the other rows that have an Arrival entry.

Result:

ID | Name | Arrival  | Departure
-----------------------------------
1  | John | NULL     |  2:30:00
4  | John | 10:30:00 | 11:00:00
2  | John | NULL     | 11:00:00
5  | John | 12:00:00 | 13:00:00
3  | John | NULL     | 14:00:00

Edit: For those who thought this is a duplicate question - originally the questions are not the same due to the second condition. However, because of the way my data is structured (Arrival times are always before Departure times), the same answer is applicable.

Edit 2: Departure can be NULL as well. When Departure is null, the Arrival entry is non-null. If this is the case, it should be ordered by Arrival. If two rows both have the same Arrival entry, then NULLs should be BEFORE non-nulls. Note this is opposite to NULL Arrival entries (condition 2 above), where NULLs are after non-nulls.

painiyff
  • 2,519
  • 6
  • 21
  • 29
  • Possible duplicate of [Conditional ORDER BY depending on column values](http://stackoverflow.com/questions/7464434/conditional-order-by-depending-on-column-values) – Tab Alleman Apr 07 '16 at 19:08
  • The second rule is somewhat unclear to me. Do you mean that if arrival in row 4 would be 11:00:00, it should always go before a row that has null arrival and a 11:00:00 departure? – Joachim Isaksson Apr 07 '16 at 19:14
  • @JoachimIsaksson I mean for rows with Departure values that are the same, then entries with null Arrival values should be below the rows with non-null Arrival values. The reason I mentioned it is because normally NULLs are ordered before non-null values. In the intended results above, row 4 is above row 2, because it has a non-null Arrival value. – painiyff Apr 07 '16 at 22:52
  • @JoachimIsaksson To answer your question, yes. – painiyff Apr 07 '16 at 23:08

5 Answers5

3

Try it with

DECLARE @tbl TABLE(ID INT,Name VARCHAR(100),Arrival TIME,Departure TIME);
INSERT INTO @tbl VALUES
 (1,'John',NULL,'2:30:00')
,(2,'John',NULL,'11:00:00')
,(3,'John',NULL,'14:00:00')
,(4,'John','10:30:00','11:00:00')
,(5,'John','12:00:00','13:00:00');

SELECT * FROM @tbl
ORDER BY Name,CASE WHEN Arrival IS NULL THEN Departure ELSE Arrival END

The result

1   John    NULL                02:30:00.0000000
4   John    10:30:00.0000000    11:00:00.0000000
2   John    NULL                11:00:00.0000000
5   John    12:00:00.0000000    13:00:00.0000000
3   John    NULL                14:00:00.0000000

You might think about ISNULL(), but you should read about "sargable", especially if you deal with many rows and indexes...

Shnugo
  • 66,100
  • 9
  • 53
  • 114
  • Uncredited duplicate of this answer? http://stackoverflow.com/a/7464492/1507566 – Tab Alleman Apr 07 '16 at 19:27
  • There aren't a ton of different ways to accomplish what the OP wanted so it shouldn't be a surprise that another person might have come to the same conclusion. – dazedandconfused Apr 07 '16 at 19:35
  • 2
    I did not vote you down but there is no rule a down vote requires a comment – paparazzo Apr 07 '16 at 20:01
  • @Paparazzi, no, but I think that this is a matter of politness... Anyway, I use down votes very rarely and ever explain why... – Shnugo Apr 07 '16 at 20:03
  • @Shnugo If it is a matter of politeness then it should not be ! with an order. That wording is not a polite request (IMHO). – paparazzo Apr 07 '16 at 20:41
  • @Paparazzi, you might read [this](http://meta.stackexchange.com/questions/135/encouraging-people-to-explain-downvotes). But to be honest: This is not important enough to quarrel about :-) Happy coding! – Shnugo Apr 07 '16 at 20:49
  • @Shnugo I did read it. Did you? Would you not consider "Please consider adding a comment if you think this post can be improved" much more polite than your ! order. – paparazzo Apr 07 '16 at 20:54
  • This wouldn't quite work if the arrival time in row 4 was `11:00:00` instead of `10:30:00`, because then the ordering of row 2 and row 4 is ambiguous. Luckily for me, there isn't a case when the Arrival time is the same as (or behind) the Departure time. So I guess this answer's good! – painiyff Apr 07 '16 at 23:11
  • 1
    @Shnugo, Thank you for providing the script with the test data. – Vladimir Baranov Apr 07 '16 at 23:34
1

How about simple ORDER without conditional expressions and functions.

If you want to order by Departure first, well, order by it. If you want to order by Arrival next, but want NULL last, use DESC.

This will work correctly even if Arrival is the same as Departure, for example, if Arrival was 11:00:00 in row 4 instead of 10:30:00.

I'd like to thank @Shnugo for providing the script with the test data.

DECLARE @tbl TABLE(ID INT,Name VARCHAR(100),Arrival TIME,Departure TIME);
INSERT INTO @tbl VALUES
 (1,'John',NULL,'2:30:00')
,(2,'John',NULL,'11:00:00')
,(3,'John',NULL,'14:00:00')
,(4,'John','10:30:00','11:00:00')
,(5,'John','12:00:00','13:00:00');

SELECT * 
FROM @tbl
ORDER BY
    Name
    ,Departure
    ,Arrival DESC;

Result

+----+------+------------------+------------------+
| ID | Name |     Arrival      |    Departure     |
+----+------+------------------+------------------+
|  1 | John | NULL             | 02:30:00.0000000 |
|  4 | John | 10:30:00.0000000 | 11:00:00.0000000 |
|  2 | John | NULL             | 11:00:00.0000000 |
|  5 | John | 12:00:00.0000000 | 13:00:00.0000000 |
|  3 | John | NULL             | 14:00:00.0000000 |
+----+------+------------------+------------------+

Though... It is not clear if it is possible to have more than one row with non-null Arrival and exactly the same Departure... What ordering you'd like in this case?

Vladimir Baranov
  • 31,799
  • 5
  • 53
  • 90
  • There should not be more than one row with non-null `Arrival` and the same `Departure`. But, one thing which I did not mention in OP is that `Departure` can be null too. If they are null then the respective `Arrival` is non-null. In those cases it should be ordered by `Arrival`. – painiyff Apr 08 '16 at 16:38
  • @painiyff, well, in this case you'd have to use some expression with `CASE` or `ISNULL` or `COALESCE` in the `ORDER BY`. – Vladimir Baranov Apr 08 '16 at 23:48
0

You can do case statements in an order by clause I believe.

ISNULL would be my preferred way to go though because it's specifically built to work with nulls and it's smaller.

ORDER BY Name, ISNULL(Arival, Departure)
Jrud
  • 1,004
  • 9
  • 25
  • No, please read [this](http://stackoverflow.com/questions/799584/what-makes-a-sql-statement-sargable) and [this](http://stackoverflow.com/questions/5024090/sargable-queries-using-isnull-in-tsql), Btw: I'm not the down voter... – Shnugo Apr 07 '16 at 19:16
  • True. Yours is the more complete answer. – Jrud Apr 07 '16 at 19:28
  • So, it is not sargable. It works. +1 I doubt the have a lot of rows in that table. – paparazzo Apr 07 '16 at 19:56
  • Thank you. Sometimes readability is a goal all of its own when it comes to coding. – Jrud Apr 07 '16 at 21:48
0

In order to avoid using conditionals in WHERE clause you can persist your ordering criteria by using a computed column. Also, using TIME type for the Arrival and Departure is more efficient (space, comparisons etc.):

create table Data 
(
    ID INT NOT NULL,
    Name VARCHAR(100) NOT NULL,
    Arrival TIME NULL,
    Departure TIME NULL,
    ActualTime AS ISNULL(Arrival, Departure) PERSISTED
)
GO

INSERT INTO Data (ID, Name, Arrival, Departure)
VALUES
 (1,'John',NULL,'2:30:00')
,(2,'John',NULL,'11:00:00')
,(3,'John',NULL,'14:00:00')
,(4,'John','10:30:00','11:00:00')
,(5,'John','12:00:00','13:00:00');
GO

select * from Data
order by ActualTime

Based on other queries, you can also have an index using ActualTime as a key.

Alexei - check Codidact
  • 22,016
  • 16
  • 145
  • 164
0

To get null to come last I think you can just add

ORDER BY Name 
       , ISNULL(Arival, dateadd(ss, 1, Departure)) 
       , Departure 

but if you have arrival that go into the next departure with nulls then you are going to get some maybe not desired results
you would need to state exactly how you want to deal with that

what if you had

6  | John | 12:30:00 | 11:00:00 

do you really want that after

5  | John | 12:00:00 | 13:00:00

it would be a lot easier to just sort on departure first

declare @bigdate datetime = cast('2099-01-01' as datetime);
ORDER BY Name  
       , Departure 
       , ISNULL(Arival, @bigdate);
paparazzo
  • 44,497
  • 23
  • 105
  • 176