惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

H
Help Net Security
T
ThreatConnect
SecWiki News
SecWiki News
F
Future of Privacy Forum
AWS News Blog
AWS News Blog
C
Cisco Blogs
A
Arctic Wolf
Vercel News
Vercel News
The GitHub Blog
The GitHub Blog
Scott Helme
Scott Helme
V
V2EX
博客园 - 叶小钗
阮一峰的网络日志
阮一峰的网络日志
K
Kaspersky official blog
G
Google Developers Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
P
Privacy International News Feed
C
Cyber Attacks, Cyber Crime and Cyber Security
N
News | PayPal Newsroom
Schneier on Security
Schneier on Security
NISL@THU
NISL@THU
Microsoft Azure Blog
Microsoft Azure Blog
量子位
The Hacker News
The Hacker News
Stack Overflow Blog
Stack Overflow Blog
Security Latest
Security Latest
M
Microsoft Research Blog - Microsoft Research
Google Online Security Blog
Google Online Security Blog
博客园_首页
C
CXSECURITY Database RSS Feed - CXSecurity.com
I
InfoQ
Google DeepMind News
Google DeepMind News
Y
Y Combinator Blog
The Cloudflare Blog
Microsoft Security Blog
Microsoft Security Blog
Martin Fowler
Martin Fowler
Cisco Talos Blog
Cisco Talos Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
T
Troy Hunt's Blog
F
Fox-IT International blog
S
Security @ Cisco Blogs
博客园 - 司徒正美
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
Comments on: Blog
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
L
LINUX DO - 最新话题
GbyAI
GbyAI
Project Zero
Project Zero
腾讯CDC
T
Tailwind CSS Blog

Все публикации подряд на Хабре

