Есть вещи, которые не нравятся, есть вещи, которые бесят, есть вызывающие жгучий гнев, и есть ситуация с NULL в SQL.

Догма

Мы все выучили наизусть: NULL не равен NULL. И не неравен NULL. И не больше, и не меньше. И не не больше, и не не меньше. Там вот такая вот особая хтонь, про которую надо постоянно помнить и которую надо постоянно аккуратно бочком обходить. Почему так всё причудливо, вам, конечно, с удовольствием объяснят. Дело в том, что NULL это неизвестность. Тень мирового зла. Каждый NULL это маленькая смерть.

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

Любая операция с неизвестностью (кроме специально выкованных эльфами операторов проверки на неизвестность) должна давать неизвестность. Это догма. Попытка оспорить догму - ересь.

Но давайте всё же попытаемся разобраться в вопросе, не особо стесняясь задавать неудобные вопросы.

В чём проблема

Проблема в том, что NULL в SQL это сейчас единственное место в информационных технологиях, где реально "в Проде" применяется троичная логика. Всё было бы ничего, но:

  1. Далеко не все об этом догадываются, и работают с этим как с обычной двоичной логикой, отягощённой некой загадочной "фичей".

  2. Троичная логика не применяется нигде кроме вычисления условий. Вне условий результат UNDEFINED интерпретируется как FALSE. Даже внутри самого SQL.

  3. Всё остальное окружение – бэкенды, фронтенды, аналитические инструменты, мозги разработчиков, мозги пользователей – всё приспособлено работать с двоичной логикой, а с троичной логикой работает через пень колоду.

В родной двоичной у нас так:

A and B

A or B

A

not A

A \ B

false

true

A \ B

false

true

false

true

false

false

false

false

false

true

true

false

true

false

true

true

true

true

В троичной всё причудливее:

A and B

A or B

A

not A

A \ B

false

NULL

true

A \ B

false

NULL

true

false

true

false

false

false

false

false

false

NULL

true

NULL

NULL

NULL

false

NULL

NULL

NULL

NULL

NULL

true

true

false

true

false

NULL

true

true

true

true

true

И дополнительно:

A

A is NULL

A is not NULL

A is TRUE

A is FALSE

false

false

true

false

true

NULL

true

false

false

false

true

false

true

true

false

По сути, внутри вычисления условия применяется троичная логика, но потом к результату применяется "is TRUE", чтобы перепрыгнуть в нормальную двоичную логику, используемую дальше.

Потому что никто понятия не имеет, как сделать систему, целиком основанную на троичной логике.

Ситуацию можно было бы посчитать нормальной, если бы SQL был экспериментальным изделием, на котором академическое сообщество обкатывает безумные идеи. Но это уже давно далеко не так.

Вообще изучать эту троичную логику одно удовольствие. Она интересна, красива. Но когда переходишь к практическому использованию в реальных проектах, слов, кроме матерных, не остаётся.

А насколько вообще нужен NULL?

Может быть можно и без него? Тогда бы и проблема троичной логики ушла бы сама собой.

На это есть два соображения – одно практическое, другое глубоко теоретическое:

  • С практической точки зрения NULL – чрезвычайно удобная штука (если бы не троичная логика, конечно). Идеальный вариант для значения по умолчанию. Если по какой-то причине, коих кроме незнания ещё штук сто, нужно воздержаться от вписывания значения, NULL – лучший кандидат. Гораздо лучше, чем вписывать фейковое значение "родного" типа.

  • С точки зрения математической основы реляционных баз данных nullable колонки даже намного естественнее и роднее, чем "not null".

Раскрою второй пункт подробнее.

Почему NULL полноправный гражданин в реляционных базах данных

Знаете почему реляционные базы данных называются реляционными? Реляция (relation) это связь, отношение. Когда говорят, что база реляционная, связь чего с чем имеется в виду? Я провёл небольшое исследование и опросил коллег. Все они неплохие разработчики, давно имеющие дело с базами данных, и все мне ответили, что термин "relational" делает акцент на том, что там есть не только таблички, но и связи между ними по внешнему ключу. Стрелочки на Entity-Relationship (ага, вот оно!) диаграммах. Что, конечно же, неправильно.

Отношениями в теории реляционных баз данных являются не связи между таблицами, а сами таблицы. И даже не сами таблицы, а подмножества их колонок.

Рассмотрим такую таблицу:

id

Фамилия

Имя

Отчество

1

Иванов

Сергей

Петрович

2

Smith

John

...

Она задаёт отношения (те самые relations) между следующими множествами:

  1. Множество "сущностей", представленное колонкой id.

  2. Множество фамилий.

  3. Множество человеческих имён.

  4. Множество отчеств.

Факты, сохранённые в строчках, можно сформулировать так:

  • Человек #1 имеет фамилию Иванов, имя Сергей, отчество Петрович.

  • Человек #2 имеет фамилию Smith, имя John, отчество... ээээ... [смущается, плачет]... давайте впишем NULL.

Можно заметить, что по сути, каждая строчка содержит по три отдельных факта вида "человек #1 имеет фамилию Иванов", "человек #1 имеет имя Сергей" и т.д. Ах да, вторая строчка не содержит факта про отчество.

С точки зрения реляционной теории мы вполне вправе разделить таблицу persons на четыре отдельные: собственно persons, в которой остаётся только колонка id, и три связанные с ней по внешнему ключу таблицы person_lnames, person_fnames и person_mnames. Когда будет нужно вытащить всё вместе, будем вязать "колоночные" таблицы к основной внешним соединением. Что, конечно, не так удобно, но сути происходящего не меняет. За исключением того, что структура с четырьмя таблицами эквивалентна исходной только в том случае, если атрибуты Фамилия, Имя и Отчество все nullable. Жёсткий эквивалент "not NULL" в конструкции с четырьмя таблицами организовать не получится. При этом, что интересно, сами "колоночные" таблицы полностью "not NULL", а пустые значения возникают в результате внешнего соединения. Например, для Джона Смита просто нет соответствующей записи в таблице person_mnames.

Избавиться от хранения значений NULL в колонках можно, но они всё равно будут возникать в результатах внешних соединений. А если избавиться от внешних соединений, получим неполноту реализации реляционной алгебры.

Итак, NULL это не ошибка, не нелепица и не результат раздолбайства, а вещь, естественным и необходимым образом присутствующая в идейной основе реляционных баз данных. Об этом, кстати, ещё даже великий Кодд писал.

NULL и троичная логика

