如何设计一个数据库来表示结构变化的数据?

How to design a database to represent data that changes in structure?

正在设计一个数据库来存储和跟踪 Web 表单内容的历史记录。通常这不是问题。天真的实现是 history table 记录表单字段内容以及时间戳。

问题是:此表单可能会随着时间的推移而改变。字段可能会被重命名、添加或删除。

如何建模并确保整个历史记录的数据完整性。

我现在的想法是 history table 可以只用两个字段来完成:timestampdata。并且,在这种情况下,data 将是一个 JSON 字符串,对应于拍摄快照时的表单字段及其数据。这意味着该软件可以随时显示表单的回滚版本,无论结构如何更改。

在数据库中表示它的其他方法可能是什么?

使用 Python/Django 和 MySQL,这可能不相关。

编辑 1:

澄清。想象一下,想要在您无法控制的网站上记录表单的历史记录。它是关于创建一个数据库来存储和记录该页面的历史记录。想想 Git 一个页面,其表单和数据的结构和内容每一两年都会发生变化。

编辑 2:

一个选择是创建一个复杂的 table 结构,其中可以使用 table 来描述表单,该 table 存储在任何给定时间可用的各种类型的表单字段,然后是 form_contentsform_history table 最终会将它们粘合在一起形成一个结构,该结构可以记录随着时间​​的推移具有不同结构的表单的历史。我可以看到这可能会变得非常复杂。

如果我理解正确的话,我可能会这样做:

CREATE TABLE IF NOT EXISTS `form_history` (
    `id`                    int unsigned    NOT NULL AUTO_INCREMENT,
    `when`                  datetime        NOT NULL,
    `field_accept`          varchar(255)    DEFAULT NULL,
    `field_accesskey`       varchar(255)    DEFAULT NULL,
    `field_alt`             varchar(255)    DEFAULT NULL,
    `field_autocomplete`    varchar(255)    DEFAULT NULL,
    `field_autofocus`       varchar(255)    DEFAULT NULL,
    `field_checked`         varchar(255)    DEFAULT NULL,
    `field_class`           varchar(255)    DEFAULT NULL,
    `field_contenteditable` varchar(255)    DEFAULT NULL,
    `field_contextmenu`     varchar(255)    DEFAULT NULL,
    `field_data`            text            DEFAULT NULL,
    `field_dir`             varchar(255)    DEFAULT NULL,
    `field_disabled`        varchar(255)    DEFAULT NULL,
    `field_draggable`       varchar(255)    DEFAULT NULL,
    `field_dropzone`        varchar(255)    DEFAULT NULL,
    `field_form`            varchar(255)    DEFAULT NULL,
    `field_formaction`      varchar(255)    DEFAULT NULL,
    `field_formtarget`      varchar(255)    DEFAULT NULL,
    `field_height`          int unsigned    DEFAULT NULL,
    `field_hidden`          varchar(255)    DEFAULT NULL,
    `field_id`              varchar(255)    DEFAULT NULL,
    `field_lang`            varchar(255)    DEFAULT NULL,
    `field_list`            varchar(255)    DEFAULT NULL,
    `field_max`             varchar(255)    DEFAULT NULL,
    `field_maxlength`       int unsigned    DEFAULT NULL,
    `field_min`             varchar(255)    DEFAULT NULL,
    `field_multiple`        varchar(255)    DEFAULT NULL,
    `field_name`            varchar(255)    DEFAULT NULL,
    `field_pattern`         varchar(255)    DEFAULT NULL,
    `field_placeholder`     varchar(255)    DEFAULT NULL,
    `field_readonly`        varchar(255)    DEFAULT NULL,
    `field_required`        varchar(255)    DEFAULT NULL,
    `field_size`            int unsigned    DEFAULT NULL,
    `field_spellcheck`      varchar(255)    DEFAULT NULL,
    `field_src`             varchar(255)    DEFAULT NULL,
    `field_step`            int unsigned    DEFAULT NULL,
    `field_style`           varchar(255)    DEFAULT NULL,
    `field_tabindex`        int unsigned    DEFAULT NULL,
    `field_title`           varchar(255)    DEFAULT NULL,
    `field_translate`       varchar(255)    DEFAULT NULL,
    `field_type`            varchar(255)    DEFAULT NULL,
    `field_value`           varchar(255)    DEFAULT NULL,
    `field_width`           int unsigned    DEFAULT NULL,
    PRIMARY KEY (`id`), KEY (`when`)
) ENGINE=InnoDB COMMENT='Field definitions';

如果这对您很重要,您也可以为事件属性添加列。

下面是一些示例数据:

|----|---------------------|-----|-----------------|-----|------------|-----|
| id | when                | ... | field_maxlength | ... | field_name | ... |
|----|---------------------|-----|-----------------|-----|------------|-----|
|  1 | 2015-06-01 00:00:01 | ... |              10 | ... | username   | ... |
|  2 | 2015-06-01 00:00:01 | ... |              10 | ... | password   | ... |
| .. | ................... | ... | ............... | ... | .......... | ... |
| 17 | 2015-06-08 00:00:01 | ... |              32 | ... | username   | ... |
| 18 | 2015-06-08 00:00:01 | ... |              32 | ... | password   | ... |
| 19 | 2015-06-08 00:00:01 | ... |              25 | ... | fname      | ... |
| 20 | 2015-06-08 00:00:01 | ... |              25 | ... | lname      | ... |
| .. | ................... | ... | ............... | ... | .......... | ... |
|----|---------------------|-----|-----------------|-----|------------|-----|

这个非常简单的示例数据仅显示表单上的两个字段(usernamepassword)。在 1 日,他们的 maxlength 都是 10,但在 8 日,他们的 maxlength 值增加到 32,并且在表单中添加了两个新字段:fnamelname.