sql - transporte - ¿Es posible realizar una función de grupo bit a bit?
grupo bit computrabajo (5)
Me interesa encontrar a todos los usuarios que tienen un indicador determinado (por ejemplo, escribir) en CUALQUIER registro
¿Qué pasa con simplemente
SELECT DISTINCT User_ID
FROM Permissions
WHERE permissions & 2 = 2
Tengo un campo en una tabla que contiene banderas bit a bit. Digamos por el bien del ejemplo, hay tres indicadores: 4 => read, 2 => write, 1 => execute
y la tabla se ve así *
:
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)
Estoy interesado en encontrar todos los usuarios que tienen un determinado conjunto de banderas (por ejemplo, escribir) en CUALQUIER registro. Para hacer esto en una consulta, calculé que si ordenaba TODOS los permisos del usuario juntos obtendría un valor único que es la "suma total" de sus permisos:
user_id | all_perms
-----------+-------------
1 | 6 (<-- 6 | 4 = 6)
2 | 5 (<-- 4 | 1 = 5)
*
Mi tabla real no tiene que ver con archivos o permisos de archivos, ''es solo un ejemplo
¿Hay alguna manera de que pueda realizar esto en una declaración? La forma en que lo veo, es muy similar a una función agregada normal con GROUP BY:
SELECT user_id, SUM(permissions) as all_perms
FROM permissions
GROUP BY user_id
... pero obviamente, alguna función mágica de "bit a bit" en lugar de SUM. Alguien sabe de algo como eso?
(Y para obtener puntos de bonificación, ¿funciona en Oracle?)
MySQL:
SELECT user_id, BIT_OR(permissions) as all_perms
FROM permissions
GROUP BY user_id
Y puedes hacerlo a bitwise o con ...
FUNCTION BITOR(x IN NUMBER, y IN NUMBER)
RETURN NUMBER
AS
BEGIN
RETURN x + y - BITAND(x,y);
END;
Ah, otra de esas preguntas donde encuentro la respuesta 5 minutos después de preguntar ... La respuesta aceptada irá a la implementación de MySQL aunque ...
He aquí cómo hacerlo con Oracle, como descubrí en el blog de Radino
Usted crea un objeto ...
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;
/
... y luego define tu propia función agregada
CREATE OR REPLACE FUNCTION bitoragg(x IN NUMBER) RETURN NUMBER
PARALLEL_ENABLE
AGGREGATE USING bitor_impl;
/
Uso:
SELECT user_id, bitoragg(permissions) FROM perms GROUP BY user_id
Necesitaría conocer los posibles componentes de permiso (1, 2 y 4) apriori (por lo tanto, más difícil de mantener), pero esto es bastante simple y funcionaría:
SELECT user_id,
MAX(BITAND(permissions, 1)) +
MAX(BITAND(permissions, 2)) +
MAX(BITAND(permissions, 4)) all_perms
FROM permissions
GROUP BY user_id