2009-09-09 2 views
14

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

Я заинтересован, чтобы найти всех пользователей, которые имеют определенный набор флагов (например, запись) на любой записи , Для того, чтобы сделать это в одном запросе, я понял, что если вы все разрешения OR-нут пользователя вместе вы получите одно значение, которое является «сумма» их разрешений:

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. Кто-нибудь знает что-нибудь подобное?

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

ответ

14

MySQL:

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

Как всегда, MySQL пинает оракула. – nickf

3

Ах, еще один из тех вопросов, где я нахожу ответ 5 минут после того, как спрашивать ... Принимается ответ будет идти к реализации MySQL, хотя ...

Вот как сделать это с Oracle, как я обнаружил на Radino's blog

Вы создаете объект ...

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; 
/

... а затем define your own aggregate function

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 
2

И вы можете сделать побитовое или с ...

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

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

Что случилось с просто

SELECT DISTINCT User_ID 
FROM Permissions 
WHERE permissions & 2 = 2 
+0

Я думаю, что он не работает (на оракуле). Оператор & не определен. Вместо этого используйте bitand. – Christian13467

+0

@ Christian13467: Я не знаю оракула, но я предполагаю, что предложение where может просто стать * WHERE bitand (permissions, 2) = 2 *? –

+0

Да, ладно - так я плохо сформулировал пример ... выполнение агрегатного поразрядного ИЛИ более полезно в моей реальной ситуации. – nickf

1

Вы должны знать возможные компоненты разрешений (1, 2 и 4) априорные (таким образом труднее поддерживать), но это довольно просто и будет работать:

SELECT user_id, 
     MAX(BITAND(permissions, 1)) + 
     MAX(BITAND(permissions, 2)) + 
     MAX(BITAND(permissions, 4)) all_perms 
FROM permissions 
GROUP BY user_id