Троичная логика не нужна. Без неё здесь можно обойтись точно так же, как мы без неё обходимся во всех остальных случаях. Двоичной логики "TRUE - FALSE" достаточно для любых применений. Без лишних усложнений. Для этого достаточно понять и принять два простых правила:

  1. NULL равен NULL. Да, вот так, без глупостей. Единственное вполне объяснимое исключение – уникальный индекс на nullable колонке. Как мы помним, nullable колонка логически эквивалентна отдельной "колоночной" таблице, в которую в случае отсутствия значения просто не добавляется строка.

  2. NULL меньше любого не-NULL значения. Почему не больше? Да просто потому что POLA (Principle of least astonishment).

Насколько мне известно, не существует ни одного рационального аргумента в пользу того, что результат сравнения "NULL = NULL" должен быть чем-то кроме TRUE. Только всякое бессмысленное словоблудие об инфернальной природе пустоты. Больше нигде – ни в науке, ни в технике, ни в культуре, ни в искусстве нет такого, чтобы отношение идентичности "is" было слабее отношения равенства.

Что касается практической полезности троичной логики, то полезность эта отсутствует. По крайней мере, я не могу вспомнить ни одного случая, когда эта самая троичная магия мне для чего-то пригодилась. Она всегда помеха, которую приходится преодолевать.

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

Выводы

Нужно признать, что решение применить троичную логику для NULL было концептуальной ошибкой, последствия которой индустрия разгребает уже несколько десятилетий, каждый день тратя уйму ресурсов и проливая тонны пота и слёз. И не имея взамен ровным счётом ничего.

Спасибо, господа академики, опыт был интересным и поучительным. Теперь пора исправить ошибку и двигаться дальше. В любом случае, рано или поздно, ошибку придётся исправлять.

Идея для стартапа: форкнуть Постгрес и сделать Postgres BFE (Bullshit-free Edition). И посмотреть, как народу зайдёт такая еретическая идея. Да, не соответствует стандарту SQL, ну так это... стандарты надо нормальные делать.

