BIT_AND / BIT_OR / BIT_XOR
MySQL incluye tres funciones de agregación que operan a nivel de bits: BIT_AND, BIT_OR y BIT_XOR. A diferencia de SUM o AVG que trabajan con valores numéricos como cantidades, estas funciones operan sobre la representación binaria de los números, combinando los bits individuales de cada valor del grupo. Aunque son menos conocidas que las funciones de agregación clásicas, resultan muy útiles cuando trabajas con campos de permisos, máscaras de bits, banderas de configuración o cualquier sistema que codifique información en bits individuales.
Sintaxis
BIT_AND(expresion)
BIT_OR(expresion)
BIT_XOR(expresion)Las tres funciones aceptan una expresión numérica entera y devuelven un entero sin signo de 64 bits (BIGINT UNSIGNED). Todas ignoran los valores NULL.
Para entender qué hace cada una, veamos cómo operan a nivel de bits individuales:
BIT_AND devuelve un 1 en cada posición de bit solo si todos los valores del grupo tienen un 1 en esa posición. Es la operación AND lógica aplicada bit a bit. El resultado identifica los bits que están activos en todos los valores del grupo.
BIT_OR devuelve un 1 en cada posición de bit si al menos un valor del grupo tiene un 1 en esa posición. Es la operación OR lógica. El resultado identifica los bits que están activos en cualquiera de los valores del grupo.
BIT_XOR devuelve un 1 en cada posición de bit si un número impar de valores del grupo tienen un 1 en esa posición. Es la operación XOR (OR exclusivo).
Comportamiento básico
Veamos las tres funciones con un ejemplo simple usando valores pequeños para poder visualizar los bits:
SELECT
BIT_AND(valor) AS resultado_and,
BIT_OR(valor) AS resultado_or,
BIT_XOR(valor) AS resultado_xor
FROM (
SELECT 5 AS valor -- binario: 101
UNION ALL SELECT 7 -- binario: 111
UNION ALL SELECT 5 -- binario: 101
) datos;| resultado_and | resultado_or | resultado_xor |
|---|---|---|
| 5 | 7 | 7 |
Analicemos bit a bit:
5 = 101
7 = 111
5 = 101
-------
AND = 101 = 5 (solo las posiciones donde TODOS tienen 1)
OR = 111 = 7 (las posiciones donde ALGUNO tiene 1)
XOR = 111 = 7 (posiciones con número impar de 1s)
Para el BIT_AND, solo la primera y tercera posición están activas en todos los valores, así que el resultado es 101 (5). Para BIT_OR, las tres posiciones tienen al menos un 1, así que el resultado es 111 (7). Para BIT_XOR, la primera posición tiene tres unos (impar), la segunda tiene uno (impar) y la tercera tiene tres (impar), así que el resultado es 111 (7).
Caso práctico: permisos con máscaras de bits
El caso de uso más común para estas funciones es trabajar con sistemas de permisos basados en bits. Imagina que cada permiso se representa con un bit diferente:
-- Definición de permisos:
-- 1 (001) = lectura
-- 2 (010) = escritura
-- 4 (100) = ejecuciónSupongamos que tienes una tabla roles_usuario donde cada usuario tiene un campo permisos que almacena una máscara de bits:
SELECT
usuario_id,
permisos,
BIN(permisos) AS permisos_binario,
IF(permisos & 1, 'Sí', 'No') AS lectura,
IF(permisos & 2, 'Sí', 'No') AS escritura,
IF(permisos & 4, 'Sí', 'No') AS ejecucion
FROM roles_usuario
WHERE grupo = 'desarrollo';| usuario_id | permisos | permisos_binario | lectura | escritura | ejecucion |
|---|---|---|---|---|---|
| 1 | 7 | 111 | Sí | Sí | Sí |
| 2 | 5 | 101 | Sí | No | Sí |
| 3 | 3 | 11 | Sí | Sí | No |
| 4 | 7 | 111 | Sí | Sí | Sí |
| 5 | 1 | 1 | Sí | No | No |
Ahora podemos usar BIT_AND para encontrar los permisos comunes a todos los miembros del grupo:
SELECT
BIT_AND(permisos) AS permisos_comunes,
BIN(BIT_AND(permisos)) AS comunes_binario,
IF(BIT_AND(permisos) & 1, 'Sí', 'No') AS todos_leen,
IF(BIT_AND(permisos) & 2, 'Sí', 'No') AS todos_escriben,
IF(BIT_AND(permisos) & 4, 'Sí', 'No') AS todos_ejecutan
FROM roles_usuario
WHERE grupo = 'desarrollo';| permisos_comunes | comunes_binario | todos_leen | todos_escriben | todos_ejecutan |
|---|---|---|---|---|
| 1 | 1 | Sí | No | No |
Solo el bit de lectura (1) está activo en todos los usuarios, así que BIT_AND devuelve 1. Esto significa que el permiso de lectura es el único que comparten todos los miembros del grupo.
Caso práctico: encontrar permisos presentes en cualquier miembro
BIT_OR encuentra los permisos que tiene al menos un miembro del grupo:
SELECT
grupo,
BIT_OR(permisos) AS cualquier_permiso,
BIN(BIT_OR(permisos)) AS permisos_binario,
IF(BIT_OR(permisos) & 1, 'Sí', 'No') AS alguien_lee,
IF(BIT_OR(permisos) & 2, 'Sí', 'No') AS alguien_escribe,
IF(BIT_OR(permisos) & 4, 'Sí', 'No') AS alguien_ejecuta
FROM roles_usuario
GROUP BY grupo;| grupo | cualquier_permiso | permisos_binario | alguien_lee | alguien_escribe | alguien_ejecuta |
|---|---|---|---|---|---|
| desarrollo | 7 | 111 | Sí | Sí | Sí |
| marketing | 3 | 11 | Sí | Sí | No |
| soporte | 1 | 1 | Sí | No | No |
BIT_OR devuelve 7 (111) para desarrollo, lo que significa que entre todos los miembros se cubren los tres permisos. Marketing tiene lectura y escritura pero nadie ejecuta. Soporte solo tiene lectura.
Caso práctico: banderas de características de productos
Otro uso práctico es con banderas de características. Supongamos que cada producto tiene un campo caracteristicas donde cada bit representa una característica:
-- Definición de características:
-- 1 = envío gratuito
-- 2 = producto destacado
-- 4 = disponible online
-- 8 = disponible en tienda
-- 16 = oferta especialPara ver qué características están presentes en cada categoría:
SELECT
c.nombre AS categoria,
BIT_OR(p.caracteristicas) AS todas_caracteristicas,
IF(BIT_OR(p.caracteristicas) & 1, 'Sí', '-') AS envio_gratis,
IF(BIT_OR(p.caracteristicas) & 2, 'Sí', '-') AS destacado,
IF(BIT_OR(p.caracteristicas) & 4, 'Sí', '-') AS online,
IF(BIT_OR(p.caracteristicas) & 8, 'Sí', '-') AS tienda,
IF(BIT_OR(p.caracteristicas) & 16, 'Sí', '-') AS oferta
FROM productos p
JOIN categorias c ON p.categoria_id = c.id
GROUP BY c.nombre;| categoria | todas_caracteristicas | envio_gratis | destacado | online | tienda | oferta |
|---|---|---|---|---|---|---|
| Smartphones | 31 | Sí | Sí | Sí | Sí | Sí |
| Portátiles | 15 | Sí | Sí | Sí | Sí | - |
| Accesorios | 13 | Sí | - | Sí | Sí | - |
| Libros | 5 | Sí | - | Sí | - | - |
La categoría Smartphones tiene todas las características (31 = 11111 en binario), mientras que Libros solo tiene envío gratuito y disponibilidad online.
Para encontrar las características comunes a todos los productos de cada categoría:
SELECT
c.nombre AS categoria,
BIT_AND(p.caracteristicas) AS caracteristicas_comunes,
IF(BIT_AND(p.caracteristicas) & 4, 'Todos online', 'No todos') AS disponibilidad_online
FROM productos p
JOIN categorias c ON p.categoria_id = c.id
GROUP BY c.nombre;Caso práctico: BIT_XOR para detectar cambios
BIT_XOR tiene un uso interesante para detectar si un valor cambió un número par o impar de veces, o para calcular checksums simples:
SELECT
BIT_XOR(id) AS checksum_simple
FROM productos;| checksum_simple |
|---|
| 23 |
Si eliminas o modificas un registro, el checksum cambiará. No es un mecanismo de seguridad robusto, pero sirve como verificación rápida de integridad.
Otra propiedad útil de XOR: si aplicas XOR dos veces con el mismo valor, vuelves al original. Esto significa que BIT_XOR puede detectar valores duplicados:
SELECT
BIT_XOR(cantidad) AS xor_cantidades
FROM detalle_pedidos
WHERE pedido_id = 142;Si todas las cantidades son iguales y hay un número par de líneas, el resultado será 0. Si hay un número impar, será el valor mismo de la cantidad.
Manejo de NULL
Las tres funciones ignoran los valores NULL. Si todas las filas tienen NULL, el comportamiento es específico: BIT_AND devuelve 18446744073709551615 (todos los bits a 1, que es el valor máximo de BIGINT UNSIGNED), BIT_OR devuelve 0 (todos los bits a 0), y BIT_XOR devuelve 0:
SELECT
BIT_AND(NULL) AS and_null,
BIT_OR(NULL) AS or_null,
BIT_XOR(NULL) AS xor_null;| and_null | or_null | xor_null |
|---|---|---|
| 18446744073709551615 | 0 | 0 |
Estos son los valores de identidad para cada operación: AND con todos los bits a 1 no cambia el resultado al combinar con otros valores, y OR/XOR con 0 tampoco.
Combinación con otras funciones
Puedes combinar las funciones de bits con GROUP BY y HAVING para consultas sofisticadas. Por ejemplo, encontrar los grupos donde todos los miembros tienen al menos el permiso de lectura:
SELECT grupo
FROM roles_usuario
GROUP BY grupo
HAVING BIT_AND(permisos) & 1 = 1;O encontrar los grupos donde ningún miembro tiene permiso de ejecución:
SELECT grupo
FROM roles_usuario
GROUP BY grupo
HAVING BIT_OR(permisos) & 4 = 0;La primera consulta usa BIT_AND para verificar que todos tienen un bit activo. La segunda usa BIT_OR para verificar que ninguno tiene un bit activo. Este patrón de combinar la agregación de bits con operaciones de enmascaramiento (&) es la base de los sistemas de permisos basados en bits.
En la siguiente sección veremos las funciones de ventana, que permiten realizar cálculos sobre conjuntos de filas relacionadas.
Escrito por Eduardo Lázaro
