根据满足多少WHERE条件对查询结果进行排名

时间:2015-10-14 01:49:59

标签: sql postgresql sql-order-by

假设我正在尝试将具有多个条件的查询组合在一起:

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特定的功能有用吗?

1 个答案:

答案 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符合条件,FALSENULL不符合。 我们需要一种技术来计算(或连接)TRUE,同时放弃NULLFALSE(或恰恰相反)。上述表达式将TRUE计为1,将NULLFALSE计为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;

我们可以使用FALSENULL折叠到<expression> OR NULL,然后使用忽略concat()值的NULL。按降序排序&#39; tt&#39;之前排序&#39; t&#39;等:

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

或者许多条件可能最短。我们甚至不需要为每个条件添加括号 - 形成一个数组并将NULLFALSEarray_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;