Является ли расточительным создание новой таблицы базы данных вместо использования типа данных enum?

38

Предположим, у меня есть 4 вида услуг, которые я предлагаю (они вряд ли будут часто меняться):

  • тестирование
  • дизайн
  • программирование
  • Другие

Предположим, у меня есть 60-80 реальных услуг, каждая из которых подпадает под одну из вышеуказанных категорий. Например, «сервис» может быть «Тестовая программа с использованием техники A» и имеет тип «Тестирование».

Я хочу закодировать их в базу данных. Я придумал несколько вариантов:

Вариант 0:

Использовать VARCHARнапрямую для кодирования типа сервиса напрямую в виде строки

Опция 1:

Используйте базу данных enum. Но enum это зло

Вариант 2:

использовать две таблицы:

service_line_item (id, service_type_id INT, description VARCHAR);
service_type (id, service_type VARCHAR);

Я даже могу наслаждаться ссылочной целостностью:

ALTER service_line_item 
    ADD FOREIGN KEY (service_type_id) REFERENCES service_type (id);

Звучит хорошо, да?

Но я все еще должен кодировать вещи и иметь дело с целыми числами, т.е. при заполнении таблицы. Или я должен создавать сложные программирования или конструкции БД при заполнении или работе с таблицей. А именно, ПРИСОЕДИНЯЕТСЯ, когда имеешь дело с базой данных напрямую, или создаешь новые объектно-ориентированные сущности на стороне программирования, и проверяю, правильно ли я ими управляю.

Вариант 3:

Не используйте enum, не используйте две таблицы, а просто используйте целочисленный столбец

service_line_item (
    id,
    service_type INT,        -- use 0, 1, 2, 3 (for service types)
    description VARCHAR
);

Это похоже на «фальшивое перечисление», которое требует больше накладных расходов на стороне кода, например, зная это {2 == 'Programming'}и работая с ним соответствующим образом.

Вопрос:

В настоящее время я реализовал его, используя вариант 2 , руководствуясь концепциями

  1. не используйте enum (вариант 1)
  2. избегать использования базы данных в качестве электронной таблицы (опция 0)

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

На «менее расточительный путь» я смотрю Option 3. ИТ легче и требует для работы практически одинаковых конструкций кода (с небольшими изменениями, но сложность и структура в основном те же, но с одной таблицей)

Я полагаю, что в идеале это не всегда расточительно, и для каждого варианта есть хорошие случаи, но есть ли хорошее руководство относительно того, когда следует использовать вариант 2, а когда - вариант 3?

Когда есть только два типа (двоичный)

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

Я решил не создавать новую таблицу только для хранения значений {"Стандарт", "Исключение"}. Таким образом, мой столбец просто содержит {0, 1}, и имя моего столбца называется exception, и мой код выполняет перевод из {0, 1} => {STANDARD, EXCEPTION}(который я закодировал как константы в языке программирования)

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

ORM

Чтобы добавить контекст, после прочтения ответов - я только что начал (недавно) снова использовать ORM, в моем случае Doctrine 2. После определения схемы БД с помощью аннотаций я хотел заполнить базу данных. Поскольку весь мой набор данных относительно мал, я хотел попробовать использовать программные конструкции, чтобы увидеть, как это работает.

Сначала я заполнил service_types, а затем service_line_items, так как существовал список из реальной таблицы. Таким образом, такие вещи, как «стандарт / исключение» и «тестирование» - все это строки в электронной таблице, и они должны быть закодированы в надлежащие типы перед сохранением их в БД.

Я нашел этот SO ответ: Что вы используете вместо ENUM в Doctrine2? , в котором предлагалось не использовать конструкцию enum DB, а использовать INTполе и кодировать типы, используя конструкцию const языка программирования.

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

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

Так что, если, скажем $str = 'Testing';, мне еще нужно где-то блок, который делает что-то вроде:

switch($str):
{ 
    case 'Testing':  $type = MyEntity::TESTING; break;
    case 'Other':    $type = MyEntity::OTHER; break;
}

Хорошо то, что вы не имеете дело с целыми числами / магическими числами [вместо того, чтобы иметь дело с закодированными постоянными величинами], но плохо то, что вы не можете автоматически извлекать вещи из базы данных без этого шага преобразования, к моему знание.

И именно это я имел в виду, говоря, что-то вроде «все еще нужно кодировать вещи и иметь дело с целыми числами». (Конечно, теперь, после комментария Окрамия, мне не нужно будет иметь дело непосредственно с целыми числами, но я буду иметь дело с именованными константами и некоторым преобразованием в / из констант по мере необходимости).

Деннис
источник
9
Что бы вы ни делали, не делайте # 3. Поддерживающий его психопат должен постоянно выяснять, что означают эти магические числа. Если вы сделаете это, вам лучше надеяться, что они не знают, где вы живете. blog.codinghorror.com/coding-for-violent-psychopaths
RubberDuck
7
Мне нравится Вариант 2. Если вам не нравится распространение таблиц поиска, используйте одну таблицу и добавьте столбец «тип поиска». Но да, создание таблицы поиска - это «стандартный» способ сделать это, так как он позволяет вам делать забавные вещи, например, легко заполнять выпадающий список в пользовательском интерфейсе.
Роберт Харви,
Не используйте «РЕДАКТИРОВАТЬ» в своих сообщениях здесь; мы не форум. Каждый пост Stack Exchange уже содержит подробную историю изменений, которую может просмотреть каждый.
Роберт Харви,
если я не могу использовать РЕДАКТИРОВАТЬ, что я буду использовать?
Деннис
Просто отредактируйте пост и сделайте его естественным, как я уже сделал. Смотрите историю изменений, чтобы просмотреть изменения.
Роберт Харви,

