Каков оптимальный способ хранения двоичных флагов / логических значений в каждом ядре базы данных? - PullRequest
6 голосов
/ 27 декабря 2010

Я видел несколько возможных подходов (в некоторых движках баз данных некоторые из них являются синонимами):

  1. TINYINT (1)
  2. BOOL
  3. БИТ (1)
  4. ENUM (0,1)
  5. CHAR (0) NULL

Следует отметить все основные движки баз данных, поддерживаемые PHP, но в качестве ссылки будет еще лучше, если будут отмечены и другие движки.

Я прошу проект, который лучше всего оптимизирован для чтения . например ВЫБОР с полем флага в условии WHERE или GROUP BY флагом. Производительность гораздо важнее, чем объем памяти (кроме случаев, когда размер влияет на производительность).

И еще несколько деталей:

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

Кроме того, если это имеет значение, если в строке есть только один (или несколько) флаг, против многих (или множества) флагов, это следует отметить.

Кстати, я где-то в SO прочитал следующее:

Использование логического значения может сделать то же самое, что и используя tinyint, однако он имеет Преимущество семантической передачи каково ваше намерение, и это чего-то стоит.

Ну, в моем случае это ничего не стоит, потому что каждая таблица представлена ​​классом в моем приложении, и все явно определено в классе и хорошо документировано.

Ответы [ 3 ]

6 голосов
/ 27 декабря 2010

Этот ответ предназначен для стандартного SQL ISO / IEC / ANSI и включает лучшие бесплатные pretend-SQL.

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

A. Категория первая

(1) (4) и (5) содержат несколько возможных значений и относятся к одной категории. Все можно легко и эффективно использовать в предложении WHERE. У них одинаковое хранилище, поэтому ни хранилище, ни производительность чтения не являются проблемой. Поэтому оставшийся выбор просто основан на фактическом типе данных для цели столбца.

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

B. Категория вторая

(2) и (3) являются двухзначными элементами: True / False; Мужской женский; Оживший мертвец. Эта категория отличается от первой категории. Его обработка как в вашей модели данных, так и в каждой платформе, различна. BOOLEAN - это просто синоним BIT, это одно и то же. Юридически (с точки зрения SQL) все платформы, совместимые с SQL, обрабатываются одинаково, и в предложении WHERE проблем нет.

Разница в производительности зависит от платформы. Sybase и DB2 упаковывают до 8 бит в один байт (здесь не имеет значения хранилище) и отображают степень двойки на лету, поэтому производительность действительно хорошая. Oracle делает разные вещи в каждой версии, и я видел, что разработчики моделей используют CHAR (1) вместо BIT, чтобы преодолеть проблемы с производительностью. MS был в порядке до 2005 года, но они сломали его с 2008 года, так как результаты непредсказуемы; поэтому краткий ответ может быть реализован как CHAR (1).

Конечно, предполагается, что вы не делаете глупостей, например, упаковываете 8 отдельных столбцов в один TINYINT. Это не только серьезная ошибка нормализации, но и кошмар для программистов. Держите каждый столбец дискретным и с правильным типом данных.

C. Несколько столбцов индикатора и Nullable

Это не имеет ничего общего с (A) и (B) и не зависит от них. То, что столбцы правильные Тип данных, зависит от того, сколько у вас есть и является ли он Nullable. Nullable означает (обычно) столбец не является обязательным. По сути, вы не выполнили упражнение по моделированию или нормализации. Функциональные зависимости неоднозначны. если вы выполните упражнение нормализации, не будет столбцов Nullable, необязательных столбцов; либо они явно существуют для определенного отношения, либо их не существует. Это означает использование обычной реляционной структуры супертипов-подтипов.

Конечно, это означает, что больше таблиц, но нет нулей. СУБД Enterpise не имеет проблем с большим количеством таблиц или объединений, для чего они оптимизированы. Нормализованные базы данных работают намного лучше, чем ненормализованные или денормализованные, и их можно расширять без «повторного факторинга». Вы можете упростить использование, предоставив представление для каждого подтипа.

Если вам нужна дополнительная информация по этому вопросу, посмотрите на этот вопрос / ответ . Если вам нужна помощь с моделированием, пожалуйста, задайте новый вопрос. На вашем уровне вопросов, я бы посоветовал вам придерживаться 5NF.

D. Производительность Nulls

Отдельно, если производительность важна для вас, исключите Nulls. Каждый столбец Nullable хранится как переменная длина; это требует дополнительной обработки для каждой строки / столбца. Корпоративные базы данных используют «отложенную» обработку для таких строк, чтобы позволить журналированию и т. Д. Перемещать очереди без ущерба для фиксированных строк. В частности, никогда не используйте столбцы переменной длины (включая столбцы Nullable) в индексе: для этого требуется распаковка при при каждом доступе .

E. Опрос

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

1 голос
/ 27 декабря 2010

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

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

Если у вас есть 200 необязательных флагов и вы запрашиваете максимум 1-2 за раз для большого количества строк, вы получите лучшую производительность, если у каждого флага будет свойТаблица.Если данные действительно редки, это становится еще лучше.

Если у вас есть 200 обязательных флагов и вы выполняете только одну выборку записей, вы должны поместить их в одну таблицу.

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

Или вы можете проявить творческий подход и использовать концепцию "мусорного измерения", в которой вы создаете отдельную таблицу со всеми 200 логическими флагами, представленными какколонны.Создайте одну строку для каждой отдельной комбинации значений флага.Каждая строка получает первичный ключ автоинкремента, на который вы ссылаетесь в основной записи.Вуаля, главная таблица теперь содержит 1 int вместо 200 столбцов.Небеса хакеров, кошмар DBA.

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

0 голосов
/ 27 декабря 2010

Любой из вышеперечисленных подойдет, и я лично предпочитаю использовать BOOL, если он поддерживается должным образом, потому что это наилучшим образом передает ваши намерения, но я бы не стал использовать ENUM(0,1).

Первая проблема с ENUM заключается в том, что его значение должно быть строкой. 0 и 1 выглядит как число, поэтому программисты склонны посылать ему числа.

Вторая проблема с ENUM заключается в том, что если вы отправите ему неправильное значение, по умолчанию будет указано первое перечисление, а в некоторых базах данных это даже не будет указывать на ошибку (я смотрю на вас MySQL). Это усугубляет первую проблему, поскольку, если вы случайно отправите ее 1 вместо "1", она сохранит значение "0" - очень нелогично!

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

...