Возможно ли выполнить побитную групповую функцию?

У меня есть поле в таблице, которое содержит побитовые флаги. Скажем, для примера есть три флага: 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

... но, очевидно, какая-то магическая "побитовая или" функция вместо SUM. Кто-нибудь знает что-нибудь подобное?

(И для бонусных очков, работает ли он в oracle?)

Ответ 1

MySQL:

SELECT user_id, BIT_OR(permissions) as all_perms
FROM permissions
GROUP BY user_id

Ответ 2

А, еще один из тех вопросов, где я нахожу ответ через 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

Ответ 3

И вы можете поразрядно или с...

FUNCTION BITOR(x IN NUMBER, y IN NUMBER)
RETURN NUMBER
AS
BEGIN
    RETURN x + y - BITAND(x,y);
END;

Ответ 4

Вам нужно будет знать возможные компоненты разрешений (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

Ответ 5

Мне интересно найти всех пользователей, которые имеют определенный флаг (например: запись) на ЛЮБОЙ записи

Что не так просто

SELECT DISTINCT User_ID
FROM Permissions
WHERE permissions & 2 = 2