SQL Ready | Базы Данных: post #2081 — TG.ME

Работайте с периодами как с одним значением!

Когда у записи есть start_at и end_at, проверки пересечений быстро превращаются в набор сравнений. В PostgreSQL для этого есть range types: например, tstzrange для диапазона timestamptz.

Вместо:
WHERE start_at < :end_at
AND end_at > :start_at


можно явно работать с диапазонами:
WHERE tstzrange(start_at, end_at, '[)')
&& tstzrange(:start_at, :end_at, '[)')


Оператор && означает «диапазоны пересекаются». [) задаёт полуинтервал: начало включено, конец исключён, поэтому встреча до 12:00 и следующая с 12:00 не считаются пересекающимися.

Но интереснее то, что PostgreSQL умеет проверять диапазоны и другими операторами:
period @> now()       -- содержит момент времени
period && :period -- пересекается
period <@ :period -- находится внутри


Если такие проверки выполняются постоянно, диапазон можно хранить непосредственно в таблице и индексировать GiST:
CREATE INDEX bookings_period_idx
ON bookings USING gist (period);


И самое полезное: правило «для одной комнаты интервалы не должны пересекаться» можно перенести из приложения непосредственно в БД:
CREATE EXTENSION IF NOT EXISTS btree_gist;

ALTER TABLE bookings
ADD CONSTRAINT bookings_no_overlap
EXCLUDE USING gist (
room_id WITH =,
period WITH &&
);


Теперь две пересекающиеся брони одной комнаты физически нельзя записать, даже если два конкурентных запроса одновременно прошли предварительную проверку в приложении.

🔥 Range types превращают интервалы времени из пары колонок и ручной логики в полноценный тип данных: его можно сравнивать, индексировать и даже запретить пересечения на уровне констрейнта.

➡️ SQL Ready | #совет
🤝14👍6🔥4❤2
August 22, 2026 1.9K 1 27