Когда вы начинаете переходить к «определенным пользователем полям», что часто встречается в средствах отслеживания ошибок, управления ресурсами клиентов и аналогичных бизнес-инструментах, это то, что они не подкреплены таблицей с баджиллионными полями (если они есть, то это, вероятно, проблема своя).
Вместо этого вы найдете дизайн таблицы значений атрибутов сущности и связанный инструмент администрирования для управления действительными атрибутами.
Рассмотрим следующую таблицу:
+ -------------- +
| вещь |
| -------------- |
| id |
| тип |
| desc |
| attr1 |
| attr2 |
| attr3 |
| attr4 |
| attr5 |
+ -------------- +
Это после того, как вы добавили несколько атрибутов. Вместо того, чтобы attr1
притворяться, что он читает artist
или tracks
или genre
или какие-либо атрибуты, которые имеет вещь. И вместо 5, что, если это было 50. Очевидно, что это неуправляемо. Это также требует обновления модели и повторного развертывания приложения для обработки нового поля. Не идеально.
Теперь рассмотрим следующую структуру таблицы:
+ -------------- + + --------------- + + ------------- +
| вещь | | thing_attr | | attr |
| -------------- | | --------------- | | ------------- |
| id | <--- + | thing_id (FK) | +> | id |
| тип | | attr_id (fk) | + - + | имя |
| desc | | значение | | |
+ -------------- + + --------------- + + ------------- +
Вы получили свою вещь с ее основными полями. У вас есть еще две таблицы. Один с атрибутами. Каждое поле представляет собой строку в attr
таблице. И затем есть thing_attr
пара внешних ключей, относящихся обратно к thing
столу и attr
столу. И тогда у него есть поле значения, в котором вы сохраняете любое значение поля для этой сущности.
И теперь у вас есть структура, в которой таблица attr может обновляться во время выполнения, и новые поля могут быть добавлены (или удалены) на лету без существенного влияния на все приложение.
Запросы немного сложнее, и валидация тоже становится более сложной (либо хриплые хранимые процедуры, либо вся клиентская часть). Это компромисс в дизайне.
Рассмотрим также ситуацию, когда однажды вам нужно будет выполнить миграцию, и вы вернетесь в приложение и обнаружите, что теперь имеется примерно полдюжины или более атрибутов, чем изначально распределенная схема. Это делает для уродливых миграций и обновлений, где таблица значений атрибутов сущности, при правильном использовании, может быть чище. (Не всегда, но может быть.)
Есть ли недостатки в том, чтобы просто изменить схему во время выполнения? Если пользователь считает, что вещь нуждается в новом атрибуте, просто динамически добавить столбец в таблицу?
Если вы работаете с соответствующим вариантом базы данных nosql, вы, вероятно, могли бы сделать это (обратите внимание, что соответствующим вариантом nosql для этого, вероятно, будет хранилище значений ключа, которое, ну, в общем, таблица EAV для реляционных таблиц, описанных выше) без особых проблем. Однако он поставляется со всеми компромиссами для nosql, которые подробно описаны в другом месте.
Если вы вместо этого работаете с реляционной базой данных - вам нужна схема. Динамическое добавление столбца означает, что некоторые подмножества из следующих вещей верны:
- Вы занимаетесь программированием базы метаданных. Вместо того, чтобы просто сопоставить этот столбец с этим полем с помощью хорошего ORM, вы, вероятно, делаете что-то вроде,
select *
а затем делаете некоторый сложный код, чтобы выяснить, каковы данные на самом деле (см . ResultSetMetaData в Java ), и затем сохраните их в карте или какой-либо другой тип данных - но не хорошие поля в коде). Это затем отбрасывает значительную часть безопасности типов и опечаток, которые вы имеете с традиционным подходом.
- Вы, вероятно, отказались от ORM. Это означает, что вы пишете raw sql для всего кода, а не позволяете системе делать всю работу за вас.
- Вы перестали делать чистые обновления. Что происходит, когда клиент добавляет поле с одним именем, которое также использует ваша следующая версия? На сайте создания матчей обновление, которое хочет добавить
hasdate
поле для хранения метки времени, уже определено как hasdate
логическое значение для успешного совпадения ... и ваше обновление прерывается.
- Вы верите, что клиент не нарушает систему, используя какое-то зарезервированное слово, которое тоже нарушает ваши запросы ... где-то.
- Вы привязали себя к одному бренду базы данных. DDL различных баз данных отличаются. Типы баз данных являются самым простым примером этого.
varchar2
против text
и тому подобное. Ваш код для добавления столбца будет работать на MySQL, но не на Postgres, Oracle или SQL Server.
- Вы доверяете клиенту на самом деле, чтобы добавить данные хорошо ? Конечно, EAV далек от идеала, но теперь у вас есть несколько ужасных неясных имен таблиц, которые вы, разработчик, не добавили, с неверным типом индекса (если есть), без каких-либо ограничений, добавленных в код, где необходимо быть и так далее.
- Вы предоставили права на изменение схемы пользователю, запустившему приложение. Маленькие Бобби Брошенные Таблицы не возможны, когда вы ограничены SQL, а не DDL (конечно, вы можете сделать
delete * from students
вместо этого, но вы не можете действительно испортить базу данных плохими способами). Количество вещей, которые могут пойти не так с доступом к схеме из-за несчастного случая или стремительного роста злонамеренных действий.
Это действительно сводится к «не делай этого». Если вы действительно этого хотите, воспользуйтесь известным шаблоном структуры таблицы EAV или базой данных, полностью посвященной этой структуре. Не позволяйте людям создавать произвольные поля в таблице. Головные боли просто не стоят этого.