假设我正在尝试将具有多个条件的查询组合在一起:
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特定的功能有用吗?
答案 0 :(得分:3)
关于如何评估WHERE
条件的评论中的讨论似乎与提出的问题正交 - 对此有简单的解决方案。
根据满足条件的条件数排名查询结果
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
。
有多种方法可以达到相同的效果:
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;
我们可以使用FALSE
将NULL
折叠到<expression> OR NULL
,然后使用忽略concat()
值的NULL
。按降序排序&#39; tt&#39;之前排序&#39; t&#39;等:
...
ORDER BY concat(
Condition1 OR NULL
, Condition2 OR NULL
, Condition3 OR NULL) DESC;
或者许多条件可能最短。我们甚至不需要为每个条件添加括号 - 形成一个数组并将NULL
和FALSE
与array_remove()
一起删除,然后我们可以直接按数组排序:
...
ORDER BY array_remove(array_remove(ARRAY[
Condition1
, Condition2
, Condition3
], NULL), FALSE) DESC;
我们甚至可以在子选择和WHERE
中使用另一个count()
子句(但由于增加的开销,这通常会更慢):
...
ORDER BY (SELECT count(*)
FROM ( VALUES
(Condition1)
, (Condition2)
, (Condition3)
) t(i)
WHERE i) DESC;