@tanstack/vue-table: почему я почти отказался от этого… Гравитация не существует. Вы задали 454 вопроса о времени. Вот ответы с уравнениями Эйнштейна Конец бесплатного кремния: как Google AI Studio превратилась из рая для инженеров в симулятор смены аккаунтов Свой AI-агент из почты, systemd и LLM MemForge2: загрузочная флешка, которая за минуту говорит — какую планку памяти менять Лицензии важны. Разбор ошибок авторов и пользователей программ От RAG-прототипа к агенту в продакшн: путь по метрикам, а не по моде Serial Terminal: кастомный веб-терминал для последовательного порта на Web Serial API Китайский стартап GigaAI обещает робота-домработника за 1 млн рублей уже в 2027 году — правда или PR? Open-source VPN клиент Tunguska Роман за 6 недель без идеи на старте: миф или реальность? ИИ построит ваш план действий за 10 секунд Security Week 2622: эффективность Claude Mythos по версии Cloudflare Reactive Forms vs Signal Forms: Эволюция сложных форм в Angular TorFlash — приложение для Linux: поиск торрентов, скачивание и копирование на флешку в одно нажатие Как я решил проблему русской диктовки для ИИ Оверинжиниринг, потопивший немецкую подлодку или некоторые «баги» не чинятся десятилетиями Как ставить цели и не забывать о них: пошаговая система с примерами в таск-менеджере Как настроить observability в Spring Boot 3 HackTheBox. Прохождение Mini Pro Lab Puppet Обзор серверного ускорителя NVIDIA Tesla V100 16 Gb в корпусе от RTX 4090: Часть 3 — Запуск локальных моделей ИИ Редактирование текста нейросетью: как сделать диплом и курсовую более человечными Самодельный ARM ноутбук, реально ли? Как 100+ авторов пишут 100+ процессов в 3 версиях и не путаются. Или как мы переехали с Wiki на Git Прошла AnalystDays – хорошие выступления и нетворкинг VSCode как IDE для embedded разработки Моделирование широкополосной антенны с двойной круговой поляризацией и высокой изоляцией Ваше прошлое физически существует прямо сейчас. И вы заморожены там навсегда От списка инструментов к technical output: как security engineer’у описывать hands-on опыт в CV и на интервью I just want an agent. Часть 1. Как я научил ИИ собирать ИИ-агентов за пользователей и выиграл конкурс I just want an agent. Часть 1. Как я научил ИИ собирать ИИ-агентов за пользователей и выиграл конкурс Вайбкодинг спас меня от подрядчиков. А потом я поняла, что сама стала подрядчиком для своих агентов Святой Августин и GAN: почему борьба добра и зла — это генеративная состязательная сеть В каждом QR-коде зашита половина лишней информации. Намеренно Я открываю автомат ключом, меняю рулон бумаги и зарабатываю 180 тысяч в месяц с точки Мастер восстановления. Культура достиженства и выгорание Недельный геймдев: #279 — 24 мая, 2026 Защита от дублирования кода агентами: семантические концепции Frontend Status: свежий дайджест фронтенда и AI — 25.05.2026 Где искать IT-работу кроме HH: подборка платформ 2026 Почему простые числа собираются в спирали? OCR для Data Lakehouse: от Apache Tika к собственному решению на базе Docling Jira — Тьюринг-полная Kubernetes-аудит после Wiz и Prisma: как живут без CNAPP в 2026 «Тестируем MVP в 4 раза быстрее»: как нейросети изменили жизнь предпринимателей На каком стеке и железе работает умное наблюдение в вашем городе: обзор технологий от разработчиков видеоаналитики Как мы ускорили согласования на двух заводах в 24 раза Heartbeat-мониторинг cron-job'ов: dead-man-switch на FastAPI [Перевод] Сегодня нет джуниоров, а в 2031 году не станет и синьоров Профайлер для PostgreSQL: от идеи до работающего MVP за сутки [Перевод] Ограничения размера cookie в ASP.NET Core в продакшене: причины и способы решения Проблема «божественного» Obsidian: почему я отказался от централизованного подхода в работе Лицензии GNU GPL: как пройти проверку Минцифры и заказчика для госзакупок и КИИ Хакатон Samsung IT Academy Hack 2026: как студенты оптимизировали поиск в корпоративном мессенджере Хакатон Samsung IT Academy Hack 2026: как студенты оптимизировали поиск в корпоративном мессенджере MTProxy jumper — делаем автоматическое переключение прокси-серверов Telegram Ты уже используешь агента. Просто не заметил Книжный салон. Послевкусие и благодарности Как отлаживать мини‑приложения в MAX и почему без DevTools это боль Cбор биометрических данных. Как защищается наша биометрия на практике Как запустить учет активов без цифровой свалки: первые 90 дней CGE: визуализация кравлера и скрытых связей между поддоменами Зачем банки тратят миллиарды на науку (спойлер: не благотворительности ради) Книга: «Современный Java Concurrency. Глубокое погружение в Virtual Threads, Structured Concurrency и Scoped Values» Как использовать подписку ChatGPT и Claude в Cursor без оплаты за API токены Специализированная ИСУП или модуль в универсальной платформе: вот в чем вопрос Обход белых списков через WebRTC на стероидах (с поддержкой iOS и десктопа) Регата INFOSTART CIO CAMP: когда команда проверяется не в переговорной, а на воде Пет-проект, который не умер: система бронирования устройств как полигон для AI-разработки Не надо встраивать ИИ в каждую корпоративную систему, это архитектурная ошибка Нейросети для дизайна интерьера: Выбираем лучший ИИ для генерации концептов и планировок квартиры Что там с Ил-114-300 Что такое DAS: как и зачем продукт-менеджеры саботируют запуск новых продуктов 8% компаний измеряют критическое мышление руководителей. Что делают остальные 92% CVE, Shell и побег из контейнера: испытываем возможности PT Cloud Application Firewall Как я научил Алису петь: генерация музыки по голосовой команде Восстановление данных с помощью бесплатной утилиты Easy Disk Checker Как мы построили сквозную аналитику в Power BI Год разработки iOS-игры, 266 тысяч показов и $33: как я делал Vault и почти ничего не заработал Ты прокрастинируешь потому, что избегаешь напрасных усилий, а не чрезмерных нагрузок Я построила диагностику «стоит ли это автоматизировать» — и она трижды говорила глупости. Разбор ошибок Как устроены world models, что показал Google на прошлой неделе и где это меняет gamedev и робототехнику Двухдневная рабочая неделя — будущий стандарт CPU не умер, он просто ждал. Китай строит двухэксафлопсный суперкомпьютер без единого GPU — прорыв, необходимость, фейк? 3Sound: поиск бесплатных звуков для игр больше не боль? 3 Тбит/с по-русски: почему DDoS в 2026 году стал угрозой для любого бизнеса 10 Гбит/с — зачем вам такая скорость передачи данных в облаке Ремонтируем аналоговый XY-самописец Endim 622 [Перевод] IPO компании SpaceX: хорошая попытка, но нет «Ща будет шрифт»: история одного русского embedded‑шрифта Как аквариум на подоконнике превратился в full-stack платформу с AI GiftsHub — из чат-бота в полноценный backend-продукт Пиратство, копирайт и DMCA: как Napster, The Pirate Bay и YouTube изменили закон. Часть II Как найти внутренние резервы для развития предприятия Как один французский чиновник от безысходности начал платил зарплаты картами и практически изобрёл банкноты RAG в энтерпрайзе: почему демо работает, а прод нет AI-агент для финансовых процессов: как мы научили ИИ считать числа из базе данных без галлюцинаций Автопостинг на 8 платформах: архитектура waterfall, custom publisher'ы и API-ловушки Кинетика против бронзы: Почему Голиаф был обречен в дуэли с Давидом [Перевод] Масштабирование LLM: от одного чипа до ЦОДа. Глава 2. Шардинг
WHERE превращает ваш LEFT JOIN в INNER JOIN. И никто вам об этом не скажет
badcasedaily · 2026-05-26 · via Все публикации подряд на Хабре

