TL; DR:这个设计是否正确,我应该如何查询?
我们假设我们有 city 和地址的历史记录表,其设计如下:
CREATE TABLE city_history (
id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
name VARCHAR(128) NOT NULL,
history_at DATETIME NOT NULL,
obj_id INT UNSIGNED NOT NULL
);
CREATE TABLE address_history (
id BIGINT UNSIGNED NOT NULL PRIMARY KEY,
city_id INT NULL,
building_no VARCHAR(10) NULL,
history_at DATETIME NOT NULL,
obj_id INT UNSIGNED NOT NULL
);
原始表几乎相同,除了 history_id 和 obj_id ( city:id,name; address:id,city_id,building_no )。 city 和地址( city_id )之间也存在外键关系。
历史记录表会在原始条目(创建,更新,删除)的每次更改时填充,并在给定时间显示条目的确切状态。
obj_id 保存原始对象的id - 没有外键,因为原始条目可以删除,历史记录条目也不能。 history_at 是创建历史记录条目的时间。
为每个表单独创建历史记录条目 - 更改城市名称会创建 city_history 条目,但不会创建 address_history 条目。
因此,要查看任何 T1 时间点的城市整个地址的状态(例如打印文档),我们会从两个历史记录表中获取给定的最新条目 obj_id 在 T1 之前创建,对吗? 理论上我们应该能够在任何给定的时间点使用 city 来查看signle 地址的状态。任何人都可以帮助我为给定的地址ID和时间创建这样的查询吗?请注意,可能有多个记录具有相同的确切时间戳。
还需要创建一个报告,用于显示给定时间段内给定地址的每个状态变化,其中包含" city_name,building_no,changed_at"等条目。是否可以使用SQL查询创建?性能在这里并不重要,这些报告不会经常产生。
在用户可以过滤结果的交互式版本中可能需要上述报告,例如按城市名称或建筑物编号。是否仍然可以在SQL中执行?
实际上地址表和 address_history 表中还有4个外键应该在报告中加入(街道,邮政编码等)。查询不会长达十页,以提供所有必需的功能吗?
我试图建立一些查询,与每组最大的玩家一起玩,但我不认为我可以随处获得。这个设计对我的用例来说真的很好吗(如果是这样,请你提供一些问题让我一起玩,以便得到我想要的地方?)?或者我应该重新考虑整个设计?
任何帮助表示感谢。
答案 0 :(得分:0)
这是一个非常“传统”的问题,当涉及对某一行的更改进行版本控制(或监控)时。
有各种“解决方案”,每种解决方案都有其自身的缺点和优势。
以下“陈述”是我的经验的结果,它们既不完美,也不声称它们是“唯一的”!
1。)创建一个“历史表”:这是最糟糕的想法。您始终需要考虑需要查询的表,具体取决于应查询的DATA。这是一个“鸡蛋”问题......
2。)使用 ONE 表与 ONE (增加)“修订版”编号:这是一种更好的方法,但查询“难以”:确定无论使用哪种方法,“最近一行”每“id”都是非常昂贵的。
我的个人表现是,遵循“双链表”的模式最好解决这个问题,当涉及数百万条记录时:
3。)在每个实体中保留两列,比如prev_version_id
和next_version_id
。如果没有以前的版本,则prev_version_id
指向NULL
。如果没有更高版本,则next_version_id
指向NULL
。
这种方法要求您在更新时始终执行两项操作:
next_version_id
)更新为刚刚执行的行。 但是,当您的数据库增长到100万行时,您会非常高兴您选择了这条路径:
where ISNULL(prev_version_id) and entity_id = 5
where ISNULL(next_version_id) and entity_id = 5
entity_id=5
,可以按prev_version_id
或next_version_id
进行排序。经常被忽视的事实:前两个查询也可用于获取实体的ALL first versions
或ALL recent versions
列表 - 大约在任何时间! (不要低估它可以确定实体的最新版本的“代价高昂”!相信我,当“测试”一切看起来都很好,但真正的斗争开始于使用数百万条记录的实时数据时。)
欢呼声, dognose
答案 1 :(得分:0)
(我的答案是从here复制的,因为该问题从未将答案标记为已接受。)
我的正常模式"在(非常)伪代码中:
触发A:
在这种情况下,您使用" x> = from和x<来查询历史记录到" (不 BETWEEN作为之前的记录' s"来自"值应该匹配下一个""值")。
此外,这种模式还可以改变日志"报告更容易。
注意:此模式可以在没有触发器的情况下实现。使用存储过程,甚至代码中的多个查询,实际上可以否定对非历史表的需求。