Привет, Хабр! Меня зовут Сергей Барановский, я руководитель проектов по аналитике в Блоке по клиентскому опыту и сервису и сегодня я хочу поделиться наболевшим. Джойн таблиц — одна из самых базовых вещей в аналитике. Казалось бы, допустить здесь ошибку почти невозможно. И правда! Что может быть проще, чем стыковать таблицы ключ к ключу?! Ковыряться в носу и то сложнее — можно ненароком кровеносный сосуд задеть. И, потеряв бдительность из-за простоты процедуры, можно набрать корзину проблем на самых базовых вещах. Под катом — познавательный кейс для тех, кто ходит тропами SQL.
У меня было два датасета, которые я предварительно пропустил через DISTINCT, то есть убрал все дубли. Мне нужно сделать LEFT JOIN. В левой таблице есть NULL-ключи, и я об этом знаю. «Но это не так важно, — подумал я, — ведь в правой таблице нет NULL-ключей, так что левые NULL-ключи останутся одинокими». С такими мыслями я начал джойн здоровенных таблиц.
Время шло, и в моей почте начали скапливаться «письма счастья», предупреждающие о приближении к лимиту WORK.
И вот, несмотря на мои скрещенные пальцы, лимит достигается, очищая WORK, словно Fairy — противень на празднике Вилларриба. Начинается разбор полетов. И оказывается, что я напрасно считал, что в правом датасете не было NULL-ключей. Они еще как были, просто без целенаправленного поиска заметить их среди миллиона строк было довольно сложно. А что случилось, когда парочка NULL-ключей нашла друг друга? Они начали яростно джойниться, тем самым раздувая WORK. Вот как это примерно выглядело:
«Ок, — подумал я, — тогда просто ставлю условие IS NOT NULL на ключи правой таблицы». Сказано — сделано. LEFT JOIN отрабатывает, начинается анализ. И тут я замечаю, что для LEFT JOIN таблица имеет слишком хорошую полноту данных. Начинаю проверять — и правда, данных из левой таблицы, по которым не было совпадений в правой, просто нет. «Но почему? — подумал я. — Я же сделал LEFT JOIN!» Конечно, я сразу сообразил, что дело в условии IS NOT NULL на правой таблице. Но с первого взгляда это выглядело нелогично. Вот как это выглядело в моем мозгу:
Но по факту это было так:
То есть правило SQL таково, что сначала идет JOIN, а после включается WHERE. Таким образом я не только пожрал ресурсы на джойн NULL-ключей, но еще и не получил того, что мне нужно. В итоге я сделал еще один запрос на правую таблицу, отфильтровав NULL-ключи. И только после этого произвел LEFT JOIN.
Вот такая вот история, как можно оступиться на базовых вещах. Берегите место, следуйте правилу бритвы Оккама — не плодите сущности.
Комментарии (10)
Gritsuk
25.10.2021 11:47Насколько я помню, в SAS при джойне PROC SQL сначала создает декартово произведение таблиц, а уже потом применяет условия. Чтобы избежать лишние операции, можно использовать опции датасетов (where=(^missing(key2)), эта штука отбросит не проходящие условие записи еще на этапе чтения из датасета.
AlexNikiforov
25.10.2021 12:08-3NULL в SQL любит удивлять, что поделать.
declare @a int declare @b int set @a = 1 set @b = NULL if @a != @b select 'а не равно b' else select 'а равно b'
unfilled
25.10.2021 12:25+2Если не знаешь, что операции сравнения с null возвращают unknown, который не true, и не false, то да, сплошное удивление.
AlexNikiforov
25.10.2021 13:49Unknown <> false, но в итоге false.
unfilled
25.10.2021 13:58+2Логика не двоичная используется с null.
В else "идёт" то, что не true. unknown - не true, поэтому попадает в else.
abutorin
Ждём статью как Вы сделали запрос из 2х таблиц по 10млн. строк в каждой без указания связей и условий.
asketoff Автор
Дельное замечание. Thx! Что написание кода, что написание статей требует опыта. В следующий раз обогащу текст кодом, чтобы помимо веселых картинок был невеселый запрос.