insert into FOO_HIST select * from FOO where id = @id
。我的同事认为这是糟糕的设计,因为我不应该为历史原因将一个表完全复制一遍,而应该在当前表中插入另一条记录,并标记其用于历史目的。
是否有处理历史数据存储的标准方法?对我来说,我认为不应该在同一个表中混杂我的历史记录和活动记录,因为它可能会超过一百万条记录(我考虑长期)。
你或你的公司如何处理这个问题?
我正在使用MS SQL Server 2008,但我想保持答案通用和任意DBMS。
insert into FOO_HIST select * from FOO where id = @id
。在操作系统中直接支持历史数据会使应用程序比预期的更加复杂。通常情况下,除非您需要在系统内操作记录的历史版本,否则我不建议这样做。
如果您仔细看一下,大多数历史数据的需求可以归为以下两类:
审计日志:最好使用审计表来完成。通过读取系统数据字典中的元数据,编写一个生成脚本以创建审计日志表和触发器的工具相当容易。这种工具可以用于将审计日志添加到大多数系统中。如果您想要实现数据仓库(见下文),还可以使用此子系统进行更改数据抓取。
历史报告:报告历史状态、“按照”位置或随时间变化的分析报告。对于简单的历史报告需求,可能可以通过查询上述审计日志表来满足。如果您有更复杂的需求,则实施数据集市以供报告可能比尝试将历史直接集成到操作系统中更经济实惠。
慢速变化的维度是追踪和查询历史状态的最简单机制,并且许多历史跟踪可以自动化。编写通用处理程序并不那么困难。通常,历史报告不必使用最新数据,因此批量刷新机制通常是可以的。这使您的核心和报告系统架构相对简单。
如果您的需求属于以上两类中的任一类,则最好不要在操作系统中存储历史数据。将历史功能分离到另一个子系统中可能会更加轻松,并产生更适合其预期目的的事务性和审计/报告数据库。
我认为没有一个特定的标准方法,但是我想提供一种可能的方法。我在Oracle工作,我们的内部Web应用程序框架利用XML存储应用程序数据。
我们使用称为Master-Detail模型的东西,最简单的情况包括:
主表,例如称为Widgets
,通常只包含一个ID。通常包含不随时间变化/不是历史性的数据。
详细/历史表,例如称为Widget_Details
,至少包含以下内容:
因此,实体从主表和详细表各自开始有1行。详细信息具有NULL的结束日期和“C”状态控制。 当发生更新时,当前行被更新为当前时间的END_DATETIME,并将status_control设置为NULL(或'A'如果更喜欢)。在详细表中创建一个新行,仍与同一主关联,其status_control为'C',进行更新的人员的ID和存储在XMLDATA列中的新数据。
这是我们历史模型的基础。Create/Update逻辑由Oracle PL/SQL包处理,因此您只需传递当前ID,您的用户ID和新的XML数据给函数,它内部执行所有更新/插入操作以在历史模型中表示该数据。开始和结束时间指示表中该行活动的时间。
存储空间价格便宜,我们通常不会删除数据而是保留审计轨迹。这样可以让我们在任何时候查看数据的样貌。通过索引 status_control = 'C' 或使用视图,杂乱无序并不是一个问题。显然,您的查询需要考虑到始终使用记录的当前版本(NULL end_datetime 和 status_control = 'C')。
我认为你的方法是正确的。历史表应该是主表的一份无索引副本,还要确保表中有更新时间戳。
如果你很快尝试另一种方法,你将会面临以下问题:
我想添加一个选项,因为我使用Azure SQL,多表格的操作对我来说太过繁琐。我在我的表上添加了一个插入/更新/删除触发器,然后使用“FOR JSON AUTO”功能将更改前/后转换为JSON格式。
SET @beforeJson = (SELECT * FROM DELETED FOR JSON AUTO)
SET @afterJson = (SELECT * FROM INSERTED FOR JSON AUTO)
这将返回变更前/后记录的JSON表示。然后,我将这些值与更改发生时间戳(我还存储有关当前记录的ID)一起存储在历史表中。使用序列化过程,我可以控制在模式更改时如何回填数据。
我从这个链接此处了解到这个方法。
您可以使用MSSQL Server审计功能。从SQL Server 2012版本开始,您将在所有版本中找到此功能:
我知道这是一篇旧帖子,但我想补充几点。
对于这样的问题,标准就是找到最适合情况的方法。了解存储的需求以及历史/审计/更改跟踪数据的潜在用途非常重要。
审计(安全目的):为所有可审计的表使用一个公共表。定义结构来存储列名、之前值和之后值字段。
归档/历史:对于像跟踪以前的地址、电话号码等的情况,如果您的活动事务表模式在未来不会发生重大变化(如果您的历史表必须具有相同的结构),则创建一个单独的表FOO_HIST更好。如果您预计表规范化、数据类型更改添加/删除列,请以xml格式存储历史数据。定义一个具有以下列(ID、日期、模式版本、XMLData)的表。这将轻松处理模式更改。但是,您必须处理xml,这可能会引入一定程度的数据检索复杂性。
另一个选择是按[每日|每小时|其他]基础归档运行数据。大多数数据库引擎支持将数据提取到归档中。
基本上,这个想法是创建一个预定的 Windows 或 CRON 作业,该作业:
许多 SQL 数据库引擎都带有可用于此目的的工具。例如,在 Linux 上使用 MySQL 时,可以使用以下命令在 CRON 作业中安排提取:
mysqldump --all-databases --xml --lock-tables=false -ppassword | gzip -c | cat > /media/bak/servername-$(date +%Y-%m-%d)-mysql.xml.gz
你可以只对表进行分区,不是吗?
“使用 SQL Server 2008 进行分区表和索引策略 当数据库表的大小增长到数百千兆字节或更多时,加载新数据、删除旧数据和维护索引可能会变得更加困难。仅仅是表的庞大大小就导致这些操作需要花费更长的时间。即使要加载或删除的数据也可能非常庞大,使得在表上执行 INSERT 和 DELETE 操作变得不切实际。Microsoft SQL Server 2008 数据库软件提供了表分区功能,以使此类操作更易于管理。”