patternsqlMinor
possible to reference a jsonb_array_length in a where clause
Viewed 0 times
referencejsonb_array_lengthwherepossibleclause
Problem
I am just starting to use some of the jsonb functionality in Postgres 9.4 (would love to move to 9.6) . I have the following query:
but I am unable to access jt_users_count in the where phrase but can like this:
To be honest, this just seems a bit unusual and possibly wasteful. Is there a way for Postgres to only compute this once and use a reference to
SELECT
queryable_type,
jsonb_array_length(as_data->'users_who_like') as js_users_count
FROM
queryables
WHERE
queryable_type = 'Item' and jt_users_count > 1;but I am unable to access jt_users_count in the where phrase but can like this:
SELECT
queryable_type,
jsonb_array_length(as_data->'users_who_like') as js_users_count
FROM
queryables
WHERE
queryable_type = 'Item' and jsonb_array_length(as_data->'users_who_like') > 1
ORDER BY
jsonb_array_length(as_data->'users_who_like') desc;To be honest, this just seems a bit unusual and possibly wasteful. Is there a way for Postgres to only compute this once and use a reference to
jt_users_count?Solution
This is the standard behaviour of SQL. SQL statements are (logically) processed in a certain specified order.
Following that order, the
That's why your second query works, but your first one doesn't.
On the other hand, it is possible to have
Don't be afraid about a certain computation being done twice. It can be either optimized (as some kind of common subexpression elimination optimization) or its impact is completely neglectable in the overall performance of a query.
If you want to really make sure that an expression is not evaluated twice, you can use one (often annoying) feature of PostgreSQL CTE: the fact that they're optimization fences (i.e.: they won't be inlined, or converted to subqueries in the final statement):
This will create a (virtual temporary) table
References:
Following that order, the
WHERE clause is executed before the SELECT one. That's why the WHERE clause cannot access the value of js_users_count: It has not yet been computed.That's why your second query works, but your first one doesn't.
On the other hand, it is possible to have
js_users_count on the ORDER BY, because that clause is processed after the SELECT:SELECT
queryable_type,
jsonb_array_length(as_data->'users_who_like') as js_users_count
FROM
queryables
WHERE
queryable_type = 'Item' and jsonb_array_length(as_data->'users_who_like') > 1
ORDER BY
js_users_count desc;Don't be afraid about a certain computation being done twice. It can be either optimized (as some kind of common subexpression elimination optimization) or its impact is completely neglectable in the overall performance of a query.
If you want to really make sure that an expression is not evaluated twice, you can use one (often annoying) feature of PostgreSQL CTE: the fact that they're optimization fences (i.e.: they won't be inlined, or converted to subqueries in the final statement):
WITH q1 AS
(
SELECT
queryable_type,
jsonb_array_length(as_data->'users_who_like') as js_users_count
FROM
queryables
WHERE
queryable_type = 'Item'
)
SELECT
*
FROM
q1
WHERE
js_users_count > 1
ORDER BY
js_users_count desc;This will create a (virtual temporary) table
q1 with the already computed values of jsonb_array_length. I would only do this kind of thing if the function used takes a very long time to execute (has a very high cost), and there's evidence that it's actually called twice (you can check with some RAISE DEBUG statements in the function itself, if it's been written by you).References:
- SQL Query Order of Operations
- You Can Reference Your Derived SELECT Columns In Your ORDER BY Clause In SQL
- SQL Programming: Understanding the Logical Order of Operations in SELECT statements
- Order of execution of a Query
Code Snippets
SELECT
queryable_type,
jsonb_array_length(as_data->'users_who_like') as js_users_count
FROM
queryables
WHERE
queryable_type = 'Item' and jsonb_array_length(as_data->'users_who_like') > 1
ORDER BY
js_users_count desc;WITH q1 AS
(
SELECT
queryable_type,
jsonb_array_length(as_data->'users_who_like') as js_users_count
FROM
queryables
WHERE
queryable_type = 'Item'
)
SELECT
*
FROM
q1
WHERE
js_users_count > 1
ORDER BY
js_users_count desc;Context
StackExchange Database Administrators Q#177910, answer score: 4
Revisions (0)
No revisions yet.