Question

I have the following flags declared:

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

I want to write a query that will group on this bitmask and get the count of each flag used.

person  mask
  a      0
  b      3
  c      7
  d      6

The result should be:

flag        count
 none        1
 read        2
 write       3
 view        2

Any tips would be appreciated.

For 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;
Was it helpful?

Solution 2

Simplest - pivoted result

Here's how I'd approach it:

 -- (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.

This doesn't do a GROUP BY as such; instead it scans the table and collects the data in a single pass, producing column-oriented results.

If you need it in row form you can pivot the result, either using the crosstab function from the tablefunc module or by hand.

If you really must GROUP BY, explode the bitmask

You cannot use GROUP BY for this in a simple way, because it expects rows to fall into exactly one group. Your rows appear in multiple groups. If you must use GROUP BY you will have to do so by generating an "exploded" bitmask where one input row gets copied to produce multiple output rows. This can be done with a LATERAL function invocation in 9.3, or with a SRF-in-SELECT in 9.2, or by simply doing a join on a VALUES clause:

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;

I don't think you'll have much luck making this as efficient as a single table scan, though.

OTHER TIPS

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top