Я намеренно оставляю это довольно туманным вначале. Я ищу обсуждение и то, какие вопросы важны, больше, чем я ищу жесткие ответы.
Я нахожусь в середине разработки приложения, которое делает что-то вроде управления портфелем. На данный момент у меня есть следующий дизайн
Мой вопрос касается временной природы этих вещей. Проблемы возникают, затем исчезают. Решения имеют ожидаемую дату разрешения, но она может быть изменена по мере их разработки. Степень взаимосвязи может меняться со временем по мере развития проблем и решений.
Итак, вопрос: каков наилучший дизайн для версионирования этих вещей, чтобы я мог получить как текущую, так и историческую перспективу моего портфеля?
Позднее: возможно, мне стоит сделать этот вопрос более конкретным, хотя ответ @Eric Beard'а заслуживает внимания.
Я'рассмотрел три варианта дизайна базы данных. Мне достаточно каждого из них, чтобы показать их недостатки. Мой вопрос: какой выбрать, или вы можете придумать что-то лучшее?
table problems
int id | string name | text description | datetime created_at | int previous_version_id
foreign key previous_version_id -> problems.id
Это проблематично, потому что каждый раз, когда я хочу получить новую версию, мне приходится дублировать всю строку, включая длинный столбец description
.
table problems
int id | string name | text description | datetime created_at
Это просто перемещает отношения из таблиц Problems и Solutions в таблицу Relationships. Та же проблема дублирования, но, возможно, немного "чище", поскольку у меня уже есть абстрактная концепция отношений.
table problems
int id
table attributes
int id | int thing_id | string thing_type | string name | string value | datetime created_at | int previous_version_id
foreign key (thing_id, thing_type) -> problems.id or solutions.id
foreign key previous_version_id -> attributes.id
Это означает, что для загрузки текущей версии проблемы или решения я должен получить все версии атрибута, отсортировать их по дате и затем использовать самую актуальную. Возможно, это не так уж и плохо. Что мне кажется действительно ужасным, так это то, что я не могу проверить тип этих атрибутов в базе данных. Этот столбец value
должен быть свободным текстом. Я могу сделать колонку name
ссылкой на отдельную таблицу attribute_names
, которая имеет колонку type
, но это не принуждает к правильному типу в таблице attributes
.
позже: ответ на комментарии @Eric Beard'а о многотабличных внешних ключах:
Увы, то, что я описал, является упрощением: есть только два типа вещей (проблемы и решения). На самом деле у меня есть около 9 или 10 различных типов Вещей, поэтому при вашей стратегии у меня было бы 9 или 10 столбцов внешних ключей. Я хотел использовать однотабличное наследование, но у Вещей так мало общего, что объединять их в одну таблицу было бы очень расточительно.
Хм, похоже на этот сайт...
Что касается дизайна базы данных, то система версий, подобная SVN, где вы никогда не делаете никаких обновлений, а только вставляете (с номером версии), когда что-то меняется, может быть тем, что вам нужно. Это называется MVCC, Multi-Value Concurrency Control. Вики - еще один хороший пример этого.
@Gaius
foreign key (thing_id, thing_type) -> problems.id or solutions.id
Будьте осторожны с такими "разнонаправленными" внешними ключами. Мой опыт показывает, что производительность запроса резко падает, когда условие присоединения должно проверить тип, прежде чем выяснить, к какой таблице присоединиться. Это кажется не таким элегантным, но nullable
problem_id and solution_id
будет работать намного лучше.
Конечно, производительность запросов также пострадает при использовании MVCC, когда вам придется добавлять проверку для получения последней версии записи. Компромисс заключается в том, что вам никогда не придется беспокоиться о конфликте при обновлении.
Как делают Вы думаете об этом:
проблемы стола
международный id | имя строки | текстовое описание | дата и время created_at
стол problems_revisions
международный пересмотр | международный id | имя строки | текстовое описание | дата и время created_at
id внешнего ключа-> problems.id
Перед обновлениями Вы должны выполнить дополнительную вставку в столе пересмотра. Эта дополнительная вставка быстра, однако, это - то, за что Вы должны заплатить
I suppose there's
Перенести общие атрибуты Thing в единую таблицу наследования, затем добавить таблицу custom_attributes
. Это упрощает работу с внешними ключами, уменьшает дублирование и обеспечивает гибкость. Это не решает проблемы безопасности типов для дополнительных атрибутов. Это также добавляет некоторую сложность, поскольку у вещи теперь есть два способа иметь атрибут.
Если description
и другие большие поля останутся в таблице Things, это также не решит проблему дублирования-пространства.
table things
int id | int type | string name | text description | datetime created_at | other common fields...
foreign key type -> thing_types.id
table custom_attributes
int id | int thing_id | string name | string value
foreign key thing_id -> things.id
It' s хорошая идея выбрать структуру данных, которая делает общие вопросы, которые Вы задаете модели, легкой ответить. It' s, скорее всего, это you' ре, заинтересованное текущим положением большую часть времени. При случае Вы захотите сверлить в историю для конкретных проблем и решений.
У меня были бы столы для проблемы, решения и отношений, которые представляют текущее положение. Также был бы 'problem_history', 'solution_history', и т.д. стол. Они были бы детскими столами проблемы, но также и содержали бы дополнительные колонки для 'VersionNumber' и 'EffectiveDate'. Ключ был бы ('ProblemId', 'VersionNumber').
Когда Вы обновляете проблему, Вы написали бы старые ценности в 'problem_history' стол. Пункт в вопросах времени поэтому возможен, поскольку Вы можете выбрать отчет 'problem_history', который действителен как - в конкретную дату.
Где I' ve, сделанный это прежде, я также создал представление о СОЮЗЕ 'проблема' и 'problem_history', поскольку это иногда полезно в различных вопросах.
Выбор 1 мешает подвергать сомнению текущую ситуацию, поскольку все Ваши исторические данные смешаны в с Вашими текущими данными.
Выбор 3 будет плохим для работы вопроса и противным, чтобы закодировать против как you' ll получить доступ к большому количеству рядов для того, что должно просто быть простым вопросом.