Уровень сложностиПростой

Время на прочтение5 мин

Охват и читатели166

Туториал

Задача знакомая, очень знакомая. Нужна выгрузка: все клиенты и их заказы за апрель. Все — значит все, включая тех, кто за месяц так ничего и не купил. Отсюда LEFT JOIN, а не INNER. Вы это прекрасно знаете, ведь не первый день в SQL.

Пишете запрос. Добавляете WHERE orders.created_at >= '2024-04-01', чтобы отрезать всё, что не апрель. Запускаете.

В таблице клиентов восемь тысяч строк. В выгрузке почему‑то шесть. Две тысячи человек куда‑то испарились. И не абы какие: пропали ровно те, ради кого вы и городили LEFT JOIN — клиенты без заказов.

Неприятнее всего тут не сама пропажа. Неприятнее то, что запрос выглядит безупречно. Вы смотрите на него и не видите ошибки. Её там и нет, в смысле синтаксиса. База не ругнулась, ничего красным не подсветилось. Просто строк меньше, чем нужно, а это глазами не ловится: выгрузка не пустая, шесть тысяч живых записей, всё выглядит настоящим.

Сейчас разберёмся, куда ушли эти две тысячи.

С LEFT JOIN всё в порядке, правда

Снимем подозрение с главного обвиняемого сразу.

LEFT JOIN делает ровно то, что обещает. Берёт каждую строку левой таблицы. Ищет ей пару справа. Нашёл — склеил. Не нашёл, и вот тут самое важное, строку всё равно оставил, просто колонки правой таблицы забил NULL‑ами.

Клиент без заказов никуда не девается. Он доезжает до результата живым, только вместо данных о заказе у него пустота.

То есть LEFT JOIN свою часть выполнил. Все клиенты были на месте. А потом пришёл WHERE.

Запрос читается не так, как выполняется

Мы читаем SQL сверху вниз: сначала SELECT, потом FROM, потом WHERE. И кажется логичным, что выполняется он в том же порядке. Не выполняется.

База сначала берётся за FROM и JOIN. Склеивает таблицы, строит промежуточный результат. И только когда он готов, по нему проходит WHERE и выкидывает ненужные строки.

Все идет к моменту, когда WHERE берётся за дело, LEFT JOIN давно закончил. В промежуточном результате уже лежат наши клиенты без заказов — с честными NULL в колонках orders.

И вот WHERE смотрит на такую строку через своё условие:

SELECT c.id, c.name, o.id AS order_id, o.created_at
FROM clients c
LEFT JOIN orders o ON o.client_id = c.id
WHERE o.created_at >= '2024-04-01';

У клиента без заказов o.created_at — это NULL. И вопрос на сообразительность: NULL >= '2024-04-01' — это правда или ложь?

Ни то ни другое. Это UNKNOWN, «неизвестно», третье значение в логике SQL. Сравнить дату с тем, чего нет, нельзя, вот база и разводит руками.

А WHERE пускает дальше только то, что строго TRUE. UNKNOWN для него — то же самое, что FALSE: на выход. И каждая строка клиента без заказов тихо вылетает.

Что остаётся? Только клиенты с заказом, да ещё подходящим по дате. То есть ровно то, что вернул бы INNER JOIN. Вы написали LEFT, получили INNER, и никто вас не предупредил.

Почему это так легко проглядеть

