根据满足多少 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

其中 C1Condition1,依此类推。所以,如果你要将每个条件转换为 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符合条件,FALSENULL不符合。
我们需要一种技术来计算(或连接)TRUE,同时丢弃 NULLFALSE(或完全相反)。上面的表达式将 TRUE 计为 1,将 NULLFALSE 计为 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 NULLFALSE 折叠为 NULL,然后使用忽略 NULL 值的 concat()。按降序排序 'tt' 在 't' 之前排序等等:

...    
ORDER  BY concat(
          Condition1 OR NULL
        , Condition2 OR NULL
        , Condition3 OR NULL) DESC;

或者,对于 许多 条件,可能是 最短。我们甚至不需要每个条件都加括号——组成一个数组,把NULLFALSE和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;