根据满足多少 WHERE 条件对查询结果进行排名
Rank query results based on how many of the WHERE conditions are satisfied
假设我正在尝试将具有多个条件的查询放在一起:
select * from t
where (Condition1 OR Condition2 OR Condition3)
我的目标是对结果进行分组和排序,以便:
Group 1 => C1 = true, C2 = true, C3 = true
Group 2 => C1 = true, C2 = true, C3 = false
OR C1 = true, C2 = false, C3 = true
OR C1 = false, C2 = true, C3 = true
Group 3 => C1 = true, C2 = false, C3 = false
OR C1 = false, C2 = true, C3 = false
OR C1 = false, C2 = false, C3 = true
其中 C1
为 Condition1
,依此类推。所以,如果你要将每个条件转换为 1 或 0,1 为真 0 为假,你将得到一个二进制数字列表:
111
110
101
011
100
010
001
请注意,它们并不是简单地从大到小排列的。
我只是有点好奇,因为我认为通常 SQL DBMS 不会费心去查看 OR 链中的其他子句,例如,如果第一个子句被证明是真的。我如何将每个条件的结果逐个记录地转换为可以排序的数字?
我希望解决方案 可扩展 到具有 N 个条件的情况,是否有一个优雅的解决方案?它还必须非常快。我对 Postgres 投入了大量资金。是否有特定于 Postgres 的函数可以提供帮助?
评论中关于如何评估 WHERE
条件的讨论似乎与提出的问题正交 - 对此有简单的解决方案。
Rank query results based on how many of the where conditions are satisfied
SELECT *
FROM tbl
WHERE (Condition1 OR Condition2 OR Condition3)
ORDER BY ((Condition1) IS TRUE)::int
+ ((Condition2) IS TRUE)::int
+ ((Condition3) IS TRUE)::int DESC;
或者使用更详细的 standard syntax for the casting,就像您 (@Isaac) 自己提供的那样:
ORDER BY CAST((Condition1) IS TRUE AS integer)
+ CAST((Condition2) IS TRUE AS integer)
+ CAST((Condition3) IS TRUE AS integer) DESC;
基本原则是这样的:
WHERE
条件是布尔表达式,只有TRUE
符合条件,FALSE
和NULL
不符合。
我们需要一种技术来计算(或连接)TRUE
,同时丢弃 NULL
和 FALSE
(或完全相反)。上面的表达式将 TRUE
计为 1
,将 NULL
或 FALSE
计为 0
.
有多种方法可以达到相同的结果:
A CASE
表达式稍微冗长,但通常 最快:
...
ORDER BY (CASE WHEN Condition1 THEN 1 ELSE 0 END
+ CASE WHEN Condition2 THEN 1 ELSE 0 END
+ CASE WHEN Condition3 THEN 1 ELSE 0 END) DESC;
我们可以使用 <expression> OR NULL
将 FALSE
折叠为 NULL
,然后使用忽略 NULL
值的 concat()
。按降序排序 'tt' 在 't' 之前排序等等:
...
ORDER BY concat(
Condition1 OR NULL
, Condition2 OR NULL
, Condition3 OR NULL) DESC;
或者,对于 许多 条件,可能是 最短。我们甚至不需要每个条件都加括号——组成一个数组,把NULL
和FALSE
和witharray_remove()
去掉,我们就可以直接按数组排序了:
...
ORDER BY array_remove(array_remove(ARRAY[
Condition1
, Condition2
, Condition3
], NULL), FALSE) DESC;
我们甚至可以在子选择和 count()
中使用另一个 WHERE
子句(但由于增加了开销,这通常较慢):
...
ORDER BY (SELECT count(*)
FROM ( VALUES
(Condition1)
, (Condition2)
, (Condition3)
) t(i)
WHERE i) DESC;
假设我正在尝试将具有多个条件的查询放在一起:
select * from t
where (Condition1 OR Condition2 OR Condition3)
我的目标是对结果进行分组和排序,以便:
Group 1 => C1 = true, C2 = true, C3 = true
Group 2 => C1 = true, C2 = true, C3 = false
OR C1 = true, C2 = false, C3 = true
OR C1 = false, C2 = true, C3 = true
Group 3 => C1 = true, C2 = false, C3 = false
OR C1 = false, C2 = true, C3 = false
OR C1 = false, C2 = false, C3 = true
其中 C1
为 Condition1
,依此类推。所以,如果你要将每个条件转换为 1 或 0,1 为真 0 为假,你将得到一个二进制数字列表:
111
110
101
011
100
010
001
请注意,它们并不是简单地从大到小排列的。
我只是有点好奇,因为我认为通常 SQL DBMS 不会费心去查看 OR 链中的其他子句,例如,如果第一个子句被证明是真的。我如何将每个条件的结果逐个记录地转换为可以排序的数字?
我希望解决方案 可扩展 到具有 N 个条件的情况,是否有一个优雅的解决方案?它还必须非常快。我对 Postgres 投入了大量资金。是否有特定于 Postgres 的函数可以提供帮助?
评论中关于如何评估 WHERE
条件的讨论似乎与提出的问题正交 - 对此有简单的解决方案。
Rank query results based on how many of the where conditions are satisfied
SELECT *
FROM tbl
WHERE (Condition1 OR Condition2 OR Condition3)
ORDER BY ((Condition1) IS TRUE)::int
+ ((Condition2) IS TRUE)::int
+ ((Condition3) IS TRUE)::int DESC;
或者使用更详细的 standard syntax for the casting,就像您 (@Isaac) 自己提供的那样:
ORDER BY CAST((Condition1) IS TRUE AS integer)
+ CAST((Condition2) IS TRUE AS integer)
+ CAST((Condition3) IS TRUE AS integer) DESC;
基本原则是这样的:
WHERE
条件是布尔表达式,只有TRUE
符合条件,FALSE
和NULL
不符合。
我们需要一种技术来计算(或连接)TRUE
,同时丢弃 NULL
和 FALSE
(或完全相反)。上面的表达式将 TRUE
计为 1
,将 NULL
或 FALSE
计为 0
.
有多种方法可以达到相同的结果:
A CASE
表达式稍微冗长,但通常 最快:
...
ORDER BY (CASE WHEN Condition1 THEN 1 ELSE 0 END
+ CASE WHEN Condition2 THEN 1 ELSE 0 END
+ CASE WHEN Condition3 THEN 1 ELSE 0 END) DESC;
我们可以使用 <expression> OR NULL
将 FALSE
折叠为 NULL
,然后使用忽略 NULL
值的 concat()
。按降序排序 'tt' 在 't' 之前排序等等:
...
ORDER BY concat(
Condition1 OR NULL
, Condition2 OR NULL
, Condition3 OR NULL) DESC;
或者,对于 许多 条件,可能是 最短。我们甚至不需要每个条件都加括号——组成一个数组,把NULL
和FALSE
和witharray_remove()
去掉,我们就可以直接按数组排序了:
...
ORDER BY array_remove(array_remove(ARRAY[
Condition1
, Condition2
, Condition3
], NULL), FALSE) DESC;
我们甚至可以在子选择和 count()
中使用另一个 WHERE
子句(但由于增加了开销,这通常较慢):
...
ORDER BY (SELECT count(*)
FROM ( VALUES
(Condition1)
, (Condition2)
, (Condition3)
) t(i)
WHERE i) DESC;