SELECT DISTINCT + ORDER BY 附加表达式
SELECT DISTINCT + ORDER BY additional expression
我没有使用 PostgreSQL 的经验,我正在将 Rails5+MySQL 应用程序迁移到 Rails5+PostgreSQL,但我遇到了查询问题。
我已经看了一些 questions/answers,但仍然无法解决我的问题。我的问题似乎很荒谬,但我需要在这里寻求帮助!
查询:
SELECT DISTINCT users.* FROM users
INNER JOIN areas_users ON areas_users.user_id = users.id
INNER JOIN areas ON areas.deleted_at IS NULL AND areas.id = areas_users.area_id
WHERE users.deleted_at IS NULL AND users.company_id = 2 AND areas.id IN (2, 4, 5)
ORDER BY CASE WHEN users.id=3 THEN 0 WHEN users.id=5 THEN 1 END, users.id, 1 ASC
运行DBeaver中的查询,returns错误:
SQL Error [42P10]: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
我需要做什么才能将此 SELECT DISTINCT
与此 ORDER BY CASE
一起使用?
好像是错误提示:
for SELECT DISTINCT, ORDER BY expressions must appear in select list
这是一个表达式:
CASE WHEN users.id=3 THEN 0 WHEN users.id=5 THEN 1 END
您不能在执行 SELECT DISTINCT users.* FROM ...
时按它排序,因为这只允许出现在 SELECT
列表中的 ORDER BY
表达式。
通常,DISTINCT
的最佳解决方案是一开始就不要使用它。如果您不复制行,则以后不必对它们进行重复数据删除。参见:
在您的情况下,使用 EXISTS
半连接(表达式/子查询)而不是连接。这避免了重复。假设 table users
中的不同行,DISTINCT
失业。
SELECT u.*
FROM users u
WHERE u.deleted_at IS NULL
AND u.company_id = 2
AND EXISTS (
SELECT FROM areas_users au JOIN areas a ON a.id = au.area_id
WHERE au.user_id = u.id
AND a.id IN (2, 4, 5)
AND a.deleted_at IS NULL
)
ORDER BY CASE u.id WHEN 3 THEN 0
WHEN 5 THEN 1 END, u.id, 1; -- ①
满足您的要求,而且通常速度也快得多。
使用 simple ("switched") CASE
语法。
①还有一点丑。在 ORDER BY
中使用位置引用可以是方便的短语法。但是,虽然您有 SELECT *
,但这是一个非常糟糕的主意。如果底层 table 中的列顺序发生变化,您的查询也会悄无声息地发生变化。拼出此用例中的列!
(通常,您一开始不需要 SELECT *
,只需要选择列。)
如果您的 ID 列保证有正数,这会更快一些:
...
ORDER BY CASE u.id WHEN 3 THEN -2
WHEN 5 THEN -1
ELSE u.id END, <name_of_first_column>
我必须使用 DISTINCT
(真的吗?)如果你坚持:
SELECT DISTINCT CASE u.id WHEN 3 THEN -2 WHEN 5 THEN -1 ELSE u.id END AS order_column, u.*
FROM users u
JOIN areas_users au ON au.user_id = u.id
JOIN areas a ON a.id = au.area_id
WHERE u.deleted_at IS NULL
AND u.company_id = 2
AND a.id IN (2, 4, 5)
AND a.deleted_at IS NULL
ORDER BY 1, <name_of_previously_first_column>; -- now, "ORDER BY 1" is ok
您在结果中获得了额外的列 order_column
。您可以将其包装在具有不同 SELECT
...
的子查询中
只是概念验证。不要使用这个。
或DISTINCT ON
?
SELECT DISTINCT ON (CASE u.id WHEN 3 THEN -2 WHEN 5 THEN -1 ELSE u.id END, <name_of_first_column>)
u.*
FROM users u
JOIN areas_users au ON au.user_id = u.id
JOIN areas a ON a.id = au.area_id
WHERE u.deleted_at IS NULL
AND u.company_id = 2
AND a.id IN (2, 4, 5)
AND a.deleted_at IS NULL
ORDER BY CASE u.id WHEN 3 THEN -2 WHEN 5 THEN -1 ELSE u.id END, <name_of_first_column>;
这可以在不返回附加列的情况下工作。仍然只是概念证明。别用了,EXISTS
查询便宜多了。
参见:
- Select first row in each GROUP BY group?
我没有使用 PostgreSQL 的经验,我正在将 Rails5+MySQL 应用程序迁移到 Rails5+PostgreSQL,但我遇到了查询问题。
我已经看了一些 questions/answers,但仍然无法解决我的问题。我的问题似乎很荒谬,但我需要在这里寻求帮助!
查询:
SELECT DISTINCT users.* FROM users
INNER JOIN areas_users ON areas_users.user_id = users.id
INNER JOIN areas ON areas.deleted_at IS NULL AND areas.id = areas_users.area_id
WHERE users.deleted_at IS NULL AND users.company_id = 2 AND areas.id IN (2, 4, 5)
ORDER BY CASE WHEN users.id=3 THEN 0 WHEN users.id=5 THEN 1 END, users.id, 1 ASC
运行DBeaver中的查询,returns错误:
SQL Error [42P10]: ERROR: for SELECT DISTINCT, ORDER BY expressions must appear in select list
我需要做什么才能将此 SELECT DISTINCT
与此 ORDER BY CASE
一起使用?
好像是错误提示:
for SELECT DISTINCT, ORDER BY expressions must appear in select list
这是一个表达式:
CASE WHEN users.id=3 THEN 0 WHEN users.id=5 THEN 1 END
您不能在执行 SELECT DISTINCT users.* FROM ...
时按它排序,因为这只允许出现在 SELECT
列表中的 ORDER BY
表达式。
通常,DISTINCT
的最佳解决方案是一开始就不要使用它。如果您不复制行,则以后不必对它们进行重复数据删除。参见:
在您的情况下,使用 EXISTS
半连接(表达式/子查询)而不是连接。这避免了重复。假设 table users
中的不同行,DISTINCT
失业。
SELECT u.*
FROM users u
WHERE u.deleted_at IS NULL
AND u.company_id = 2
AND EXISTS (
SELECT FROM areas_users au JOIN areas a ON a.id = au.area_id
WHERE au.user_id = u.id
AND a.id IN (2, 4, 5)
AND a.deleted_at IS NULL
)
ORDER BY CASE u.id WHEN 3 THEN 0
WHEN 5 THEN 1 END, u.id, 1; -- ①
满足您的要求,而且通常速度也快得多。
使用 simple ("switched") CASE
语法。
①还有一点丑。在 ORDER BY
中使用位置引用可以是方便的短语法。但是,虽然您有 SELECT *
,但这是一个非常糟糕的主意。如果底层 table 中的列顺序发生变化,您的查询也会悄无声息地发生变化。拼出此用例中的列!
(通常,您一开始不需要 SELECT *
,只需要选择列。)
如果您的 ID 列保证有正数,这会更快一些:
...
ORDER BY CASE u.id WHEN 3 THEN -2
WHEN 5 THEN -1
ELSE u.id END, <name_of_first_column>
我必须使用 DISTINCT
(真的吗?)如果你坚持:
SELECT DISTINCT CASE u.id WHEN 3 THEN -2 WHEN 5 THEN -1 ELSE u.id END AS order_column, u.*
FROM users u
JOIN areas_users au ON au.user_id = u.id
JOIN areas a ON a.id = au.area_id
WHERE u.deleted_at IS NULL
AND u.company_id = 2
AND a.id IN (2, 4, 5)
AND a.deleted_at IS NULL
ORDER BY 1, <name_of_previously_first_column>; -- now, "ORDER BY 1" is ok
您在结果中获得了额外的列 order_column
。您可以将其包装在具有不同 SELECT
...
只是概念验证。不要使用这个。
或DISTINCT ON
?
SELECT DISTINCT ON (CASE u.id WHEN 3 THEN -2 WHEN 5 THEN -1 ELSE u.id END, <name_of_first_column>)
u.*
FROM users u
JOIN areas_users au ON au.user_id = u.id
JOIN areas a ON a.id = au.area_id
WHERE u.deleted_at IS NULL
AND u.company_id = 2
AND a.id IN (2, 4, 5)
AND a.deleted_at IS NULL
ORDER BY CASE u.id WHEN 3 THEN -2 WHEN 5 THEN -1 ELSE u.id END, <name_of_first_column>;
这可以在不返回附加列的情况下工作。仍然只是概念证明。别用了,EXISTS
查询便宜多了。
参见:
- Select first row in each GROUP BY group?