Postgres группа битовых масок
У меня есть следующие объявленные флаги:
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
Любые советы будут оценены.
Для Крейга
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 ответа
Самый простой - сводный результат
Вот как я подхожу к этому:
-- (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
вам нужно будет сделать это путем генерации "разнесенной" битовой маски, в которую будет скопирована одна входная строка для создания нескольких выходных строк. Это можно сделать с помощью LATERAL
вызов функции в 9.3, или с помощью SRF-in-SELECT в 9.2, или просто путем объединения 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;
Я не думаю, что вам повезет, если вы сделаете это так же эффективно, как сканирование одной таблицы.
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