Синтаксис чистый, оптимизатор доволен. Результат не пустой, в нём тысячи строк, и каждая выглядит как надо. Чтобы заподозрить неладное, нужно заранее знать, сколько строк ты ждёшь, и сверить. А кто это делает каждый раз?

В код‑ревью такое часто пропускается. Коллега видит LEFT JOIN, видит понятный фильтр по дате, ставит апрув. Всё же логично написано.

А отчёт потом не падает, он просто врёт. «Клиентов, которые в апреле ничего не купили, у нас нет», красивый вывод из запроса, где этих клиентов вырезали ещё до того, как кто‑то взялся их считать.

ON и WHERE спрашивают у базы разное

Чинится всё переносом одного условия. Из WHERE — в ON:

SELECT c.id, c.name, o.id AS order_id, o.created_at
FROM clients c
LEFT JOIN orders o
  ON o.client_id = c.id
  AND o.created_at >= '2024-04-01';

С виду — косметика, условие переехало на две строчки выше. На деле поменялось всё.

Условие в ON — это часть правила, по которому ищется пара. Оно отвечает на вопрос «что вообще считать совпадением». Теперь заказ подходит клиенту, только если совпал и по client_id, и по апрелю. У клиента без апрельских заказов пары нет, и LEFT JOIN, как мы уже выяснили, в таком случае оставляет строку с NULL. А WHERE её больше не трогает: фильтра по дате в WHERE теперь просто нет. Клиент остаётся.

Условие в WHERE совсем другое. Это фильтр по уже готовому, склеенному результату. И вопрос здесь другой: «что оставить из того, что получилось». А NULL‑строкам в этом фильтре не выжить, любое сравнение с колонкой правой таблицы их срежет.

Вот и вся разница. ON и WHERE — не два кармана, куда можно сунуть одно и то же условие. Это два разных вопроса. ON спрашивает: что считать парой? WHERE спрашивает: что оставить в конце?

Иногда условие в WHERE — это правильно

Только не уносите отсюда правило «фильтр по правой таблице в WHERE — всегда плохо». Не всегда.

Нужны только клиенты с апрельскими заказами? Пишите INNER JOIN и живите спокойно. Маскировать INNER‑логику под LEFT JOIN с фильтром в WHERE плохо не потому, что не работает — работает. Плохо потому, что следующий человек прочитает LEFT JOIN и поверит ему.

А иногда NULL‑строка в WHERE нужна вам совершенно сознательно. Например, когда надо найти клиентов вообще без заказов:

SELECT c.id, c.name
FROM clients c
LEFT JOIN orders o ON o.client_id = c.id
WHERE o.id IS NULL;

LEFT JOIN притащил всех. WHERE o.id IS NULL оставил только тех, кому пара не нашлась. У всего этого есть имя — антиджойн, способ найти «то, чего нет».

И заметьте: работает он за счёт того же поведения, которое в начале статьи ломало нам выгрузку. LEFT JOIN создаёт NULL‑строки, WHERE умеет по ним фильтровать. Одно и то же свойство — то баг, то фича.

Разница в намерении. В антиджойне вы фильтруете по IS NULL, то есть прямо говорите «лови отсутствие пары». А в сломанном запросе фильтр по дате цепляет NULL‑строку случайно, как побочку, про которую автор и не думал.

Куда же делись две тысячи клиентов

Теперь понятно. LEFT JOIN их не терял — он добросовестно довёл всех до промежуточного результата, с NULL‑ами вместо заказов. Их выкинул WHERE: сравнил NULL с датой, получил UNKNOWN, а UNKNOWN для WHERE — это «не пускать».

С собой унести мысль смотреть на запрос: ON и WHERE задают базе разные вопросы. ON — «что здесь пара?». WHERE — «что оставить, когда всё склеилось?».

Как только эти вопросы перестанут сливаться в один, LEFT JOIN перестанет вас подводить.

И каждый раз, встретив в WHERE условие на колонку правой таблицы рядом с LEFT JOIN, вы поймаете себя на мысли: так, а вот тут LEFT только что превратился в INNER.

Кажется, что с SQL всё давно понятно, пока один WHERE внезапно не превращает LEFT JOIN в INNER JOIN. Если хотите проверить, насколько уверенно ориентируетесь в таких нюансах, попробуйте пройти бесплатный тест по SQL для разработчиков и аналитиков.

Если тема SQL и баз данных вам близка, приходите и на бесплатные открытые уроки:

Больше открытых уроков и материалов по backend, SQL и инфраструктуре публикуем на канале OTUS в MAX.