Postgresql如何排序使用“CASE”计算的列,什么时候”

jckbn6z7  于 2023-06-22  发布在  PostgreSQL
关注(0)|答案(2)|浏览(146)

我的代码中有一些NativeQuery,看起来像这样,其中“stateValue”将被计算,可以是1或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,

我想排序的“状态值”列,但如果我试图使用“状态值”在ORDER BY我得到错误

*“错误:列“statevalue”不存在”

有什么办法能把它修好吗?谢谢!

zte4gxcn

zte4gxcn1#

您的问题中的查询有一列,这是一个聚合表达式,因此您将只得到1行。
假设您有更多的列作为分组依据,例如:

SELECT category, 
       MAX(CASE 
           WHEN(? < t.someDate + interval '3 months' ) THEN 1 
           ELSE 0 
       END) AS stateValue
    FROM
        my_table t 
    GROUP BY category

如果你不想在ORDER BY子句中重复完整的MAX(CASE ...)表达式(这确实是个好主意),你可以使用CTE,就像@ahmed建议的那样:

WITH cte AS (
    SELECT category, 
       MAX(CASE 
           WHEN(? < t.someDate + interval '3 months' ) THEN 1 
           ELSE 0 
       END) AS stateValue
    FROM
        my_table t 
    GROUP BY category
)
SELECT category, stateValue
    FROM cte
    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

这可能有用,但我认为这很奇怪。奇怪的是,您使用了一种变通方法,使用参数值来完成标识符(排序列名)和关键字(ASCDESC)的工作。
(* 详细示例如下... *)
当然,它可以工作,但它可能会使您的SQL变得不必要的复杂。我怀疑它在某些情况下也会混淆查询规划器。
我不确定您使用的是什么语言来构建这个查询,但您显然使用了SQL本身之外的东西(可能是Java/Hibernate)。
当你想把值传递到SQL查询中时,你确实应该总是使用查询参数,而不是连接字符串(以避免SQL注入),在这里,你可以在构建SQL查询时,在SQL查询外部测试排序列和排序顺序。
sortColumnsortOrder不需要在SQL查询中作为值使用(并且您实际上正在将它们与当前构造的查询中的文字进行比较)。

query = """
WITH cte AS (
  SELECT category, MAX(val) AS stateValue
     FROM table1
     GROUP BY category
)
SELECT *
   FROM cte
""";

if ("status".equalsIgnoreCase(sortColumn)) {
    query += " ORDER BY stateValue";
    if ("desc".equalsIgnoreCase(sortOrder)) {
        query += " DESC";
    }
}

这里的主要区别是sortDirectionsortOrder永远不会是或引用表或表达式中的任何。相反,它们用于确定查询中需要使用哪些标识符关键字ASC/DESC)。

当然,请确保不要将这些变量直接连接到查询字符串中(同样,为了避免SQL注入),但是使用外部代码使用 literals 在查询字符串中构建标识符并没有错。

详细示例:
基本上,你依赖于这样一个事实,

  • LOWER(:sortColumn) = 'status' AND LOWER(:sortDirection) = 'asc'为假时,
  • CASE WHEN LOWER(:sortColumn) = 'status' AND LOWER(:sortDirection) = 'asc' THEN stateValue ENDNULL
  • 所以第一个排序表达式为null,它将依赖第二个排序表达式来执行排序CASE WHEN LOWER(:sortColumn) = 'status' AND LOWER(:sortDirection) = 'desc' THEN stateValue END DESC

假设你的例子看起来像这样:

CREATE TABLE table1 (
    id SERIAL PRIMARY KEY,
    category TEXT NOT NULL,
    val INTEGER
);

INSERT INTO table1(category,val)
VALUES ('A', 1), ('A', 10), ('A', 15),
       ('B', 4), ('B', 9), ('B', 25);

扩展后,当sortDirectionasc时,它看起来像这样:

WITH cte AS (
  SELECT category, MAX(val) AS m
     FROM table1
     GROUP BY category
)
SELECT *,
       CASE WHEN LOWER('status')='status' AND LOWER('asc')='asc' THEN m END,
       CASE WHEN LOWER('status')='status' AND LOWER('asc')='desc' THEN m END
   FROM cte
ORDER BY
   CASE WHEN LOWER('status')='status' AND LOWER('asc')='asc' THEN m END ASC,
   CASE WHEN LOWER('status')='status' AND LOWER('asc')='desc' THEN m END DESC

| 类别|m|案例|案例|
| - -----|- -----|- -----|- -----|
| 一个|十五|十五|(空)|
| B|二十五|二十五|(空)|
这里,使用了一阶表达式,而第二阶表达式中的所有值都为null,因此它是不相关的。
sortDirectiondesc

WITH cte AS (
  SELECT category, MAX(val) AS m
     FROM table1
     GROUP BY category
)
SELECT *,
       CASE WHEN LOWER('status')='status' AND LOWER('desc')='asc' THEN m END,
       CASE WHEN LOWER('status')='status' AND LOWER('desc')='desc' THEN m END
   FROM cte
ORDER BY
   CASE WHEN LOWER('status')='status' AND LOWER('desc')='asc' THEN m END ASC,
   CASE WHEN LOWER('status')='status' AND LOWER('desc')='desc' THEN m END DESC

| 类别|m|案例|案例|
| - -----|- -----|- -----|- -----|
| B|二十五|(空)|二十五|
| 一个|十五|(空)|十五|
这里,第一阶表达式中的值都是空的,因此在相同的级别处理,因此然后使用第二表达式中的值。

xqk2d5yq

xqk2d5yq2#

不管你的查询返回什么(见@Bruno评论),你需要在一个子查询或cte中 Package 你的查询,考虑以下简化:
以下内容将引发错误(列“sd”不存在):

select someDate sd 
from my_table
order by case when 1=1 then sd end desc

以下方法可行:

with t as
(
  select someDate sd 
  from my_table
)
select sd
from t
order by case when 1=1 then sd end desc

demo

相关问题