Ответы:

35

Вариант № 2, использующий справочные таблицы, является стандартным способом сделать это. Он был использован миллионами программистов и, как известно, работает. Это шаблон , поэтому любой, кто будет смотреть на ваши вещи, сразу же узнает, что происходит. Существуют библиотеки и инструменты, которые работают с базами данных, избавляя вас от большого и большого количества работы, которая справится с этим правильно. Преимущества его использования неисчислимы.

Это расточительно? Да, но только немного. Любая полуприличная база данных всегда будет хранить такие часто соединяемые маленькие таблицы в кеше, так что отходы обычно незаметны.

Все другие варианты, которые вы описали, являются специальными и хакерскими, включая MySQL enum, потому что они не являются частью стандарта SQL. (Помимо этого, то, что отстой, enum- это реализация MySQL, а не сама идея. Я не возражаю, если однажды я увижу это как часть стандарта.)

Ваш последний вариант # 3 с использованием простого целого числа особенно хакерский. Вы получаете худшее из всех миров: нет ссылочной целостности, нет именованных значений, нет точных знаний в базе данных о значении, просто произвольные целые числа, выброшенные повсеместно. Таким образом, вы можете также отказаться от использования констант в вашем коде и начать использовать жестко закодированные значения. circumference = radius * 6.28318530718;, Как насчет этого?

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

Ваше предложение о необходимости «кодировать вещи и иметь дело с целыми числами», или «создавать сложные программные конструкции», или «создавать новые объектно-ориентированные объекты на стороне программирования», говорит мне, что, возможно, вы пытаетесь сделать объектно-реляционный сопоставление (ORM) на лету рассредоточено по всему коду вашего приложения, или, в лучшем случае, вы можете попытаться развернуть свой собственный механизм объектно-реляционного сопоставления вместо использования существующего инструмента ORM для работы, такого как Hibernate. Все эти вещи очень легки с Hibernate. Чтобы выучить его, требуется некоторое время, но как только вы его выучите, вы действительно можете сосредоточиться на разработке своего приложения и забыть о мельчайших механизмах представления содержимого в базе данных.

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

  1. Создайте представления, которые объединяют ваши основные таблицы с любыми ссылочными таблицами, на которые они ссылаются, чтобы каждая строка содержала не только ссылочные идентификаторы, но и соответствующие имена.

  2. Вместо использования целочисленного идентификатора для справочной таблицы используйте столбец CHAR (4) с четырехбуквенными сокращениями. Таким образом, идентификаторы ваших категорий станут «ТЕСТ», «DSGN», «PROG», «OTHR». (Их описания останутся правильными английскими словами, конечно.) Это будет немного медленнее, но поверьте мне, никто не заметит.

Наконец, когда есть только два типа, большинство людей просто используют логический столбец. Таким образом, этот столбец «стандарт / исключение» будет реализован как логическое значение и будет называться «IsException».

Майк Накис
источник
3
Кроме того, у Postgres также есть типы enum . Они просты и ничего особенного, что позволяет вам использовать читаемую строку в качестве значения, но имеет более эффективное целочисленное значение, которое используется внутри.
Кат
Как насчет случая, когда данные последовательно повторяются, но не являются избыточными (например, не приведет к аномалиям обновления / вставки / удаления)? Например, пол человека (маловероятно, чтобы вводить новые типы данных, никогда не нужно будет менять имя пола и т. Д.)
Адам Томпсон,
Это потому, что в конечном итоге вы обнаружите, что вам нужна «среда принятия», а ваши неизменяемые перечисления должны быть изменены.
Питер Б
3

Вариант 2 с константами или перечислениями в конце программирования.
Хотя оно дублирует знания, нарушая принцип «Единого источника истины», вы можете справиться с ним, используя технику быстрого отказа. Когда ваша система загружается, она проверит, что значения enum или const существуют в базе данных. Если нет, система должна выдать ошибку и отказаться от загрузки. Как правило, в настоящее время исправить эту ошибку дешевле, чем позже, когда может произойти что-то более серьезное.

Хосе Маргаса Лопес
источник
0

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

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

table service_type 
( id VARCHAR 
, name VARCHAR 
  primary key ( id ) 
);
table service_line_item 
( id 
, service_type VARCHAR 
, description VARCHAR
  foreign key ( service_type ) references service_type ( id )
);

select * from service_type ; 

+-------------+----------------+
| id          | name           |
+-------------+----------------+
| Testing     | Testen         |
| Design      | Design         | 
| Programming | Programmierung |
| Other       | Andere         |
+-------------+----------------+

или, для ваших французских клиентов ...

update services_types set name = 'Essai'         where id = 'Testing'; 
update services_types set name = 'Conception'    where id = 'Design'; 
update services_types set name = 'Programmation' where id = 'Programming'; 
update services_types set name = 'Autre'         where id = 'Other'; 
Фил В.
источник