TGViewer
SQL Ready | Базы Данных SQL Ready | Базы Данных @sql_ready · 17.5K subscribers
Post #1799 2.66K
Анти-JOIN — как корректно выбирать строки без связанных записей!

Частая задача в SQL — найти строки, для которых отсутствуют связанные записи в другой таблице. Это классический anti-join. Например, нужно получить клиентов, которые ни разу не сделали заказ.

Таблицы:
customers(id, name)
orders(id, customer_id, created_at)


LEFT JOIN + IS NULL:
SELECT c.id, c.name
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.id
WHERE o.id IS NULL;


LEFT JOIN возвращает всех клиентов. Если совпадений нет, поля из orders становятся NULL, и фильтр оставляет только клиентов без заказов.

Проверять нужно гарантированно NOT NULL колонку правой таблицы (обычно PK, например o.id), иначе возможны логические ошибки.

NOT EXISTS (предпочтительно):
SELECT c.id, c.name
FROM customers c
WHERE NOT EXISTS (
SELECT 1
FROM orders o
WHERE o.customer_id = c.id
);


Запрос напрямую выражает отсутствие строк. В простом случае он эквивалентен LEFT JOIN, но лучше отражает намерение, устойчив к NULL в подзапросе и часто даёт более предсказуемый план при усложнении условий.

Почему не NOT IN:
SELECT c.id, c.name
FROM customers c
WHERE c.id NOT IN (
SELECT customer_id
FROM orders
);


Если подзапрос возвращает хотя бы один NULL, результат может стать пустым из-за трёхзначной логики SQL. Использовать этот вариант безопасно только при гарантированном NOT NULL в orders.customer_id.

Нюанс с условиями в JOIN:
SELECT c.id
FROM customers c
LEFT JOIN orders o
ON o.customer_id = c.id
AND o.created_at >= DATE '2026-01-01'
WHERE o.id IS NULL;


Этот запрос ищет клиентов без заказов после указанной даты.

Если требуется найти клиентов вообще без заказов, добавлять условия в JOIN нельзя — это меняет бизнес-смысл. В таком случае корректнее использовать NOT EXISTS.

🔥 Минимально необходим индекс orders(customer_id). При частой фильтрации по дате логичен составной индекс (customer_id, created_at).

➡️ SQL Ready | #практика
  • ❤ 16
  • 👍 10
  • 🔥 8
More from @sql_ready
  1. Oct 7, 2026📂 Шпаргалка по паттернам распределённых систем! Например, Replication повышает доступност…
  2. Oct 6, 2026Проверяйте связи с учётом периода! В PostgreSQL 18 связь может учитывать ещё и период его…
  3. Oct 6, 2026⚡️5 фундаментальных курсов по ИБ по цене одного Это предложение для тех, кто готов войти в…
  4. Oct 6, 2026👍 Устройство PostgreSQL — подробная документация на русском языке! Материалы посвящены не…
  5. Oct 5, 2026COUNT(*) в PostgreSQL: MVCC, visibility map и стоимость выполнения! В PostgreSQL точный CO…
  6. Oct 5, 2026Как оплачивать зарубежные сервисы в 2026 году? Можно бегать между посредниками и бояться б…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →