Привет, Хабр! Меня зовут Сергей Барановский, я руководитель проектов по аналитике в Блоке по клиентскому опыту и сервису и сегодня я хочу поделиться наболевшим. Джойн таблиц — одна из самых базовых вещей в аналитике. Казалось бы, допустить здесь ошибку почти невозможно. И правда! Что может быть проще, чем стыковать таблицы ключ к ключу?! Ковыряться в носу и то сложнее — можно ненароком кровеносный сосуд задеть. И, потеряв бдительность из-за простоты процедуры, можно набрать корзину проблем на самых базовых вещах. Под катом — познавательный кейс для тех, кто ходит тропами 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)


  1. abutorin
    25.10.2021 11:31
    +1

    Ждём статью как Вы сделали запрос из 2х таблиц по 10млн. строк в каждой без указания связей и условий.


    1. asketoff Автор
      25.10.2021 11:53

      Дельное замечание. Thx! Что написание кода, что написание статей требует опыта. В следующий раз обогащу текст кодом, чтобы помимо веселых картинок был невеселый запрос.


  1. Vlad074
    25.10.2021 11:41

    В MS SQL NULL=NULL - истина?


    1. asketoff Автор
      25.10.2021 11:45

      В чистом MS SQL - нет. А вот в SQL, используемом в SAS, - вполне. Для меня это было хорошим напоминанием о важности изучения правил среды, в которой работаешь.


  1. Gritsuk
    25.10.2021 11:47

    Насколько я помню, в SAS при джойне PROC SQL сначала создает декартово произведение таблиц, а уже потом применяет условия. Чтобы избежать лишние операции, можно использовать опции датасетов (where=(^missing(key2)), эта штука отбросит не проходящие условие записи еще на этапе чтения из датасета.


  1. Olegun
    25.10.2021 11:57

    Очень жаль, что ни одного запроса на sql в статье не присутствует.


  1. AlexNikiforov
    25.10.2021 12:08
    -3

    NULL в SQL любит удивлять, что поделать.

    declare @a int
    declare @b int
    set @a = 1
    set @b = NULL
    if @a != @b select 'а не равно b'
    else select 'а равно b'


    1. unfilled
      25.10.2021 12:25
      +2

      Если не знаешь, что операции сравнения с null возвращают unknown, который не true, и не false, то да, сплошное удивление.


      1. AlexNikiforov
        25.10.2021 13:49

        Unknown <> false, но в итоге false.


        1. unfilled
          25.10.2021 13:58
          +2

          Логика не двоичная используется с null.

          В else "идёт" то, что не true. unknown - не true, поэтому попадает в else.