Postgres bitmask group by

时间:2013-08-01 01:06:35

标签: postgresql bit-manipulation postgresql-9.2 bitmask

我声明了以下标志:

0 - None
1 - Read
2 - Write
4 - View

我想编写一个查询,该查询将对此位掩码进行分组并获取所使用的每个标志的计数。

person  mask
  a      0
  b      3
  c      7
  d      6

结果应为:

flag        count
 none        1
 read        2
 write       3
 view        2

任何提示都将不胜感激。

对于Craig

SELECT  lea.mask as trackerStatusMask,
        count(*) as count
FROM    Live le 
INNER JOIN (
 ... --some guff
) lea on le.xId = lea.xId
WHERE   le.xId = p_xId   
GROUP BY lea.mask;

2 个答案:

答案 0 :(得分:3)

SQL Fiddle

select
    count(mask = 0 or null) as "None",
    count(mask & 1 > 0 or null) as "Read",
    count(mask & 2 > 0 or null) as "Write",
    count(mask & 4 > 0 or null) as "View"
from t

答案 1 :(得分:2)

最简单 - 透视结果

以下是我接近它的方法:

 -- (after fixing the idiotic mistakes in the first version)
 SELECT
    count(nullif(mask <> 0, True)) AS "none",
    count(nullif(mask & 2,0))      AS "write",
    count(nullif(mask & 1,0))      AS "read",
    count(nullif(mask & 4,0))      AS "view"
FROM my_table;

-- ... though @ClodAldo's version of it below is considerably clearer, per comments.

这不会GROUP BY这样做;相反,它扫描表并一次收集数据,产生面向列的结果。

如果您需要以行格式显示,可以使用crosstab模块中的tablefunc函数或手动来旋转结果。

如果你真的必须GROUP BY,请爆炸位掩码

您不能以简单的方式使用GROUP BY,因为它希望行只属于一个组。您的行显示在多个组中。如果必须使用GROUP BY,则必须通过生成“爆炸”位掩码来执行此操作,其中一个输入行被复制以生成多个输出行。这可以通过9.3中的LATERAL函数调用,或9.2中的SRF-in-SELECT,或者通过简单地对VALUES子句进行连接来完成:

SELECT
   CASE 
     WHEN mask_bit = 1 THEN 'read'
     WHEN mask_bit = 2 THEN 'write'
     WHEN mask_bit = 4 THEN 'view'
     WHEN mask_bit IS NULL THEN 'none'
   END AS "flag",
   count(person) AS "count"
FROM t
LEFT OUTER JOIN (
    VALUES (4),(2),(1)
) mask_bits(mask_bit)
ON (mask & mask_bit = mask_bit)
GROUP BY mask_bit;

我认为你不会有太多运气使得它像单个表扫描一样高效。