BOOL_AND y BOOL_OR son funciones de agregacion sobre una columna booleana. La primera responde a "?todas las filas del grupo son verdaderas?", la segunda a "?hay al menos una verdadera?". Son la forma directa de preguntar cosas como "?todos los pedidos de este grupo cumplen?" o "?algun usuario es admin?", sin la gimnasia de COUNT(*) = COUNT(...).
Que devuelven BOOL_AND y BOOL_OR
Ambas reciben una expresion booleana y pliegan el grupo en un unico valor:
BOOL_AND(expr) es verdadero solo cuando expr es verdadero en todas las filas del grupo.
BOOL_OR(expr) es verdadero cuando expr es verdadero en al menos una fila.
SELECT
BOOL_AND(active) AS all_active,
BOOL_OR(is_admin) AS has_admin
FROM users;
Lo normal es usarlas junto a un GROUP BY. Por pais, de un vistazo ves donde todas las cuentas estan activas y donde hay al menos un administrador:
SELECT
country,
BOOL_AND(active) AS everyone_active,
BOOL_OR(is_admin) AS any_admin
FROM users
GROUP BY country;
La expresion no tiene que ser una columna booleana ya hecha; sirve cualquier comparacion. Asi se comprueba que todos los pedidos de un usuario son grandes:
SELECT
user_id,
BOOL_AND(amount >= 100) AS all_big_orders
FROM orders
GROUP BY user_id;
Los NULL se ignoran (y un grupo vacio da NULL)
Como el resto de agregados, BOOL_AND y BOOL_OR saltan las filas donde la expresion es NULL. El resultado se calcula solo sobre los valores booleanos "conocidos":
BOOL_AND mira solo las filas no NULL; un unico NULL no fuerza el resultado a falso.
BOOL_OR se apoya igualmente en las filas no NULL.
- Si todos los valores del grupo son
NULL (o no hay filas), el resultado es NULL, no true/false.
SELECT
user_id,
BOOL_AND(status = 'shipped') AS all_shipped
FROM orders
GROUP BY user_id;
Trampa: la regla "todo NULL produce NULL" se escapa con facilidad. Un filtro WHERE all_shipped = true descarta en silencio los grupos cuyo estado nunca se rellena, porque para ellos el predicado es NULL, no false. Si esos grupos deben contar como "no cumplidos", envuelve el resultado: COALESCE(BOOL_AND(status = 'shipped'), false).
BOOL_AND frente a EVERY
En PostgreSQL, EVERY es el sinonimo del SQL estandar para BOOL_AND, identico en comportamiento. No existe un agregado ANY simetrico en el estandar (ANY esta reservado para subconsultas), asi que BOOL_OR cubre el caso de "al menos una".
SELECT
dept,
EVERY(salary >= 50000) AS all_well_paid,
BOOL_OR(salary >= 200000) AS any_top_earner
FROM employees
GROUP BY dept;
Elegir entre EVERY y BOOL_AND es cuestion de estilo y portabilidad: EVERY esta mas cerca del estandar, mientras que BOOL_AND se lee mejor junto a BOOL_OR. Un patron util es una compuerta de grupo entero en HAVING, por ejemplo departamentos donde cada empleado gana al menos un umbral:
SELECT dept
FROM employees
GROUP BY dept
HAVING BOOL_AND(salary >= 50000);
Emularlo en MySQL y ClickHouse
MySQL no tiene BOOL_AND/BOOL_OR nativos, pero alli un booleano es 0/1, asi que MIN y MAX dan el mismo resultado: el minimo de las banderas es 1 solo cuando todas son 1 (eso es AND), y el maximo es 1 cuando alguna es 1 (eso es OR).
SELECT
user_id,
MIN(amount >= 100) AS all_big_orders,
MAX(status = 'paid') AS any_paid
FROM orders
GROUP BY user_id;
El mismo truco funciona en PostgreSQL si primero conviertes el booleano a entero: MIN((amount >= 100)::int). Algunas diferencias a tener presentes:
- En MySQL la comparacion
amount >= 100 ya devuelve 1/0, asi que no hace falta cast explicito. En PostgreSQL anade ::int, porque MIN/MAX no aceptan un boolean directamente.
- El
MIN/MAX de MySQL tambien ignora los NULL, asi que el comportamiento ante grupos vacios (salida NULL) coincide.
- ClickHouse tiene
min/max nativos sobre UInt8, ademas de minOrNull/maxOrNull; no hay un bool_and directo, asi que se escribe min(amount >= 100) y max(status = 'paid').
En resumen: para las comprobaciones de "todo el grupo cumple una condicion" y "al menos uno cumple", usa BOOL_AND/BOOL_OR (o EVERY) en PostgreSQL, recordando que ignoran NULL y devuelven NULL en un grupo totalmente vacio; en otros motores, el mismo sentido se construye con MIN/MAX de una bandera booleana.
BOOL_ANDyBOOL_ORson funciones de agregacion sobre una columna booleana. La primera responde a "?todas las filas del grupo son verdaderas?", la segunda a "?hay al menos una verdadera?". Son la forma directa de preguntar cosas como "?todos los pedidos de este grupo cumplen?" o "?algun usuario es admin?", sin la gimnasia deCOUNT(*) = COUNT(...).Que devuelven BOOL_AND y BOOL_OR
Ambas reciben una expresion booleana y pliegan el grupo en un unico valor:
BOOL_AND(expr)es verdadero solo cuandoexpres verdadero en todas las filas del grupo.BOOL_OR(expr)es verdadero cuandoexpres verdadero en al menos una fila.SELECT BOOL_AND(active) AS all_active, BOOL_OR(is_admin) AS has_admin FROM users;Lo normal es usarlas junto a un
GROUP BY. Por pais, de un vistazo ves donde todas las cuentas estan activas y donde hay al menos un administrador:SELECT country, BOOL_AND(active) AS everyone_active, BOOL_OR(is_admin) AS any_admin FROM users GROUP BY country;La expresion no tiene que ser una columna booleana ya hecha; sirve cualquier comparacion. Asi se comprueba que todos los pedidos de un usuario son grandes:
SELECT user_id, BOOL_AND(amount >= 100) AS all_big_orders FROM orders GROUP BY user_id;Los NULL se ignoran (y un grupo vacio da NULL)
Como el resto de agregados,
BOOL_ANDyBOOL_ORsaltan las filas donde la expresion esNULL. El resultado se calcula solo sobre los valores booleanos "conocidos":BOOL_ANDmira solo las filas no NULL; un unicoNULLno fuerza el resultado a falso.BOOL_ORse apoya igualmente en las filas no NULL.NULL(o no hay filas), el resultado esNULL, notrue/false.-- a NULL status is ignored, not treated as 'not shipped' SELECT user_id, BOOL_AND(status = 'shipped') AS all_shipped FROM orders GROUP BY user_id;BOOL_AND frente a EVERY
En PostgreSQL,
EVERYes el sinonimo del SQL estandar paraBOOL_AND, identico en comportamiento. No existe un agregadoANYsimetrico en el estandar (ANYesta reservado para subconsultas), asi queBOOL_ORcubre el caso de "al menos una".-- EVERY is exactly BOOL_AND SELECT dept, EVERY(salary >= 50000) AS all_well_paid, BOOL_OR(salary >= 200000) AS any_top_earner FROM employees GROUP BY dept;Elegir entre
EVERYyBOOL_ANDes cuestion de estilo y portabilidad:EVERYesta mas cerca del estandar, mientras queBOOL_ANDse lee mejor junto aBOOL_OR. Un patron util es una compuerta de grupo entero enHAVING, por ejemplo departamentos donde cada empleado gana al menos un umbral:SELECT dept FROM employees GROUP BY dept HAVING BOOL_AND(salary >= 50000);Emularlo en MySQL y ClickHouse
MySQL no tiene
BOOL_AND/BOOL_ORnativos, pero alli un booleano es0/1, asi queMINyMAXdan el mismo resultado: el minimo de las banderas es 1 solo cuando todas son 1 (eso es AND), y el maximo es 1 cuando alguna es 1 (eso es OR).-- MySQL: MIN/MAX over a boolean expression cast to 0/1 SELECT user_id, MIN(amount >= 100) AS all_big_orders, -- like BOOL_AND MAX(status = 'paid') AS any_paid -- like BOOL_OR FROM orders GROUP BY user_id;El mismo truco funciona en PostgreSQL si primero conviertes el booleano a entero:
MIN((amount >= 100)::int). Algunas diferencias a tener presentes:amount >= 100ya devuelve1/0, asi que no hace falta cast explicito. En PostgreSQL anade::int, porqueMIN/MAXno aceptan unbooleandirectamente.MIN/MAXde MySQL tambien ignora los NULL, asi que el comportamiento ante grupos vacios (salidaNULL) coincide.min/maxnativos sobreUInt8, ademas deminOrNull/maxOrNull; no hay unbool_anddirecto, asi que se escribemin(amount >= 100)ymax(status = 'paid').En resumen: para las comprobaciones de "todo el grupo cumple una condicion" y "al menos uno cumple", usa
BOOL_AND/BOOL_OR(oEVERY) en PostgreSQL, recordando que ignoran NULL y devuelvenNULLen un grupo totalmente vacio; en otros motores, el mismo sentido se construye conMIN/MAXde una bandera booleana.