BOOL_AND e BOOL_OR sao funcoes de agregacao sobre uma coluna booleana. A primeira responde "todas as linhas do grupo sao verdadeiras?", a segunda "ha ao menos uma verdadeira?". Sao a forma direta de perguntar coisas como "todos os pedidos deste grupo se qualificam?" ou "algum usuario e admin?", sem a ginastica de COUNT(*) = COUNT(...).
O que BOOL_AND e BOOL_OR retornam
Ambas recebem uma expressao booleana e dobram o grupo em um unico valor:
BOOL_AND(expr) e verdadeiro apenas quando expr e verdadeiro em todas as linhas do grupo.
BOOL_OR(expr) e verdadeiro quando expr e verdadeiro em ao menos uma linha.
SELECT
BOOL_AND(active) AS all_active,
BOOL_OR(is_admin) AS has_admin
FROM users;
Na maioria das vezes elas ficam ao lado de um GROUP BY. Por pais, de relance voce ve onde todas as contas estao ativas e onde ha ao menos um administrador:
SELECT
country,
BOOL_AND(active) AS everyone_active,
BOOL_OR(is_admin) AS any_admin
FROM users
GROUP BY country;
A expressao nao precisa ser uma coluna booleana pronta; qualquer comparacao serve. Assim se verifica que todos os pedidos de um usuario sao grandes:
SELECT
user_id,
BOOL_AND(amount >= 100) AS all_big_orders
FROM orders
GROUP BY user_id;
Os NULL sao ignorados (e um grupo vazio retorna NULL)
Como os demais agregados, BOOL_AND e BOOL_OR pulam as linhas em que a expressao e NULL. O resultado e calculado apenas sobre os valores booleanos "conhecidos":
BOOL_AND olha somente para as linhas nao NULL; um unico NULL nao forca o resultado para falso.
BOOL_OR tambem se apoia nas linhas nao NULL.
- Se todos os valores do grupo forem
NULL (ou nao houver linhas), o resultado e NULL, e nao true/false.
SELECT
user_id,
BOOL_AND(status = 'shipped') AS all_shipped
FROM orders
GROUP BY user_id;
Pegadinha: a regra "tudo NULL produz NULL" e facil de deixar passar. Um filtro WHERE all_shipped = true descarta em silencio os grupos cujo status nunca e preenchido, porque para eles o predicado e NULL, nao false. Se esses grupos devem contar como "nao cumpridos", envolva o resultado: COALESCE(BOOL_AND(status = 'shipped'), false).
BOOL_AND versus EVERY
No PostgreSQL, EVERY e o sinonimo do SQL padrao para BOOL_AND, identico no comportamento. Nao existe um agregado ANY simetrico no padrao (ANY esta reservado para subconsultas), entao BOOL_OR cobre o caso de "ao menos uma".
SELECT
dept,
EVERY(salary >= 50000) AS all_well_paid,
BOOL_OR(salary >= 200000) AS any_top_earner
FROM employees
GROUP BY dept;
Escolher entre EVERY e BOOL_AND e questao de estilo e portabilidade: EVERY esta mais perto do padrao, enquanto BOOL_AND fica mais legivel ao lado de BOOL_OR. Um padrao util e um portao de grupo inteiro no HAVING, por exemplo departamentos em que cada funcionario ganha ao menos um limite:
SELECT dept
FROM employees
GROUP BY dept
HAVING BOOL_AND(salary >= 50000);
Emulando no MySQL e no ClickHouse
O MySQL nao tem BOOL_AND/BOOL_OR nativos, mas la um booleano e 0/1, entao MIN e MAX dao o mesmo resultado: o minimo das flags e 1 somente quando todas sao 1 (isso e AND), e o maximo e 1 quando alguma e 1 (isso e OR).
SELECT
user_id,
MIN(amount >= 100) AS all_big_orders,
MAX(status = 'paid') AS any_paid
FROM orders
GROUP BY user_id;
O mesmo truque funciona no PostgreSQL se voce converter o booleano para inteiro antes: MIN((amount >= 100)::int). Algumas diferencas para ter em mente:
- No MySQL a comparacao
amount >= 100 ja retorna 1/0, entao nao e preciso cast explicito. No PostgreSQL adicione ::int, porque MIN/MAX nao aceitam um boolean diretamente.
- O
MIN/MAX do MySQL tambem ignora os NULL, entao o comportamento diante de grupos vazios (saida NULL) coincide.
- O ClickHouse tem
min/max nativos sobre UInt8, alem de minOrNull/maxOrNull; nao ha um bool_and direto, entao escreve-se min(amount >= 100) e max(status = 'paid').
Resumindo: para as verificacoes de "o grupo inteiro satisfaz uma condicao" e "ao menos um satisfaz", use BOOL_AND/BOOL_OR (ou EVERY) no PostgreSQL, lembrando que ignoram NULL e retornam NULL em um grupo totalmente vazio; em outros motores, o mesmo sentido e construido com MIN/MAX de uma flag booleana.
BOOL_ANDeBOOL_ORsao funcoes de agregacao sobre uma coluna booleana. A primeira responde "todas as linhas do grupo sao verdadeiras?", a segunda "ha ao menos uma verdadeira?". Sao a forma direta de perguntar coisas como "todos os pedidos deste grupo se qualificam?" ou "algum usuario e admin?", sem a ginastica deCOUNT(*) = COUNT(...).O que BOOL_AND e BOOL_OR retornam
Ambas recebem uma expressao booleana e dobram o grupo em um unico valor:
BOOL_AND(expr)e verdadeiro apenas quandoexpre verdadeiro em todas as linhas do grupo.BOOL_OR(expr)e verdadeiro quandoexpre verdadeiro em ao menos uma linha.SELECT BOOL_AND(active) AS all_active, BOOL_OR(is_admin) AS has_admin FROM users;Na maioria das vezes elas ficam ao lado de um
GROUP BY. Por pais, de relance voce ve onde todas as contas estao ativas e onde ha ao menos um administrador:SELECT country, BOOL_AND(active) AS everyone_active, BOOL_OR(is_admin) AS any_admin FROM users GROUP BY country;A expressao nao precisa ser uma coluna booleana pronta; qualquer comparacao serve. Assim se verifica que todos os pedidos de um usuario sao grandes:
SELECT user_id, BOOL_AND(amount >= 100) AS all_big_orders FROM orders GROUP BY user_id;Os NULL sao ignorados (e um grupo vazio retorna NULL)
Como os demais agregados,
BOOL_ANDeBOOL_ORpulam as linhas em que a expressao eNULL. O resultado e calculado apenas sobre os valores booleanos "conhecidos":BOOL_ANDolha somente para as linhas nao NULL; um unicoNULLnao forca o resultado para falso.BOOL_ORtambem se apoia nas linhas nao NULL.NULL(ou nao houver linhas), o resultado eNULL, e naotrue/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 versus EVERY
No PostgreSQL,
EVERYe o sinonimo do SQL padrao paraBOOL_AND, identico no comportamento. Nao existe um agregadoANYsimetrico no padrao (ANYesta reservado para subconsultas), entaoBOOL_ORcobre o caso de "ao menos uma".-- 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;Escolher entre
EVERYeBOOL_ANDe questao de estilo e portabilidade:EVERYesta mais perto do padrao, enquantoBOOL_ANDfica mais legivel ao lado deBOOL_OR. Um padrao util e um portao de grupo inteiro noHAVING, por exemplo departamentos em que cada funcionario ganha ao menos um limite:SELECT dept FROM employees GROUP BY dept HAVING BOOL_AND(salary >= 50000);Emulando no MySQL e no ClickHouse
O MySQL nao tem
BOOL_AND/BOOL_ORnativos, mas la um booleano e0/1, entaoMINeMAXdao o mesmo resultado: o minimo das flags e 1 somente quando todas sao 1 (isso e AND), e o maximo e 1 quando alguma e 1 (isso e 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;O mesmo truque funciona no PostgreSQL se voce converter o booleano para inteiro antes:
MIN((amount >= 100)::int). Algumas diferencas para ter em mente:amount >= 100ja retorna1/0, entao nao e preciso cast explicito. No PostgreSQL adicione::int, porqueMIN/MAXnao aceitam umbooleandiretamente.MIN/MAXdo MySQL tambem ignora os NULL, entao o comportamento diante de grupos vazios (saidaNULL) coincide.min/maxnativos sobreUInt8, alem deminOrNull/maxOrNull; nao ha umbool_anddireto, entao escreve-semin(amount >= 100)emax(status = 'paid').Resumindo: para as verificacoes de "o grupo inteiro satisfaz uma condicao" e "ao menos um satisfaz", use
BOOL_AND/BOOL_OR(ouEVERY) no PostgreSQL, lembrando que ignoram NULL e retornamNULLem um grupo totalmente vazio; em outros motores, o mesmo sentido e construido comMIN/MAXde uma flag booleana.