Можно ли выполнить побитовую групповую функцию?
У меня есть поле в таблице, которая содержит побитовые флаги. Допустим, для примера есть три флага: 4 => read, 2 => write, 1 => execute
и стол выглядит так*
:
user_id | file | permissions
-----------+--------+---------------
1 | a.txt | 6 ( <-- 6 = 4 + 2 = read + write)
1 | b.txt | 4 ( <-- 4 = 4 = read)
2 | a.txt | 4
2 | c.exe | 1 ( <-- 1 = execute)
Мне интересно найти всех пользователей, у которых установлен какой-либо флаг (например, запись) в ЛЮБОЙ записи. Чтобы сделать это в одном запросе, я решил, что, если бы вы ИЛИ соединили все разрешения пользователя вместе, вы бы получили одно значение, которое является "общей суммой" их разрешений:
user_id | all_perms
-----------+-------------
1 | 6 (<-- 6 | 4 = 6)
2 | 5 (<-- 4 | 1 = 5)
*
Моя фактическая таблица не связана с файлами или правами доступа к файлам, это пример
Есть ли способ, которым я мог бы выполнить это в одном утверждении? На мой взгляд, это очень похоже на обычную агрегатную функцию с GROUP BY:
SELECT user_id, SUM(permissions) as all_perms
FROM permissions
GROUP BY user_id
... но, очевидно, какая-то магическая "побитовая или" функция вместо СУММЫ. Кто-нибудь знает что-нибудь подобное?
(А для бонусных баллов это работает в оракуле?)
5 ответов
MySQL:
SELECT user_id, BIT_OR(permissions) as all_perms
FROM permissions
GROUP BY user_id
Ах, еще один из тех вопросов, где я нахожу ответ через 5 минут после того, как спрашиваю... Принятый ответ перейдет к реализации MySQL, хотя...
Вот как это сделать с Oracle, как я обнаружил в блоге Radino.
Вы создаете объект...
CREATE OR REPLACE TYPE bitor_impl AS OBJECT
(
bitor NUMBER,
STATIC FUNCTION ODCIAggregateInitialize(ctx IN OUT bitor_impl) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateIterate(SELF IN OUT bitor_impl,
VALUE IN NUMBER) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateMerge(SELF IN OUT bitor_impl,
ctx2 IN bitor_impl) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateTerminate(SELF IN OUT bitor_impl,
returnvalue OUT NUMBER,
flags IN NUMBER) RETURN NUMBER
)
/
CREATE OR REPLACE TYPE BODY bitor_impl IS
STATIC FUNCTION ODCIAggregateInitialize(ctx IN OUT bitor_impl) RETURN NUMBER IS
BEGIN
ctx := bitor_impl(0);
RETURN ODCIConst.Success;
END ODCIAggregateInitialize;
MEMBER FUNCTION ODCIAggregateIterate(SELF IN OUT bitor_impl,
VALUE IN NUMBER) RETURN NUMBER IS
BEGIN
SELF.bitor := SELF.bitor + VALUE - bitand(SELF.bitor, VALUE);
RETURN ODCIConst.Success;
END ODCIAggregateIterate;
MEMBER FUNCTION ODCIAggregateMerge(SELF IN OUT bitor_impl,
ctx2 IN bitor_impl) RETURN NUMBER IS
BEGIN
SELF.bitor := SELF.bitor + ctx2.bitor - bitand(SELF.bitor, ctx2.bitor);
RETURN ODCIConst.Success;
END ODCIAggregateMerge;
MEMBER FUNCTION ODCIAggregateTerminate(SELF IN OUT bitor_impl,
returnvalue OUT NUMBER,
flags IN NUMBER) RETURN NUMBER IS
BEGIN
returnvalue := SELF.bitor;
RETURN ODCIConst.Success;
END ODCIAggregateTerminate;
END;
/
... а затем определите свою собственную агрегатную функцию
CREATE OR REPLACE FUNCTION bitoragg(x IN NUMBER) RETURN NUMBER
PARALLEL_ENABLE
AGGREGATE USING bitor_impl;
/
Использование:
SELECT user_id, bitoragg(permissions) FROM perms GROUP BY user_id
И вы можете сделать поразрядно или с...
FUNCTION BITOR(x IN NUMBER, y IN NUMBER)
RETURN NUMBER
AS
BEGIN
RETURN x + y - BITAND(x,y);
END;
Вам нужно будет знать возможные компоненты разрешений (1, 2 и 4) apriori (таким образом, сложнее поддерживать), но это довольно просто и будет работать:
SELECT user_id,
MAX(BITAND(permissions, 1)) +
MAX(BITAND(permissions, 2)) +
MAX(BITAND(permissions, 4)) all_perms
FROM permissions
GROUP BY user_id
Я заинтересован, чтобы найти всех пользователей, у которых установлен определенный флаг (например: запись) в ЛЮБОЙ записи
Что не так просто
SELECT DISTINCT User_ID
FROM Permissions
WHERE permissions & 2 = 2