OVERLAPS e um operador infixo do padrao SQL que responde a uma unica pergunta: dois periodos de tempo se cruzam? Ele dispensa a comparacao manual de quatro datas e e ideal para encontrar conflitos em reservas, turnos e assinaturas.
Sintaxe basica
Cada periodo e um par entre parenteses: (start, end). A expressao devolve true quando os intervalos compartilham algum instante.
SELECT (DATE '2024-03-01', DATE '2024-03-10')
OVERLAPS (DATE '2024-03-08', DATE '2024-03-15') AS does_overlap;
SELECT (DATE '2024-03-01', DATE '2024-03-10')
OVERLAPS (DATE '2024-03-10', DATE '2024-03-20') AS does_overlap;
O segundo exemplo devolve false, e esse e o ponto-chave a internalizar: os limites sao tratados como um intervalo semiaberto [start, end). O fim de um periodo tocar o inicio de outro nao conta como cruzamento, exatamente o que voce quer para turnos em sequencia.
- Cada operando e um par
(inicio, fim).
- Aceita
date, timestamp, time e ate um par (instante, interval).
- Se o inicio for maior que o fim, o PostgreSQL os troca silenciosamente.
Encontrar conflitos de reservas
Suponha que as reservas estejam ligadas a pedidos. Queremos qualquer pedido cujo periodo se cruze com uma nova solicitacao de 5 a 8 de marco:
SELECT o.id, o.user_id
FROM orders o
WHERE (o.created_at, o.created_at + INTERVAL '3 days')
OVERLAPS (TIMESTAMP '2024-03-05', TIMESTAMP '2024-03-08');
Para encontrar cada par de periodos sobrepostos dentro de uma mesma tabela, fazemos um self-join e usamos o1.id < o2.id para descartar duplicatas e a comparacao de uma linha com ela mesma:
SELECT o1.id AS first_id, o2.id AS second_id
FROM orders o1
JOIN orders o2
ON o1.user_id = o2.user_id
AND o1.id < o2.id
AND (o1.created_at, o1.created_at + INTERVAL '1 hour')
OVERLAPS (o2.created_at, o2.created_at + INTERVAL '1 hour');
OVERLAPS e acucar sintatico sobre a condicao classica. Dois periodos se cruzam se e somente se:
WHERE a_start < b_end
AND b_start < a_end
Essa forma e indispensavel quando OVERLAPS e inconveniente ou inexistente — por exemplo no MySQL, que nao tem esse operador. A mesma busca de conflitos:
SELECT o1.id, o2.id
FROM orders o1
JOIN orders o2
ON o1.user_id = o2.user_id
AND o1.id < o2.id
AND o1.created_at < o2.created_at + INTERVAL '1 hour'
AND o2.created_at < o1.created_at + INTERVAL '1 hour';
Pegadinha: um erro comum e escrever <= em vez de <. Com < estrito, periodos adjacentes (fim = inicio) nao sao tratados como conflito, igual ao OVERLAPS. Se voce realmente quer que o toque conte como cruzamento (por exemplo, para cobertura continua), use <= — mas ai o comportamento diverge do OVERLAPS.
Os tipos range como alternativa
O PostgreSQL tem tipos de faixa dedicados (tsrange, daterange, int4range) e um operador de sobreposicao &&. Le-se melhor e, ao contrario de OVERLAPS, pode ser indexado com GiST.
SELECT o.id
FROM orders o
WHERE tsrange(o.created_at, o.created_at + INTERVAL '3 days')
&& tsrange(TIMESTAMP '2024-03-05', TIMESTAMP '2024-03-08');
A grande vantagem sao as restricoes de exclusao: o proprio banco garante que os periodos nunca se cruzem, sem condicoes de corrida na aplicacao. Por exemplo, voce pode proibir reservar um funcionario para dois turnos ao mesmo tempo:
ALTER TABLE employees
ADD COLUMN shift tsrange;
ALTER TABLE employees
ADD CONSTRAINT no_shift_conflict
EXCLUDE USING gist (id WITH =, shift WITH &&);
Qualquer tentativa de inserir um turno sobreposto para o mesmo id falha no nivel do banco. O ClickHouse nao tem nem OVERLAPS nem tipos range, entao la voce recorre a condicao manual a_start < b_end AND b_start < a_end.
Resumindo: para uma checagem pontual no WHERE use OVERLAPS; para portabilidade, a comparacao manual; e para indexacao mais garantias rigidas de integridade, os tipos range com EXCLUDE.
OVERLAPSe um operador infixo do padrao SQL que responde a uma unica pergunta: dois periodos de tempo se cruzam? Ele dispensa a comparacao manual de quatro datas e e ideal para encontrar conflitos em reservas, turnos e assinaturas.Sintaxe basica
Cada periodo e um par entre parenteses:
(start, end). A expressao devolvetruequando os intervalos compartilham algum instante.SELECT (DATE '2024-03-01', DATE '2024-03-10') OVERLAPS (DATE '2024-03-08', DATE '2024-03-15') AS does_overlap; -- true SELECT (DATE '2024-03-01', DATE '2024-03-10') OVERLAPS (DATE '2024-03-10', DATE '2024-03-20') AS does_overlap; -- falseO segundo exemplo devolve
false, e esse e o ponto-chave a internalizar: os limites sao tratados como um intervalo semiaberto[start, end). O fim de um periodo tocar o inicio de outro nao conta como cruzamento, exatamente o que voce quer para turnos em sequencia.(inicio, fim).date,timestamp,timee ate um par(instante, interval).Encontrar conflitos de reservas
Suponha que as reservas estejam ligadas a pedidos. Queremos qualquer pedido cujo periodo se cruze com uma nova solicitacao de 5 a 8 de marco:
SELECT o.id, o.user_id FROM orders o WHERE (o.created_at, o.created_at + INTERVAL '3 days') OVERLAPS (TIMESTAMP '2024-03-05', TIMESTAMP '2024-03-08');Para encontrar cada par de periodos sobrepostos dentro de uma mesma tabela, fazemos um self-join e usamos
o1.id < o2.idpara descartar duplicatas e a comparacao de uma linha com ela mesma:SELECT o1.id AS first_id, o2.id AS second_id FROM orders o1 JOIN orders o2 ON o1.user_id = o2.user_id AND o1.id < o2.id AND (o1.created_at, o1.created_at + INTERVAL '1 hour') OVERLAPS (o2.created_at, o2.created_at + INTERVAL '1 hour');O equivalente com comparacao de datas
OVERLAPSe acucar sintatico sobre a condicao classica. Dois periodos se cruzam se e somente se:-- a_start < b_end AND b_start < a_end WHERE a_start < b_end AND b_start < a_endEssa forma e indispensavel quando
OVERLAPSe inconveniente ou inexistente — por exemplo noMySQL, que nao tem esse operador. A mesma busca de conflitos:SELECT o1.id, o2.id FROM orders o1 JOIN orders o2 ON o1.user_id = o2.user_id AND o1.id < o2.id AND o1.created_at < o2.created_at + INTERVAL '1 hour' AND o2.created_at < o1.created_at + INTERVAL '1 hour';Os tipos range como alternativa
O PostgreSQL tem tipos de faixa dedicados (
tsrange,daterange,int4range) e um operador de sobreposicao&&. Le-se melhor e, ao contrario deOVERLAPS, pode ser indexado com GiST.SELECT o.id FROM orders o WHERE tsrange(o.created_at, o.created_at + INTERVAL '3 days') && tsrange(TIMESTAMP '2024-03-05', TIMESTAMP '2024-03-08');A grande vantagem sao as restricoes de exclusao: o proprio banco garante que os periodos nunca se cruzem, sem condicoes de corrida na aplicacao. Por exemplo, voce pode proibir reservar um funcionario para dois turnos ao mesmo tempo:
ALTER TABLE employees ADD COLUMN shift tsrange; ALTER TABLE employees ADD CONSTRAINT no_shift_conflict EXCLUDE USING gist (id WITH =, shift WITH &&);Qualquer tentativa de inserir um turno sobreposto para o mesmo
idfalha no nivel do banco. OClickHousenao tem nemOVERLAPSnem tipos range, entao la voce recorre a condicao manuala_start < b_end AND b_start < a_end.Resumindo: para uma checagem pontual no
WHEREuseOVERLAPS; para portabilidade, a comparacao manual; e para indexacao mais garantias rigidas de integridade, os tipos range comEXCLUDE.