ORDER BY 中的最后一个案例执行不正确 postgres

Posted

技术标签:

【中文标题】ORDER BY 中的最后一个案例执行不正确 postgres【英文标题】:Last case in ORDER BY executes incorrectly postgres 【发布时间】:2021-12-15 10:25:08 【问题描述】:

我有以下 postgres 存储功能。最后一个 CASE WHEN in ORDER BY 返回顺序不正确的数据。但是,类似的第二个 CASE WHEN 可以按预期工作。 我已经仔细检查了 sort_column 和 sort_direction 是否正确。任何帮助表示赞赏

DB 小提琴:https://dbfiddle.uk/?rdbms=postgres_14&fiddle=32ad76d21a9cb97518b92acf58ddf803

    CREATE OR REPLACE FUNCTION find_experts(BIGINT, _sort_column VARCHAR(256), _sort_direction VARCHAR(256)) 
    RETURNS TABLE (
    user_id BIGINT,
    name VARCHAR(256),
    email VARCHAR(256),
    email_validated BOOLEAN,
    is_verified BOOLEAN,
    rating FLOAT,
    is_promoted BOOLEAN,
    registry_date TIMESTAMP,
    picture VARCHAR(256),
    price FLOAT,
    biography VARCHAR(2028),
    short_bio VARCHAR(1000),
    timeslots VARCHAR(256)[],
    timezone VARCHAR(256)
) AS $$
    SELECT users.user_id, users.name, users.email, users.email_validated, users.is_verified, users.rating, users.is_promoted, users.registry_date,
    user_settings.picture, user_settings.price, user_settings.biography, user_settings.short_bio, user_settings.timeslots, user_settings.timezone
    FROM users 
    LEFT JOIN user_settings ON (users.user_id=user_settings.user_id)
    WHERE users.user_id IN 
    (SELECT user_id FROM users_tech) AND
    active_role='expert' AND price IS NOT NULL
    ORDER BY 
    CASE WHEN _sort_column = 'rating' AND _sort_direction = 'DESC' THEN rating END DESC,
    CASE WHEN _sort_column = 'price' AND _sort_direction = 'ASC' THEN price END ASC, rating ASC, registry_date ASC,
    CASE WHEN _sort_column = 'price' AND _sort_direction = 'DESC' THEN price END DESC, rating DESC, registry_date ASC
    OFFSET ($1 - 1) * 10
    LIMIT 10
$$ LANGUAGE SQL;

函数是这样执行的:

pool.query(`SELECT * FROM find_experts($1, $2, $3)`, [1, 'price', 'DESC'])

实际结果:

250
12
11
102
43
856
21
34
63
85

预期结果:

856
250
102
85
63
43
34
21
12
11

【问题讨论】:

您为什么期待这些结果?我没有看到任何会产生任何结果的数据或 SQL 语句。请创建我们可以使用的东西:dbfiddle.uk/?rdbms=postgres_14 @FrankHeikens 嗨,添加了小提琴:dbfiddle.uk/… 我认为您希望这些选项在单个 case 语句中成为多个选项。目前,您的每个 case 块构成一个单独的 order by 子句,并具有自己的选项。它应该是 order by case when ... then, when ... then else...end offset... 而不是 order by case when...end, case when ...end。你觉得可能是这样吗? 您也可以切换到 plpgsql 函数,并在正确的位置连接您的 _sort_column_sort_direction。 Here 和 here 是相关线程。 @Zegarek 嗨,将订单改为 one 的情况只有在我有一列用于排序时才有效。我也可以指定订单方向。刚刚在这里尝试过:dbfiddle.uk/… 但是,我在 order by 子句中有几列,这里有不同的顺序方向:dbfiddle.uk/… 【参考方案1】:

修复:

您需要将所有选项包装在order by 子句中的case 语句中,以使其按预期工作:

ORDER BY 
    CASE WHEN _sort_column = 'rating' AND _sort_direction = 'DESC' THEN rating END DESC,
    CASE WHEN _sort_column = 'price' AND _sort_direction = 'ASC' THEN price END ASC, 
    CASE WHEN _sort_column = 'price' AND _sort_direction = 'ASC' THEN rating END ASC, 
    CASE WHEN _sort_column = 'price' AND _sort_direction = 'ASC' THEN registry_date END ASC,
    CASE WHEN _sort_column = 'price' AND _sort_direction = 'DESC' THEN price END DESC, 
    CASE WHEN _sort_column = 'price' AND _sort_direction = 'DESC' THEN rating END DESC, 
    CASE WHEN _sort_column = 'price' AND _sort_direction = 'DESC' THEN registry_date END ASC

或者切换到 plpgsql 和动态 sql,只切换到 concatenate your desired order by column 和 direction。 这是你的db<>fiddle,已修复。

说明:

您的 case 语句构成独立的 order by 子句。我想您可能会认为该部分中的每一行都是不同的情况,但它们实际上最终成为order by 的一组半常数列,因此它们的格式如下所示更有意义:

ORDER BY 
    CASE WHEN _sort_column = 'rating' AND _sort_direction = 'DESC' THEN rating END DESC,--order column 1, sometimes skipped
    CASE WHEN _sort_column = 'price' AND _sort_direction = 'ASC' THEN price END ASC, --order column 2, sometimes skipped
    rating ASC,        --order column 3, never skipped
    registry_date ASC, --order column 4, never skipped
    CASE WHEN _sort_column = 'price' AND _sort_direction = 'DESC' THEN price END DESC, --order column 5, sometimes skipped
    rating DESC,       --order column 6, never skipped
    registry_date ASC  --order column 7, never skipped

并且以CASE 开头的每一行要么最终成为您想要的,要么成为null,因为您没有指定else 块。 Order by 将跳过那些评估为 null 的 case 语句,因此在您的示例调用中,它最终会像这样

ORDER BY 
    null::integer DESC, --skipped
    null::integer ASC, --skipped
    rating ASC, 
    registry_date ASC,
    price DESC, 
    rating DESC, 
    registry_date ASC

请注意,PostgreSQL 允许将 order by 中的 caseselect 评估为 null,但不允许将文字转换为可排序的内容。

【讨论】:

能够将其设为单个条件块会很好,但在这种情况下,无法在 when 情况下或在单独的连续 case 内处理方向堵塞。 order by case when true then col1 asc end 导致语法错误,order by case when true then col1 end case when true then asc end 也是如此。

以上是关于ORDER BY 中的最后一个案例执行不正确 postgres的主要内容,如果未能解决你的问题,请参考以下文章

合理使用Order by 重要性

MySQL order by - 排序不正确

ORACLE 两个order by的SQL使用 UNION 或者 UNION ALL 报错 ORA-00933:sql命令未正确结束

一次 group by + order by 性能优化分析

sql子查询 order by失效问题

如果使用 ORDER BY String Column,MySQL 查询需要很长时间才能执行