I have some NativeQuery in my code which looks like this and where "stateValue" will be calculated and can be 1 or 0:
SELECT
MAX(CASE
WHEN(? < t.someDate + interval '3 months' ) THEN 1
ELSE 0
END) AS stateValue
FROM
my_table t
ORDER BY
CASE WHEN LOWER(:sortColumn) = 'status' AND LOWER(:sortDirection) = 'asc' THEN stateValue END ASC,
CASE WHEN LOWER(:sortColumn) = 'status' AND LOWER(:sortDirection) = 'desc' THEN stateValue END DESC,
I would like to sort by "stateValue" column but if I’m trying to use "stateValue" in ORDER BY I’m getting error
- "ERROR: column "statevalue" does not exist"
any idea how to fix it and do properly ? Thanks!
2
Answers
Regardless of what your query is returning (see @Bruno comment), You need to wrap your query within a subquery or a cte, consider the following simplification:
The following will raise an error (column "sd" does not exist):
While the following would work:
demo
The query in your question on has one column, which is a aggregated expression, so you’ll only get 1 row.
Assuming you have more columns to group by, for example:
If you don’t want to repeat the full
MAX(CASE ...)
expression in theORDER BY
clause itself (which is indeed a good idea), you could use a CTE as @ahmed suggested:That might work, but I’d consider it very odd.
What’s odd is that you’re using a workaround to use parameter values to do the job of identifiers (the sort column name) and keywords (
ASC
andDESC
).(Detailed example below…)
Sure, it works, but it can make your SQL unnecessarily complex. I’d suspect it may also confuse the query planner in some cases.
I’m not sure what language you’re using to build this query, but you’re visibly using something outside SQL itself (maybe Java/Hibernate).
While you should indeed always use query parameters and not concatenate strings when you want to pass values into a SQL query (to avoid SQL injections), here you can actually test the sort column and sort order outside the SQL query, when you build it.
sortColumn
andsortOrder
don’t need to be used as values in your SQL query (and you’re literally comparing them to literals within the query you’ve constructed at the moment).The main distinction here is that
sortDirection
andsortOrder
will never be or refer to any values in your tables or expressions. Rather, they’re used to determine which identifiers and keywords (ASC
/DESC
) need to be used in the query.Of course, make sure you don’t concatenate these variables directly into the query string (again, to avoid SQL injection), but there’s nothing wrong with using external code to use literals to build identifiers in your query string.
Detailed example:
Essentially, you’re relying on the fact that,
LOWER(:sortColumn) = 'status' AND LOWER(:sortDirection) = 'asc'
is false,CASE WHEN LOWER(:sortColumn) = 'status' AND LOWER(:sortDirection) = 'asc' THEN stateValue END
isNULL
,CASE WHEN LOWER(:sortColumn) = 'status' AND LOWER(:sortDirection) = 'desc' THEN stateValue END DESC
Assuming your example looks like this:
Expanded, when
sortDirection
isasc
, it looks like this:Here, the first order expression is used and all the values in the second one are null, so it’s irrelevant.
When
sortDirection
isdesc
Here, the values in the first order expression are all null so treated at the same leve, so the values in the second expression are then used.