Postgres conditional / mixed sorting

Viewed 260

I have a table with a timestamp column, something like this:

╔═══════╦════════════╦
║ name  ║ date_time  ║ 
╠═══════╬════════════╬
║  A    ║     100    ║
║  B    ║     110    ║
║  C    ║     120    ║
║  D    ║     140    ║
║  E    ║     180    ║
║  F    ║     190    ║
╚═══════╩════════════╩

I need to return the records so that the records come in two groups, the records where date_time is in the future are the first group, and the records where date_time is in the past are the second group. The records in the future need to be sorted in ascending order(i.e. the closest to now comes first), and the records in the past need to be sorted in descending order.

I managed to solve this by making two separate queries and joining the results with union all, but this is not very performant and I was curious to know if there was any better approach? Maybe by using a conditional sorting on the order by?

2 Answers

In Postgres you can sort booleans - false values come before true values. So you can sort according to two expressions - a condition that checks if a record is in the past or future, and the absolute distance of it from the current timestamp:

SELECT *
FROM   mytable
ORDER BY date_time < CURRENT_TIMESTAMP, 
         ABS(EXTRACT(EPOCH FROM CURRENT_TIMESTAMP) -
             EXTRACT(EPOCH FROM date_time))

I would do this using three sort keys:

order by (date_time < current_timestamp),
         (case when date_time < current_timestamp then date_time end) desc,
         date_time asc;

Here is a rextester.

Related