Комментарии (130)


  1. 0x1000000
    26.08.2022 11:47
    +6

    Есть ещё один очень популярный язык программирование в котором всё время приходится учитывать троичную логику (и даже в каком-то смысле четверичную) — это JavaScript со своими undefined и null

    Скрытый текст
    var a = {};
    
    console.log(a.b); // undefined
    console.log(Object.keys(a)); // []
    
    a.b = undefined;
    console.log(a.b); // undefined
    console.log(Object.keys(a)); // ["b"]
    
    


    1. Amareis
      26.08.2022 12:06
      +1

      И не дай боже null через typeof проверять...


    1. slonopotamus
      27.08.2022 10:58
      +1

      В вашем примере всё становится логично, если считать что объекты в JS - это всего лишь Map<String, Object>. Разница между пустым Map и Map, в котором есть запись "b" -> undefined вполне очевидна. Это впрочем никак не оправдывает кучу другой дичи в JS.


    1. 3735928559
      28.08.2022 23:50

      Ну тогда уже стоит вспомнить и TypeScript, где проблема nullability решается на уровне типов. Или Kotlin.


  1. rrrad
    26.08.2022 11:49
    +5

    Из опыта собеседований на junior-middle позиции, не менее четверти кандидатов не знают как работает сравнение с null.

    В отличии от Oracle, в PostgreSQL тип boolean - это first class citizen, а значит троичную логику можно выпилить только с выпиливанием NULL-ов вообще. Если задуматься о смысле значения NULL, то правила работы с логическим NULL-ом устроены достаточно логично.

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

    Автор, конечно, волен замутить PostgreSQL с выпиленной двоичной логикой (хотя я бы посоветовал подумать о более корректном названии), но думаю, что он будет единственным пользователем. Никому не нужны ни с чем несовместимые системы.


    1. maslyaev Автор
      26.08.2022 16:18

      Монга не соответствует стандарту SQL, Эластик не соответствует, много кто ещё не соответствует. Но это не мешает людям ими с удовольствием пользоваться.

      Вообще, тенденция сейчас такова, что SQL потихоньку просачивается в самые неожиданные места. Не удивлюсь, если скоро приделают запросы к DOM в браузере. Если ещё не приделали. Тащить везде за собой эту глупую концептуальную ошибку с троичной логикой... ну, не обязательно, разве нет?


      1. rrrad
        26.08.2022 18:30
        +4

        Давайте я вам помогу: при анализе дампов памяти java в Memory Analyzer'е можно использовать sql-подобный язык для поиска объектов.

        Это всё не важно: есть стандарт, которому должны следовать (в меру возможностей) все уважающие себя RDBMS и он общепризнан. Ни монга, ни эластик RDBMS не являются. Давать такие примеры - это всё равно, что говорить, что несущие крылья для самолёта - это концептуальная ошибка, т.к. они занимают много места и вообще, у вертолёта Ми-8 нет таких крыльев и это не мешает ему летать.


        1. maslyaev Автор
          26.08.2022 23:13
          -1

          Реляционная алгебра тоже вполне применима к данным, лежащим в Монге или Эластике. Помню, как нарисовал ER-диаграмму монговской базы, и это оказалось вот прям очень полезно.

          Да, они не SQL. Они "Not only SQL". Но они тоже RDBMS. Не надо ставить знак равенства между SQL и RDBMS.


          1. rrrad
            27.08.2022 02:49
            +4

            Вы довольно таки широко трактуете термины, за пределами классического их использования. В такой трактовке, термины теряют свой смысл. Также при этом, PostgreSQL, внезапно, становится настоящей Not only SQL базой, при чём, возможно, в большей степени, чем вышеназванные, ведь она поддерживает настоящий полновесный диалект SQL, выполняя подавляющее большинство стандарта этого языка, а также позволяет использовать её как документоориентированную базу данных за счёт поддержки json/jsonb.

            То, что MongoDB может использоваться в качестве rdbms - это всё-таки в некотором роде её развитие (вызванное тем, что людям из соседней экосистемы хочется выполнять знакомые действия привычным способом), однако большинство источников их противопоставляют.


  1. m03r
    26.08.2022 11:53
    +21

    Если NULL будет равен сам себе, то внешние соединения по нулевым полям дадут совершенно неинтуитивный результат. Отсутствующее отчество у Джона Смита — это не то же, что отсутствующее отчество у Джимми Вонга


    1. KongEnGe
      26.08.2022 13:19
      +2

      Это как в разборе математических софизмов: ищи, где втихую обе части равенства на ноль домножили или разделили :)


    1. maslyaev Автор
      26.08.2022 13:54
      -1

      Вот тут как раз интуиция всегда оказывается на стороне того, что NULL=NULL. И чтобы вспомнить, что это "на самом деле" не так, приходится делать над собой усилие. Кстати, в некоторых случаях SQL работает без глупостей. Например:

      select mname, count(*)
      from persons
      group by mname

      Кто-нибудь страдает от такого хамского попрания догмы со стороны group by? Неа, все только наслаждаются.


      1. m03r
        26.08.2022 16:37
        +5

        В одном из ответов на stackoverflow приведены интересные цитаты из спецификации SQL. Если кратко: NULL друг другу не равны (not equal), но при этом они неразличимы (not distinct). Таким образом, GROUP BY не попирает догму, а просто использует иную концепцию.

        Собственно, SQL2003 определяет оператор IS DISTINCT FROM, а в MySQL есть <=>


        1. maslyaev Автор
          26.08.2022 21:25
          +2

          NULL друг другу не равны (not equal), но при этом они неразличимы (not distinct)

          Я же говорю, единственное место в наследии нашей цивилизации, где идентичность слабее равенства. Это как если бы в JS "===" давало true, а результат "==" интерпретировался бы как false.


          1. m03r
            27.08.2022 13:00

            Почему Вы упорно пытаетесь свести NOT DISTINCT к равенству/идентичности? Это просто отдельный концепт.

            Если уж говорить об аналогиях NULL среди типов данных языков программирования, то для меня это всегда казалось чем-то близким монаде MayBe или типу Option .

            А поскольку NULL может случиться где угодно, в Option получаются неявно завёрнуты вообще все значения.

            И distinct/равенство, отлично вписываются в эту концепцию: distinct сравнивает сами Option'ы (и возвращает bool), а равенство — их содержимое (и возвращает тоже Option). А вот в JS ничего подобного нет.


            1. maslyaev Автор
              27.08.2022 14:32

              а равенство — их содержимое (и возвращает тоже Option)

              Зачем? Какой в этом практический смысл? Можете привести пример кода, где это полезно?


              1. m03r
                27.08.2022 18:17
                +2

                Вам ни разу не приходилось делать JOIN по nullable-колонкам? Если ко всем NULLам из одной таблицы приджойнятся все NULLы из другой, будет хорошо и правильно?


      1. playermet
        27.08.2022 09:30
        +6

        Вот тут как раз интуиция всегда оказывается на стороне того, что NULL=NULL

        Почему? Просто NULL следует трактовать как неизвестное значение. Равно ли одно неизвестное значение другому неизвестному значению? Неизвестно. Ну т.е. NULL.


        1. mSnus
          27.08.2022 10:30

          Конечно, равны. Undefined === undefined.


        1. michael_v89
          27.08.2022 11:42

          Просто NULL следует трактовать как неизвестное значение.

          Почему? Почему не как отсутствующее значение? Равно ли отсутствие значения отсутствию значения? Да.


          1. playermet
            27.08.2022 18:01
            +2

            Можно и как отсутствующее. Только нужно понимать, что это не значение поля равняется отсутствию, это значение поля отсутствует в БД, а следовательно для БД оно неизвестно. Ну а дальше все то же, что я написал ранее.


            1. michael_v89
              27.08.2022 18:24

              отсутствует в БД, а следовательно для БД оно неизвестно

              Нет, отсутствие это именно отсутствие, а не присутствие неизвестного значения. У англичанина отчество именно отсутствует, а не имеется какое-то, но неизвестное БД. NULL показывает отсутствие значения, отсутствие значения равно отсутствию значения.


              это не значение поля равняется отсутствию

              Тут 2 значения NULL, а не одно. "Значение поля" в конкретной строке это NULL, это показывает отсутствие значения. Оно сравнивается например с константой из WHERE, которая тоже NULL и показывает, что нам нужны записи с отсутствием значения. То есть это не "значение поля равняется отсутствию", а "отсутствие равняется отсутствию".


              1. playermet
                27.08.2022 18:39
                +1

                У англичанина отчество именно отсутствует

                SQL не вкладывают какую-то дополнительную семантику в NULL. Сказано - значение отсутствует в БД, и все. Причин по которым оно отсутствует могут быть десятки, это уже не проблема СУБД.

                "отсутствие равняется отсутствию"

                Стремлением так это трактовать вы сами себе создаете проблемы на ровном месте, при том что исправить их кроме как созданием машины времени и нападения на авторов спецификации SQL невозможно.


                1. michael_v89
                  27.08.2022 18:48

                  Сказано — значение отсутствует в БД, и все.

                  Ну так я про это и говорю, значение именно отсутствует в БД, а не присутствует, но неизвестно какое. То есть аргументы, основанные на семантике неизвестности, некорректны.


                  Стремлением так это трактовать вы сами себе создаете проблемы на ровном месте

                  В языках программирования null == null работает без всяких проблем.


                  1. playermet
                    27.08.2022 19:05
                    +1

                    а не присутствует, но неизвестно какое.

                    Это то же самое. Если значение отсутствует в БД, то оно по определению неизвестно для БД. NULL это не часть диапазона возможных значений поля, это специальное слово, указывающее что значения нет. А потому и сравнивать по значению ничего нельзя - его нет.

                    В языках программирования null == null работает без всяких проблем.

                    В языках программирования у null совсем другая семантика, которая исторически уходит корнями в нулевой указатель на тип. В то же время в языках программирования NaN == NaN дает false, потому что слева нет определенного значения, и справа нет определенного значения, а результат сравнения двух неопределенных значений неизвестен.


                    1. michael_v89
                      27.08.2022 20:29
                      +2

                      Это то же самое. Если значение отсутствует в БД, то оно по определению неизвестно для БД.

                      Вот я как раз и пытаюсь объяснить, что это не то же самое. "Значение неизвестно" подразумевает, что значение есть, но какое точно, неизвестно. А его с большой вероятностью вообще нет. Это является в общем-то наиболее часто моделируемым случаем, потому что моделирование наличия какого-то неизвестного значения для практических целей бесполезно, потому что мы его никак не можем обработать. Там где это все-таки нужно, наличие какого-то неизвестного значения моделируется полем boolean, а не null.


                      В языках программирования у null совсем другая семантика

                      Семантика там абсолютно такая же, отсутствие значения, и при загрузке данных из БД NULL напрямую мапится в null языка программирования.


                      исторически уходит корнями в нулевой указатель на тип

                      Это как раз нулевой указатель появился исключительно для того, чтобы обозначать отсутствие значения. Ни для чего другого он не нужен, так как правильного значения по историческим причинам там быть не может.


        1. maslyaev Автор
          27.08.2022 11:59

          А можно пример запроса, в котором действительно оказывается удобно, что ((null = null) is true) даёт false?


          1. playermet
            27.08.2022 18:21

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


            1. maslyaev Автор
              27.08.2022 19:05

              Не надо слов. Покажите мне код.


              1. playermet
                27.08.2022 19:15
                +3

                CREATE TABLE foo (x int);
                CREATE TABLE bar (x int);
                INSERT INTO foo (x) VALUES (1), (2), (NULL), (NULL), (NULL);
                INSERT INTO bar (x) VALUES (1), (2), (NULL), (NULL), (NULL);
                SELECT * FROM foo INNER JOIN bar ON foo.x = bar.x;

                Сейчас результатом будут ожидаемые две строки. А если поменять поведение NULL, то результатом будут эти же две строки, а после них девять строк c NULL. Попробуйте все NULL в этом коде заменить на 5, например, и посмотрите сами.


                1. maslyaev Автор
                  27.08.2022 19:33
                  -2

                  Попробуйте все NULL в этом коде заменить на 5, например, и посмотрите сами.

                  А вот действительно, если колонки не nullable, и мы джойним по колонкам, в которых повторяются значения, кого будем ругать за то, что у нас вылезло 11 строк? Самих себя, или потребуем, чтобы операция сравнения "5 = 5" давала бы какую-нибудь хтонь, которая бы конвертировалась в булево как false?


                  1. playermet
                    27.08.2022 19:48
                    +1

                    LEFT JOIN постоянно делается по колонкам, в которых есть NULL. По сути, это главный его юзкейс. И в нем будет такой такой же эффект. Вы сейчас буквально просите сломать все запросы сложнее хелло-ворлдов. И все это только ради того, чтобы не писать "is" или "is not distinct from" вместо "=", которые нужны не сказать что повсеместно, и это не то чтобы большое отличие.


                    1. maslyaev Автор
                      27.08.2022 21:11
                      -1

                      Всё в порядке с хелловорлдами. Основной юзкейс лефт-джойна - хождение между табличками по внешним ключам. Если мы идём в прямом направлении, у нас nullable слева, но not null справа (потому что там это первичный ключ). А если идём в обратном направлении, первичный ключ у нас слева.

                      Если ради чего-то мы используем в джойне nullable и слева, и справа, то почти наверняка будем ожидать, что дублирующиеся NULLы будут вести себя так же, как дублирующиеся пятёрки. По крайней мере у меня с ходу не получается выдумать разумный пример для обратного.


                      1. m03r
                        27.08.2022 21:40
                        +2

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

                        Интересно, кто что себе покупает на ДР.

                        Типа: ON user.id = order.user_id AND user.bdate = DATE(order.paid_at).

                        В реальности я бы скорее смотрел заказы за неделю до дня рождения, т. е. BETWEEN bd - INTERVAL 7 DAY AND bd, и всё бы тоже прекрасно работало.


                      1. maslyaev Автор
                        28.08.2022 13:54
                        -1

                        Интересный пример. В этом случае следование стратегии "NULL=NULL" даст хоть и неправильный, но объяснимый результат. Ситуация, когда не отфильтровали пустые значения и получили гору мусора на выходе, не редкость и сейчас. Троичная логика от этого не защищает.


                      1. michael_v89
                        28.08.2022 16:33

                        ON user.id = order.user_id AND user.bdate = DATE(order.paid_at)

                        Ой нет, вот пожалуйста не надо продвигать эти подходы, когда в ON есть куча условий кроме соединения по ключам. Потом в таком запросе фиг разберешься, почему он иногда выдает неправильные результаты. Есть WHERE, все фильтры можно писать там.


                        Если нам нужны записи, у которых указана дата заказа и дата рождения пользователя, можно так и написать WHERE user.bdate != NULL AND order.paid_at != NULL, а не использовать хитрые хаки с отсечением части результатов в джойне. Так сразу понятно, что мы хотим получить.


                      1. m03r
                        28.08.2022 22:35

                        Да хоть во WHERE, не нужны дополнительные проверки на NULL, потому что то же равенство даст тот же NULL и строки не будут выбраны


                      1. michael_v89
                        28.08.2022 23:01

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


                      1. m03r
                        29.08.2022 00:27

                        Насчёт правильного условия в JOIN — согласен, сравнение дат лучше во WHERE.

                        Тем не менее, для меня очевидно и ожидаемо, что WHERE user.bdate = DATE(order.ctime) отбросит нулевые значения.


                      1. michael_v89
                        29.08.2022 09:02

                        Это для вас очевидно, потому что вы его писали. А завтра в выводимых данных найдут баг, и другому программисту придется разбираться в вашем запросе, и он не знает, какие из всех возможных побочных эффектов вы использовали намеренно, а про какие не подумали, из-за чего и возник баг.


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


    1. shai_hulud
      26.08.2022 13:54

      Вроде в стандарте скуэльэтот тип данных назывался unknown и сразу все было ппонятно почему нельзя сравнивать неизвестные значения.


      1. mSnus
        27.08.2022 11:21
        -1

        А почему нельзя? Неизвестность и есть неизвестность


  1. sukharichev
    26.08.2022 12:09
    -1

    Я, конечно, вообще ничего не понимаю в базах данных, но по-моему, в примере отчество Джона Смита не null а эмм... NationallyTraditionallyNotApplicable и его не надо делать NULL и на уровне приложения надо обрабатывать ситуацию NationallyTraditionallyNotApplicable, правда же? Просто это лишние телодвижения, неопределенность, потому что этих самых национальных традиций именования людей миллион вариаций, и никто не будет этим заморачиваться. Зато так у нас никакой троичной логики, а просто множество дополнительных двоичных веток с вариациями национальных имен. Правильно я понимаю?


    1. pavel_raskin
      26.08.2022 12:41
      +1

      Как мне кажется, замена задачи "ФИО/ФИ" на " NationallyTraditionallyNotApplicable" всего лишь передвинет вопрос NULL в другую часть предметной области, плюс неоправданно усложнит манипуляцию с данными.

      Ну и нужно понимать, что автор выбрал пример "ФИО/ФИ" ради иллюстрации своих рассуждений про NULL, а не наоборот.


      1. sukharichev
        26.08.2022 13:50
        +2

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


        1. nin-jin
          27.08.2022 21:27
          +1

          Ключевой момент тут в том, что NULL не участвует в релейшенах. По хорошему, это, конечно, должно быть не одно захардкоженное значение, а enum с перечислением значений не допускающих джойны. Что-то типа:

          CREATE TABLE foo (x int | #0 | #broken | #unknown | #meaningless | #secret);

          Да, тут ещё и ноль не будет джойниться.


  1. Tanner
    26.08.2022 12:40

    Наверное, то, о чём вы говорите, называется Null Object. В Python сделано примерно так, там None == None` даёт True.


    1. maslyaev Автор
      26.08.2022 14:13
      -1

      Ну вот да, в Питоне None==None, и никто горя не знает. А совет использовать там "is" вместо "==" при проверке на None исключительно потому, что некоторые всратые библиотеки могут переопределять оператор равенства для своих объектов, и делать это неочевидным образом. Например, могут упороться и применить троичную логику.


      1. Tanner
        26.08.2022 14:22

        Дело даже не в равенстве как таковом, а в том, что переменная со значением None – это не пустая ссылка, а ссылка на объект, который ведёт себя как объект. Например, реализует методы. Сравнение, например.

        В случае SQL нулевой объект в качестве NULL мог бы реализовывать поведение, подобающее для отсутствующего отношения. Или мы слишком многого хотим от SQL?


        1. rrrad
          26.08.2022 18:35

          Требовать от SQL поведения аналогичного императивным языкам - очень странно. Например, в SQL неопределён порядок вычисления булевых выражений. Нужно объяснять, почему так сделано?


          1. 0xd34df00d
            27.08.2022 18:28
            +1

            Деление на императивные и неимперативные языки переоценено, так что объясните, пожалуйста, если не сложно.


            1. rrrad
              28.08.2022 15:59

              По поводу порядка вычисления булевых выражений (точнее - предикатов). Очевидно, чтобы планировщик мог свободно решать, какие из предикатов применять в каком порядке, руководствуясь их селективностью, а не порядком следования.


              1. 0xd34df00d
                28.08.2022 17:12
                +1

                Насколько я знаю, в реализациях SQL не все функции тотальны, поэтому от порядка вычисления успех (или неуспех) вычисления вполне себе может зависеть.


                1. rrrad
                  28.08.2022 23:39

                  По стандарту SQL, все части предиката будут вычислены. Поэтому обычно в SQL условия типа (N - число) AND (to_number(N) > 5) приведут к ошибке. По опыту, даже оборачивание первого условие внутрь подзапроса не всегда поможет. Хотите жесткой определённости последовательности вычислений? Есть вариант с использованием оператора case, но только, например, в PostgreSQL если аргумент - литерал или входящий параметр (который также может считаться литералом, т.к. первоначально учитывается при планировании), а функция детерминирована вообще (immutable) или в пределах транзакции (stable), то планировщик может попытаться её вычислить даже если она находится в неработающей ветке (просто потому что так работает последовательное упрощение дерева вычислений).


  1. Akina
    26.08.2022 12:53
    +5

    Троичная логика не применяется нигде кроме вычисления условий. Вне условий результат UNDEFINED интерпретируется как FALSE. Даже внутри самого SQL.

    Увы Вам. Будучи полученным в CHECK constraint, NULL разрешает создание/изменение записи, т.е. интерпретируется как / выполняет тот же выбор, что и TRUE.


    1. maslyaev Автор
      26.08.2022 13:16

      Хахаха, больше безумия богу безумия


    1. rrrad
      26.08.2022 18:36
      +2

      в данном случае работает логика "мы не можем проверить эту строчку, поэтому пропускаем check". Нужно другое поведение? Никто не мешает использовать coalesce.

      Ну или другая интерпретация: check constraint проверяет строку на соответствие условию (expr) = false. При выполнении - кидает ошибку.

      Вообще, в SQL достаточно странностей (например - работа типов varchar(n)), но едва ли к странностям можно отнести работу NULL-ов в булевых выражениях.


  1. avshkol
    26.08.2022 13:17
    +10

    Насколько мне известно, не существует ни одного рационального аргумента в пользу того, что результат сравнения "NULL = NULL" должен быть чем-то кроме TRUE. 

    Нет, NULL - это неопределенность, например, в булевом поле это означает: "пока нет данных, 0 это или 1". Поэтому два NULL могут оказаться разными, а могут и одинаковыми (поэтому - неопределенность, NULL при сравнении NULL == NULL).

    Далее NULL is NULL = True, то есть на вопрос "это неопределенность?" отвечаем, видя NULL - да, это проклятая неопределенность!

    А or B, если А=1, будет 1, даже если B неопределено (каким бы оно не было).

    А or B, если А=0, будет NULL, даже если B неопределено (то есть на месте В возможен и 1 и 0).

    А and B, если А=0, будет 0, при любом, даже неопределенном В

    А and B, если А=1, будет NULL, поскольку возможны оба варианта.

    По-моему, все понятно и логично. В примере с отчеством нужно понимать, что NULL не равно текстовой строке "", хотя в печати обычно так и выводится. Вместо NULL может быть и "" (нет отчества - то есть фактически - "нет ни одного символа отчества"), и "мы пока не знаем отчества, запишем, когда узнаем".


    1. maslyaev Автор
      26.08.2022 21:14
      +2

      NULL - это неопределенность

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

      Вообще, обстоятельств, почему клеточка в таблице осталась пустой, сильно больше, чем два. Кодировать их отдельными null-like значениями (null для одного, undefined для другого, void для пятого, empty для десятого) - только всех запутать. Одного NULLа достаточно, а если действительно будет нужно уточнение, мы точно придумаем, как его записать в базу.


      1. avshkol
        26.08.2022 21:43
        +1

        NULL-неприменимость можно убрать, изменив схему данных. Если мы для товара хотим ввести признак «шаг резьбы» и «под крестовую отвертку», нам лучше создать отдельную таблицу/таблицы, где будет id товара и этот признак.


        1. maslyaev Автор
          26.08.2022 22:24
          +2

          Да, как в примере с выносом фамилии, имени и отчества в отдельные таблицы, где атрибуты будут not null, но в результатах запроса всё равно будем иметь тот самый null.


          1. Sergeant101
            27.08.2022 09:18

            А если вместо null иметь в таблице запись "-" (прочерк), тогда во первых значение отчества будет точно определено - отчества нет, его не забыли написать, просто нет.


            1. mSnus
              27.08.2022 11:30

              Прочерк - это определенное состояние, т.е. вы точно знаете, что отчества нет. А NULL - неопределенное, когда вы ещё не спросили Джона, есть ли у него middle name


            1. maslyaev Автор
              27.08.2022 12:22
              +3

              Вписывание "специальных" фейковых значений - очень плохая практика.


            1. 0xd34df00d
              27.08.2022 18:31
              +2

              А если иметь вторую колонку, где 0 означает валидное значение в первой, 1 — его отсутствие, 2 — неизвестность… тьфу ты, кажется, я переизобретаю кодирование ADT в памяти после компиляции.


      1. dmitryvolochaev
        27.08.2022 01:47

        Ну вот почему Питон не дает наследоваться от NoneType? None ведет себя как объект, но NoneType не ведет себя как класс


    1. mSnus
      27.08.2022 11:24

      пока нет данных, 0 это или 1". Поэтому два NULL могут оказаться разными, а могут и одинаковыми

      Нет же, как только появляются данные, они перестают быть NULL. А пока "неизвестно" - нам одинаково неизвестно про любую переменную, что там в ней.


      1. nin-jin
        27.08.2022 21:37

        Пустая коробка и коробка с неизвестным рычащим зверем внутри - две большие разницы.


  1. s_f1
    26.08.2022 13:19
    +1

    Насколько мне известно, не существует ни одного рационального аргумента в пользу того, что результат сравнения «NULL = NULL» должен быть чем-то кроме TRUE.
    Что есть рациональный аргумент? Вот у нас есть
    HAS_DOOR boolean
    DOOR_COLOUR colour
    Можно ли сказать, что все дома без дверей имеют одинаковый цвет двери? Имхо тут найдётся аргументация для противоположных ответов, и, соответственно, для противоположных взглядов на вопрос равенства NULL друг другу.


    1. maslyaev Автор
      26.08.2022 14:53
      -1

      Если придерживаться догмы, то да. Но когда дело дойдёт до дела, равенство "никаких" цветов между собой будет наименее удивляющим поведением системы.


    1. Deosis
      26.08.2022 15:20
      +5

      Вообще-то про цвет отсутствующих дверей можно говорить что угодно.

      Что все отсутствующие двери одного цвета, либо у каждой отсутствующей двери уникальный цвет.

      Эти утверждения непроверяемые.


      1. slonopotamus
        27.08.2022 11:47
        +1

        Для такого даже специальный термин есть: vacuous truth


      1. nin-jin
        27.08.2022 21:40
        -2

        Вас обманули. Про цвет несуществующих дверей ничего говорить нельзя, кроме того, что их не существует. Любые другие утверждения являются абсурдом.


        1. 0xd34df00d
          27.08.2022 23:33

          У вас опять ваша четверичная логика пошла? Не, я не против продолжить разговор — в конце концов, вы мне так и не ответили за аксиому исключённого десятого.


    1. michael_v89
      26.08.2022 16:04
      +1

      Можно ли сказать, что все дома без дверей имеют одинаковый цвет двери?

      А они не имеют одинаковый цвет ни в каком смысле. Типы "сolour null" и "colour not null" отличаются тем, что для null есть отдельный скрытый флаг. Вот он и показывает признак "запись имеет свойство цвет". Когда этот флаг true, это означает, что запись информации о цвете не имеет, и то значение, которое записано в соседних байтах где по схеме хранится фактический colour, не имеет смысла, и учитывать его не надо.


      NULL показывает отсутствие значения в информационной системе. И в этом смысле да, все дома без дверей имеют одинаковый признак, что цвета двери у них нет, потому и равенство между ними выглядит логично.


      1. nin-jin
        27.08.2022 21:43

        все дома без дверей имеют одинаковый признак, что цвета двери у них нет, потому и равенство между ними по этому признаку выглядит бредово

        Пофиксил, не благодарите.


        1. michael_v89
          27.08.2022 22:00

          Утверждение "у этого дома нет цвета двери и у того дома нет цвета двери, в этом они одинаковы" выглядит бредово, а утверждение "у этого дома нет цвета двери и у того дома нет цвета двери, в этом они различаются" выглядит не бредово? Не, это чушь какая-то.


          1. nin-jin
            27.08.2022 22:13

            Обратите внимание, что в обоих ваших утверждениях нет сравнения по цвету ("у этих домов одинаковые цвета дверей" или "left.doorcolor === right.doorcolor"), но есть сравнение по наличию признака ("left has-attribute doorcolor === right has-attribute doorcolor" или "left.doorcolor is NULL and right.doorcolor is NULL").


            1. michael_v89
              27.08.2022 22:27

              Обратите внимание, что в обоих ваших утверждениях нет сравнения по цвету

              Естественно, я про это и сказал. "для null есть отдельный скрытый флаг. Вот он и показывает признак "запись имеет свойство цвет"".


    1. mSnus
      28.08.2022 02:06

      IS_DAY boolean CAT_COLOR color

      Можно ли сказать, что ночью все кошки серы?


  1. Dekmabot
    26.08.2022 14:20
    +2

    А зачем вы сравниваете null с boolean-значениями?

    Null идеально подходит для обозначения отсутствия внешней связи. Используйте его по назначению, и тьма рассеется.


    1. maslyaev Автор
      26.08.2022 15:19

      Домен boolean-значений такой же, как и все другие, в том числе и "сущностные" домены. Не хуже и не лучше. И boolean-колонка это relation между доменом первичного ключа и boolean-домена. В случае отсутствия relation будет честный null.

      (впрочем, это не отменяет того, что за nullable boolean колонки нужно бить по рукам)


      1. MonkAlex
        26.08.2022 17:36
        +6

        (впрочем, это не отменяет того, что за nullable boolean колонки нужно бить по рукам)

        Почему? Жили у нас клиенты в БД.

        В каком то году, государство обязало нас уведомлять их о каких-то специфических вещах, из разряда "примите новые условия обслуживания".

        И наивная реализация будет как раз nullable boolean - клиент либо ещё не заходил и его решение - null, либо зашел и решил будет он пользоваться дальше (true), либо нет (false).

        Быстрая, наивная и вполне удобная реализация.

        Да, можно завести отдельную табличку, создавать в ней записи, наличие записи - факт действия пользователя, все дела. Но обязательно ли это? Очевидно, нет.


        1. Delta-f
          26.08.2022 18:20
          +1

          Поддерживаю: банальная задача - Таблица А со справочником товаров и таблица В с весогабаритными характеристиками товаров, связаны с собой по артикулу товара. Поиск товаров, которые есть в таблице А, но не имеют описания ВГХ в таблице В, происходит элементарным лефт джойном по условию "артикул товара в таблице В как раз NULL".


        1. AnthonyMikh
          26.08.2022 19:20
          +1

          Ну так это концептуально неправильная реализация, вообще говоря. NULL слишком перегружен смыслами. Означает ли NULL, что мы не знаем, что решил клиент? Нет, тут смысл вполне конкретный: клиент ещё не сделал решения. Собственно, все три варианта вполне себе покрываются отдельным типом:


          enum ClientDecision {
              NotDecided,
              Agreed,
              Disagreed,
          }

          И для значения этого типа факт равенства одного NotDecided другому — абсолютно логичная вещь.


          Но, разумеется, пользовательские сумм-типы — это слишком сложная вещь для SQL. ????


          1. MonkAlex
            26.08.2022 19:28
            +1

            Означает ли NULL, что мы не знаем, что решил клиент?

            И это решение программиста\бизнеса. В данном случае - да, клиент ещё не сделал решения.

            Делать отдельный тип на то, что прекрасно закрывается стандартным типом - оверинжениринг.

            ПС: навскидку кстати не уверен, даже если делать тип и прочее - по моему скулю дешевле будет создать колонку со значениями в null, чем колонку со значениями в NotDecided, что на большой БД может быть существенно.


            1. AnthonyMikh
              26.08.2022 23:12

              Делать отдельный тип на то, что прекрасно закрывается стандартным типом — оверинжениринг.

              прекрасно

              Вовсе нет. Как вы зафиксируете семантику NULL для этой колонки? БД об этом ничего не знает. И, как я уже сказал, в данном случае неравенство NULL самому себе бессмысленно.


              1. MonkAlex
                27.08.2022 04:43

                Я, честно, не понял вопроса. Что такое семантика null для колонки?

                И зачем нужно иметь какой то смысл в "неравенство NULL самому себе" тоже не понял.


          1. mSnus
            27.08.2022 11:37
            +1

            А также: NotYetQuestioned, StillThinking, DataLostOnTheWay, PodpisNerazborchiva. Но если бизнесу важно только знать - получен ли определенный ответ, и если да, то какой - тогда NULL, TRUE, FALSE.


        1. vadimr
          28.08.2022 16:27

          Вообще тут описаны просто три равноправные состояния документа, и кошерно будет поле CHAR (1) со значениями вроде Y/N/U.


          1. MonkAlex
            28.08.2022 16:41

            Есть ли у вашего предложения плюсы перед nullable boolean? Потому что я вижу только (из небольших минусов) необходимость работы с константами в процедурах либо на бэке (этими самыми YNU) и необходимость явно заполнять дефолтное значение, что на больших объемах тоже не бесплатно.


            1. vadimr
              28.08.2022 16:51

              Тут нет дефолтного значения, это фактический статус документа, который нужно как-то определить (узнать). Эти три значения сами по себе не исключают возможности использования и NULL тоже, как признака неизвестности.


              1. MonkAlex
                28.08.2022 17:01

                Так я неспроста описал бизнес кейс в первом посте.

                Какая бизнесовая разница у вас между null и U?

                И ещё раз, вопрос был - в чём плюс вашего предложения с char.


                1. vadimr
                  28.08.2022 17:26

                  U - о документе известно, что он не подписывался. NULL - нет информации о статусе документа (например, в нашу базу интегрируются данные нового Урюпинского дочернего предприятия, которые до того велись в Урюпинске нерегулярно и на бересте).

                  Плюс в том, что NULL не предназначен для кодирования содержательного значения атрибута. Соответственно, дальше не возникнут такие проблемы, как описаны автором.


                  1. MonkAlex
                    28.08.2022 17:32

                    Так с точки зрения бизнеса оно равноценно же.

                    В случае пока у клиента null или U - мы ему предлагаем ознакомиться с новыми условиями и принять или отказаться.

                    Т.е. с точки зрения поведения нашей системы - нет разницы, нет смысла делать так.


                    1. vadimr
                      28.08.2022 17:53

                      Если он уже подписал или отказался, надо выяснить статус, а не повторно предоставлять. Иначе получится, что он может два раза сделать разные выборы, что приведёт к юридической коллизии.

                      Сценарии тяжело обсуждать вне конкретики. Но и при проектировании БД редко мы знаем всю конкретику досконально. Я бы не взял ответственность объединять эти случаи на уровне хранения данных.


  1. AnthonyMikh
    26.08.2022 19:26
    +1

    Идея для стартапа: форкнуть Постгрес и сделать Postgres BFE (Bullshit-free Edition). И посмотреть, как народу зайдёт такая еретическая идея.

    Рвение ценю, но Postgres — та ещё хтонь, её невозможно развивать в одиночку.


    А насчёт темы: я вот недавно читал папир с формализацией SQL с NULL (формализации SQL были и раньше, но они не брали NULL в расчёт), и там, в частности, было показано, что троичная логика в плане выразительности не имеет никаких преимуществ перед двоичной: запрос в двоичной логике можно перевести в запрос на троичной логике и наоборот без потери смысла.


    1. vadimr
      27.08.2022 10:48
      +1

      Я вам страшную вещь скажу, то и другое равномощно машине Тьюринга.


  1. Delta-f
    27.08.2022 15:26
    +1

    Вот чего автор привязался к троичной логике в SQL-92, она работает нормально, и вполне себе используется теми, кому она нужна )
    Предлагаю- следующую тему для систем на COBOLе пусть обсудит. Вот это гораздо интереснее (нет).


    1. maslyaev Автор
      27.08.2022 17:16
      +1

      Очень хотелось бы посмотреть на пример запрса, в котором троичная логика оказывается полезна. Вот пример, где она мешает:

      select *
      from persons
      where mname = :MName
        or mname is null
          and :MName is null

      где :MName это подставляемый из переменной параметр.

      Мерзкий костыль, разве нет? Конструкции, подобные этому "or ..." приходится городить постоянно. Особенно оно доставляет в сложных запросах с множеством джойнов и нетривиальными условиями. Обход этой дебильной "фичи" уже давно доведён до автоматизма, но всё равно время от времени где-нибудь да словишь западло. Это как головная боль, к которой невозможно привыкнуть.

      Всё ещё надеюсь увидеть пример запроса, где троичная логика полезна, но пока что мне что-то подсказывает, что жду зря.


      1. vadimr
        27.08.2022 18:29

        У вас неправильно спроектирована база данных, в таком случае целесообразно использовать пустую строку, а не null.


        1. maslyaev Автор
          27.08.2022 19:03
          -1

          Так лучше?

          select *
          from persons
            left outer join person_mnames
              on person_mnames.person_id = person.id
          where person_mnames.mname = :MName
            or person_mnames.mname is null
              and :MName is null
          


          1. vadimr
            27.08.2022 19:08

            В классическом SQL времён коддовской DB2 вообще не было операций outer join. Будьте проще, и люди к вам потянутся, а таблицы будут нормализованы.


            1. maslyaev Автор
              27.08.2022 19:47

              Хахаха, когда-то и телефоны только по проводам были.


          1. miksoft
            27.08.2022 21:58
            +1

            Напомнило ситуацию, когда для поля person_mnames.mname надо было различать NULL, хранящийся в приджойненной записи, и NULL, возникший из-за неприджойненной записи.


        1. miksoft
          27.08.2022 21:59
          +2

          Сумятицы добавляет тот факт, что в некоторых СУБД пустая строка тождественна NULL, а в некоторых это разные вещи.


      1. playermet
        27.08.2022 18:47

        Ваш пример можно легко переписать в удобном виде:

        select * from persons where mname is :MName;


        1. maslyaev Автор
          27.08.2022 19:13

          Это не везде работает. В Посгресе, например, если параметр не null, не true и не false, отвалится с ошибкой.


          1. playermet
            27.08.2022 19:21
            +1

            Вот так в постгресе точно должно работать:

            select * from persons where mname is not distinct from :MName;


            1. maslyaev Автор
              27.08.2022 22:44

              Прикольно. В Постгресе "1 is 1" не работает, а "1 is not distinct from 1" работает. А в SQLite наоборот.

              В любом случае 99.9% из миллионов тех, кто пишет на SQL, голосуют ногами за "=". Потому что всякое такое "is not distinct from" это долбаная экзотика, даже не имеющая единообразной поддержки со стороны основных игроков.

              Ну ладно, с равенством (пардон, с нотдистинктностью) разобрались. Что будем делать с другими парами операторов сравнения, от которых нам нужна работа по закону исключённого третьего, а мы получаем в лицо закон исключённого четвёртого? Я имею в виду меньше/не меньше и больше/не больше?


              1. m03r
                27.08.2022 23:53
                +1

                А как по-вашему 1 > NULL — это истина или ложь? А 1 < NULL?

                Если я хочу сравнить с числом средний чек пользователя, а у него покупок нет, то AVG(amount) будет NULL, даже если вообще вся база у меня спроектирована без нулевых колонок.

                Как ещё предложите обрабатывать такие случаи?


                1. maslyaev Автор
                  28.08.2022 13:02

                  С точки зрения Мировой Истины, Сияющей, Вечной и Неизменной, вопрос "что больше, 1 или неизвестно сколько", смысла не имеет. Но когда мы сортируем nullable-колонку по возрастанию, "nulls first" является наименее удивительным поведением. Соответственно, если по убыванию, то "nulls last". Именно поэтому "NULL < 1" должно давать true.

                  Можно, конечно, сделать наоборот. С позиции Сияющей Истины пофиг. Но с точки зрения least astonishment совсем не пофиг.


                  1. nin-jin
                    28.08.2022 15:48

                    А если нужен nulls last? Тут больше проблема в слабой типизации. По хорошему программист тут обязан сделать явное приведение обоих операндов к единому типу, а не полагаться, что свои иррациональные ожидания.


                    1. maslyaev Автор
                      28.08.2022 16:16

                      Тогда, как и раньше, пишем order by ... nulls last.
                      Фишка в том, чтобы применяемое по умолчанию упорядочивание стопроцентно соответствовало логике операций сравнения на больше-меньше. Иначе западло и источник багов.


                      1. nin-jin
                        28.08.2022 16:23

                        А вы географические координаты как будете сравнивать на больше/меньше?


                      1. maslyaev Автор
                        28.08.2022 17:08
                        -1

                        Понимаете ли, нам совершенно плевать на глобальную экзистенциальную истинность того, что вот эта пара координат больше вон той другой. Нам всего лишь нужно отношение упорядочивания, которое работает понятным, единообразным и неабсурдным образом.
                        Вот, например, кому-нибудь плохо от того, что "Больше" < "Меньше" даёт true?


              1. nin-jin
                28.08.2022 01:51

                В реальной жизни "закон исключённого третьего" работает примерно никогда, так как почти всегда есть та или иная форма неопределённости (не задано, бессмысленно, ещё не загружено, удалено, да и банальное всё не так однозначно). Бинарная логика - это математическая абстракция, работающая только в своём мани-мирке.


                1. maslyaev Автор
                  28.08.2022 12:31

                  У нас и 2+2 это математическая абстракция, живущая только в своём тавтологически самом на себя замкнутом мини-мирке (см. "Логико-философский трактат" Витгенштейна), но это нам не мешает использовать арифметику как простой, понятный, надёжный и полезный инструмент.


                  1. nin-jin
                    28.08.2022 15:51

                    Только 2+2 не противоречит реальности.


                    1. maslyaev Автор
                      28.08.2022 16:18
                      -2

                      Иногда противоречит. Хотите пример?


                    1. maslyaev Автор
                      28.08.2022 19:49
                      -3

                      Пример: скидка 2% плюс ещё одна скидка 2% даёт общую скидку 3.96%
                      При этом наценка 2% плюс ещё наценка 2% будет 4.04% наценки. Получается прямо как в том анекдоте - "а мы покупаем или продаём?"
                      2+2=4 только когда применяем правило к аддитивным величинам. А вопрос о том, являются ли величины аддитивными, обычно вне математики.


                      1. nin-jin
                        28.08.2022 22:30

                        И где вы в реальности проценты нашли? ±2% - это короткая запись для множителя ( 100 ± 2 )/100


                      1. maslyaev Автор
                        28.08.2022 23:38
                        -3

                        Ещё пример. Вчера на улице было 2 десятка градусов, и сегодня 2 десятка. Итого в выходные у нас было 40°С, ага?


        1. miksoft
          28.08.2022 12:53
          +1

          В некоторых СУБД можно так:

          select * from persons where mname <=> :MName


      1. michael_v89
        27.08.2022 18:49

        С темой статьи согласен, но вообще обычно это делается на уровне приложения в квери билдере, если параметр null, он ставит "is", если не null, ставит "=".


        1. maslyaev Автор
          27.08.2022 19:19

          Ага, обкостылить эту дурацкую фичу специальным ифом в коде бэкенда. Хрен редьки не слаще.


          1. michael_v89
            27.08.2022 20:44

            А какая вам разница, если вам не надо вручную это писать, ни код бэкенда, ни сам SQL? Поставили через пакетный менеджер и пишете как удобно. Вам важнее понятность кода вашего приложения или количество ифов где-то внутри сторонней библиотеки?


            1. maslyaev Автор
              27.08.2022 23:25

              Речь о всяких вещах типа Dbeaver?


              1. michael_v89
                28.08.2022 01:33

                Нет, просто код фреймворка.


                1. maslyaev Автор
                  28.08.2022 16:22

                  Некоторые ORMки сами умеют это обкостылевать, некоторые (например, SQLAlchemy) нет. И я не уверен на сто процентов, действительно ли я хочу, чтобы ORM за меня додумывал этот момент.