Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Postgres CASE in ORDER BY using an alias

I have the following query which works great in Postgres 9.1:

SELECT users.id, GREATEST(
 COALESCE(MAX(messages.created_at), '2012-07-25 16:05:41.870117'),
 COALESCE(MAX(phone_calls.created_at), '2012-07-25 16:05:41.870117')
) AS latest_interaction
FROM users LEFT JOIN messages ON users.id = messages.user_id
LEFT JOIN phone_calls ON users.id = phone_calls.user_id
GROUP BY users.id
ORDER BY latest_interaction DESC
LIMIT 5;

But what I want to do is something like this:

SELECT users.id, GREATEST(
 COALESCE(MAX(messages.created_at), '2012-07-25 16:05:41.870117'),
 COALESCE(MAX(phone_calls.created_at), '2012-07-25 16:05:41.870117')
) AS latest_interaction
FROM users LEFT JOIN messages ON users.id = messages.user_id
LEFT JOIN phone_calls ON users.id = phone_calls.user_id
GROUP BY users.id
ORDER BY
  CASE WHEN(
    latest_interaction > '2012-09-05 16:05:41.870117')
  THEN 0
  WHEN(latest_interaction > '2012-09-04 16:05:41.870117')
  THEN 2
  WHEN(latest_interaction > '2012-09-04 16:05:41.870117')
  THEN 3
  ELSE 4
  END
LIMIT 5;

And I get the following error: ERROR: column "latest_interaction" does not exist

It seems like I cannot use the alias for the aggregate latest_interaction in the order by clause with a CASE statement.

Are there any workarounds for this?

like image 502
dwilkie Avatar asked Sep 06 '12 10:09

dwilkie


People also ask

Can I use alias in ORDER BY?

Due to logical query processing order, alias can be used in order by.

Can I use alias in GROUP BY?

You can use an alias for any field or aggregated field in a SELECT statement in a SOQL query. Use a field alias to identify the field when you're processing the query results in your code. Specify the alias directly after the associated field.

How does Postgres ORDER BY default?

ASC order is the default. Ascending order puts smaller values first, where “smaller” is defined in terms of the < operator. Similarly, descending order is determined with the > operator.


2 Answers

Try to wrap it as a subquery:

SELECT * 
FROM 
(
    SELECT users.id, 
        GREATEST(
             COALESCE(MAX(messages.created_at), '2012-07-25 16:05:41.870117'),
             COALESCE(MAX(phone_calls.created_at), '2012-07-25 16:05:41.870117')
        ) AS latest_interaction
        FROM users LEFT JOIN messages ON users.id = messages.user_id
        LEFT JOIN phone_calls ON users.id = phone_calls.user_id
        GROUP BY users.id
) Sub
ORDER BY
  CASE WHEN(
    latest_interaction > '2012-09-05 16:05:41.870117')
  THEN 0
  WHEN(latest_interaction > '2012-09-04 16:05:41.870117')
  THEN 2
  WHEN(latest_interaction > '2012-09-04 16:05:41.870117')
  THEN 3
  ELSE 4
  END
LIMIT 5;
like image 190
Mahmoud Gamal Avatar answered Oct 14 '22 21:10

Mahmoud Gamal


The PG manual says the ORDER BY expression:

Each expression can be the name or ordinal number of an output column (SELECT list item), or it can be an arbitrary expression formed from input-column values.

The sub-query solution from @Mahmoud will work, or you can create the ORDER BY using the original columns messages.created_at or phone_calls.created_at

like image 34
pd40 Avatar answered Oct 14 '22 20:10

pd40