Добавить в корзинуПозвонить
Найти в Дзене

Зоопарк булевых значений: как СУБД представляют понятие «истины» (True) // Байки ИТ-редактора

Некоторое время назад я переводил книгу PostgreSQL Mistakes and How to Avoid Them. Книга адресована администраторам СУБД и разработчикам, автор описывает разные грабли тонкие моменты, с которыми он сталкивался за свою многолетнюю практику работы с этой СУБД. Добравшись до главы 10.4 Not using proper BOOLEANs я вдруг осознал, что я явно недооценивал глубину темы «булевых» значений. Большинству этот тип данных известен по известным логическим значениям "ИСТИНА" и "ЛОЖЬ". Вообще, само название типа — boolean — отсылает нас к своему создателю, Джорджу Булю, английскому математику и логику, жившему в XIX-м веке. Он в душе не чаял, что его труды будут каким-то образом использованы для работы компьютеров и решал он тогда совсем другую задачу, в некотором плане даже более амбициозную — он думал о том, как облечь человеческую логику в математическую форму. В своем главном труде An Investigation of the Laws of Thought от 1854 г. он предложил систему, где сложные словесные утверждения можно
Оглавление

Некоторое время назад я переводил книгу PostgreSQL Mistakes and How to Avoid Them. Книга адресована администраторам СУБД и разработчикам, автор описывает разные грабли тонкие моменты, с которыми он сталкивался за свою многолетнюю практику работы с этой СУБД.

Добравшись до главы 10.4 Not using proper BOOLEANs я вдруг осознал, что я явно недооценивал глубину темы «булевых» значений. Большинству этот тип данных известен по известным логическим значениям "ИСТИНА" и "ЛОЖЬ".

Немного истории

Вообще, само название типа — boolean — отсылает нас к своему создателю, Джорджу Булю, английскому математику и логику, жившему в XIX-м веке. Он в душе не чаял, что его труды будут каким-то образом использованы для работы компьютеров и решал он тогда совсем другую задачу, в некотором плане даже более амбициозную — он думал о том, как облечь человеческую логику в математическую форму.

-2

В своем главном труде An Investigation of the Laws of Thought от 1854 г. он предложил систему, где сложные словесные утверждения можно было записать как алгебраические уравнения.

Ключевая идея Буля была проста и гениальна — любое высказывание может быть либо истинным, либо ложным. Всего два состояния — True или False. 1 или 0. Он создал алгебру с операторами И, ИЛИ, НЕ / AND, OR, NOT, которая позволяла манипулировать этими состояниями - истиной и ложью также формально, как обычная алгебра работает с числами.

И вообще, сам Буль считал, что его работа - это вклад не столько в математику, сколько в философию. Поэтому ни в каких снах ему не могло присниться то, что на его бинарной системе построят всю цифровую цивилизацию.

Про фрагмент из книги

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

Автор работал с базой данных MariaDB, и экспортировал из нее данные с результатами голосования в PostgreSQL. В этой базе была колонка "Voted" с типом значений boolean, т.е. проставлялась отметка, голосовал человек или нет:

-3

И вот на этом экспорте Postres выбрасывает ошибку:

psql:prod_dump.sql:24: ERROR: column "Voted" is of type boolean but

➥expression is of type integer

LINE 1:... "Voting"."Election2024" ("VoterID", "Voted") VALUES (500, 0);

HINT: You will need to rewrite or cast the expression.

Как такое может быть, если и там, и там тип данных boolean — где там можно ошибиться? После некоторых манипуляций, автор приходит к выводу, что, цитирую по своему переводу:

<MariaDB и MySQL хранят BOOLEAN внутри как TINYINT(1). Документация этих СУБД по типам BOOL/BOOLEAN подтверждает это:

Эти типы являются синонимами для TINYINT(1). Нулевое значение считается ложью, ненулевые — истиной.>

Перевожу на понятный: в MariaDB и MySQL — "истина" и "ложь" обозначаются как "1" и "0", а для Postres это строго — true и false.

Тут я припомнил, что в Python, эти же значения еще и чувствительны к регистру, то есть, если вы в коде напишете не True и False, а true и false — то Python выбросит ошибку типа. Вот тут я начал понимать, что с этими данными оказывается целый зоопарк, и ниже я покажу лишь его часть, на примере работы с СУБД (привожу по материалам статьи SQL Boolean Type: How to Use It in All Major Relational Databases).

  • MySQL и MariaDB, как уже было сказано, притворяются, что у них есть BOOLEAN, но на деле используют TINYINT(1). Экономно, но не по стандарту.
  • Microsoft SQL Server предлагает тип BIT, который может хранить 0, 1 или NULL. Тоже число, но хотя бы специализированное.
  • Oracle — это вообще отдельная история. До недавнего времени там в принципе не было булева типа. Разработчики выкручивались как могли: кто-то использовал CHAR(1) для записи 'Y' (Yes) и 'N' (No), кто-то — NUMBER(1). И только в 2023 году, в версии 23c, Oracle наконец-то добавил нативную поддержку BOOLEAN. Спустя каких-то 24 года после того, как тип был включен в стандарт SQL:1999.

На этом фоне PostgreSQL выглядит как последний защитник чистоты и порядка. Он единственный из этой компании, кто реализовал BOOLEAN именно так, как завещал стандарт: как отдельный тип данных, который хранит true, false и специальное значение UNKNOWN (неизвестность), представленное как NULL. Он даже любезно принимает строковые значения вроде 'yes', 'no', 'on', 'off' и числа 1 или 0 при вставке, но внутри хранит их в единственно правильном, каноническом виде.

Именно эта строгость и вызвала в упомянутой выше ситуации проблемы при миграции данных. Если вы переезжаете на PostgreSQL с другой СУБД, вам придется «очистить» свои данные, то есть превратить все эти единички, нолики, буквы 'Y' и 'N' в настоящие true и false.

Отдельно расскажу про значение unknown. Некоторое время оно вызывало у меня недоумение, типа, зачем нужно, если есть true и false? В реальном мире мы не всегда можем сказать "да" или "нет". Иногда правильный ответ — "неизвестно". Например, в таблице пользователей есть столбец "Согласен на рассылку". Если там стоит true, пользователь согласен. Если false — не согласен. А если там NULL (который и представляет UNKNOWN), это может означать, что мы его еще не спрашивали, или он пропустил этот пункт в анкете. Мы просто не знаем его воли. Это не "да" и не "нет". Это третье, совершенно отдельное состояние, и правильные СУБД умеют с ним работать.

В общем, как в старой поговорке — век живи, век учись, и все равно помрешь чайником.

Спасибо за внимание. Подписывайтесь, если было полезно.