cor*_*iKa 35 sql versioning orm
这是一个虚构的场景,其中包含一些填充数据.出于税收目的,我的虚构公司必须保留历史数据的记录.出于这个原因,我在表格中添加了一个版本列.
TABLE EMPLOYEE: (with personal commentary)
|ID | VERSION | NAME | Position | PAY |
+---+---------+------------+----------+-----+
| 1 | 1 | John Doe | Owner | 100 | Started company
| 1 | 2 | John Doe | Owner | 80 | Pay cut to hire a coder
| 2 | 1 | Mark May | Coder | 20 | Hire said coder
| 2 | 2 | Mark May | Coder | 30 | Productive coder gets raise
| 3 | 1 | Jane Field | Admn Asst| 15 | Need office staff
| 2 | 3 | Mark May | Coder | 35 | Productive coder gets raise
| 1 | 3 | John Doe | Owner | 120 | Sales = profit for owner!
| 3 | 2 | Jane Field | Admn Asst| 20 | Raise for office staff
| 4 | 1 | Cody Munn | Coder | 20 | Hire another coder
| 4 | 2 | Cody Munn | Coder | 25 | Give that coder raise
| 3 | 3 | Jane Munn | Admn Asst| 20 | Jane marries Cody <3
| 2 | 4 | Mark May | Dev Lead | 40 | Promote mark to Dev Lead
| 4 | 3 | Cody Munn | Coder | 30 | Give Cody a raise
| 2 | 5 | Mark May | Retired | 0 | Mark retires
| 5 | 1 | Joey Trib | Dev Lead | 40 | Bring outside help for Dev Lead
| 6 | 1 | Hire Meplz | Coder | 10 | Hire a cheap coder
| 3 | 4 | Jane Munn | Retired | 0 | Jane quits
| 7 | 1 | Work Fofre | Admn Asst| 10 | Hire Janes replacement
| 8 | 1 | Fran Hesky | Coder | 10 | Hire another coder
| 9 | 1 | Deby Olav | Coder | 25 | Hire another coder
| 4 | 4 | Cody Munn | VP Ops | 80 | Promote Cody
| 9 | 2 | Deby Olav | VP Ops | 80 | Cody fails at VP Ops, promote Deby
| 4 | 5 | Cody Munn | Retired | 0 | Cody retires in shame
| 5 | 2 | Joey Trib | Dev Lead | 50 | Give Joey a raise
+---+---------+------------+----------+-----+
Run Code Online (Sandbox Code Playgroud)
现在,如果我想做一些像"获取当前程序员列表"这样的事情,我不能这么做,SELECT * FROM EMPLOYEE WHERE Position = 'Coder'因为这会返回大量的历史数据......这很糟糕.
我正在寻找处理这种情况的好主意.我看到了一些跳出来的选项,但是我肯定有人会说"哇,这是一个新手的错误,发光......试试这个大小:"这就是这个地方的全部,对吧?:-)
创意编号1:使用当前版本保留版本表
TABLE EMPLOYEE_VERSION:
|ID |VERSION|
+---+-------+
| 1 | 3 |
| 2 | 5 |
| 3 | 4 |
| 4 | 6 |
| 5 | 2 |
| 6 | 1 |
| 7 | 1 |
| 8 | 1 |
| 9 | 2 |
+---+-------+
Run Code Online (Sandbox Code Playgroud)
虽然我不确定如何使用单个查询来做到这一点,但我确信它可以完成,我敢打赌,我可以通过相当少的努力来解决这个问题.
当然,每次插入EMPLOYEE表时,我都必须更新此表,以增加给定ID的版本(或者在创建新ID时插入版本表).
这的开销似乎是不可取的.
想法2:保留存档表和主表.在更新主表之前,将我要覆盖的行插入到归档表中,并像往常一样使用主表,就好像我不关心版本控制一样.
想法3:找到一个增加某些东西的查询SELECT * FROM EMPLOYEE WHERE Position = 'Coder' and version=MaxVersionForId(EMPLOYEE.ID)...不完全确定我是怎么做到的.这对我来说似乎是最好的主意,但我现在还不确定.
想法编号4:为"当前"创建一列并添加"WHERE current = true AND ..."
它发生在我之前,人们肯定已经做过这件事,碰到这些相同的问题,并有分享的见解,所以我来收集它!:)我已经尝试在这里找到问题的例子,但它们似乎专门针对特定场景.
谢谢!
编辑1:
首先,我感谢所有答案,你们都说了同样的话 - DATE比...更好VERSION NUMBER.我采用的一个原因是VERSION NUMBER简化服务器中的更新过程以防止出现以下情况
人员A在他的会话中加载员工记录3,它具有版本4.人员B在他的会话中加载员工记录3,它具有版本4.人员A进行更改和提交.这是有效的,因为数据库中的最新版本是4.现在是5. Person B进行更改和提交.这失败了,因为最新的版本是5,而他的版本是4.
该EFFECTIVE DATE模式将如何解决这个问题?
编辑2:
我想我可以做这样的事情:人A在他的会话中加载员工记录3,它的生效日期是1-1-2010,下午1:00,没有任何消息.B人在他的会话中加载员工记录3,其生效日期为1-1-2010,1:00 pm,没有任何费用.人员A进行更改和提交.旧副本进入存档表(基本上是想法2),考试日期为9/22/2010下午1:00.主表的更新版本的生效日期为2010年9月22日下午1:00.B人进行更改和提交.提交失败,因为生效日期(在数据库和会话中)不匹配.
Not*_*tMe 35
我想你已经走错了路.
通常,对于版本控制或存储历史数据,您可以执行两项(或两项)操作之一.
您有一个单独的表,模仿原始表+更改日期的日期/时间列.每当更新记录时,您都会在更新之前将现有内容插入到历史记录表中.
您有一个单独的仓库数据库.在这种情况下,您可以像上面的#1那样对其进行版本修改,或者您只需每隔一段时间对其进行一次快照(每小时,每天,每周......)
将您的版本号保存在与正常版本相同的表中有几个问题.首先,表格大小会像疯了一样增长.这将对正常的生产查询施加持续的压力.
其次,它会大大增加连接等的查询复杂性,以确保使用每个记录的最新版本.
Zac*_*tes 30
你在这里所拥有的是一个缓慢变化的维度(SCD).有一些经过验证的方法可以解决它:
http://en.wikipedia.org/wiki/Slowly_changing_dimension
以为我会补充说,因为似乎没有人通过名字来称呼它.
Red*_*ter 10
这是我建议的方法,过去对我来说效果很好:
StartDate和EndDate列ID,并且只有一个记录与a NULL EndDate相同ID(这是您当前有效的记录)StartDate和EndDate; 这应该会给你合理的表现这将很容易让您按日期报告:
select *
from MyTable
where MyReportDate between StartDate and EndDate
Run Code Online (Sandbox Code Playgroud)
或获取当前信息:
select *
from MyTable
where EndDate is null
Run Code Online (Sandbox Code Playgroud)
Jam*_*ane 10
我为最近的数据库设计的方法是使用如下修订:
将您的实体保存在两个表中:
"employee"存储主键ID以及您不希望进行版本控制的任何数据(如果有).
"employee_revision"存储有关雇员的所有显着数据,其中包含employee表的外键和外键,"RevisionID"表示名为"revision"的表.
创建一个名为"revision"的新表.这可以由数据库中的所有实体使用,而不仅仅是员工.它包含主键的标识列(或自动编号,或者数据库调用的任何内容).它还包含EffectiveFrom和EffectiveTo列.我还在表上有一个文本列 - entity_type - 出于人类可读性的原因,它包含主修订表的名称(在本例中为"employee").修订表不包含外键.EffectiveFrom的默认值是19-Jan-1900,EffectiveTo的默认值是31-Dec-9999.这使我无法简化日期查询.
我确保修订表已在(EffectiveFrom,EffectiveTo,RevisionID)以及(RevisionID,EffectiveFrom,EffectiveTo)上编入索引.
然后我可以使用连接和简单的<>比较来为任何日期选择适当的记录.这也意味着实体之间的关系也是完全版本化的.实际上,我发现使用SQL Server表值函数可以非常简单地查询任何日期.
这是一个示例(假设您不希望对员工姓名进行版本设置,以便在他们更改名称时,此更改在历史上是有效的).
--------
employee
--------
employee_id | employee_name
----------- | -------------
12351 | John Smith
-----------------
employee_revision
-----------------
employee_id | revision_id | department_id | position_id | pay
----------- | ----------- | ------------- | ----------- | ----------
12351 | 657442 | 72 | 23 | 22000.00
12351 | 657512 | 72 | 27 | 22000.00
12351 | 657983 | 72 | 27 | 28000.00
--------
revision
--------
revision_id | effective_from | effective_to | entity_type
----------- | -------------- | ------------ | -----------
657442 | 01-Jan-1900 | 03-Mar-2007 | EMPLOYEE
657512 | 04-Mar-2007 | 22-Jun-2009 | EMPLOYEE
657983 | 23-Jun-2009 | 31-Dec-9999 | EMPLOYEE
Run Code Online (Sandbox Code Playgroud)
将修订元数据存储在单独的表中的一个优点是,可以轻松地将其一致地应用于所有实体.另一个是它更容易扩展它以包括其他东西,例如分支或场景,而不必修改每个表.我的主要原因是它使您的主要实体表保持清晰和整洁.
(上面的数据和示例都是虚构的 - 我的数据库没有为员工建模).
尽管这个问题在 8 年前就有人提出过,但值得一提的是,SQL Server 2016 中有一个专门针对此问题的功能。系统版本化的临时表
SQL Server 2016 及更高版本中的每个表都可以有一个历史表,历史数据将由 SQL Server 本身自动填充。
您只需要向表中添加两个 datetime2 列和一个子句:
CREATE TABLE Employee
(
Id int NOT NULL PRIMARY KEY CLUSTERED,
[Name] varchar(50) NOT NULL,
Position varchar(50) NULL,
Pay money NULL,
ValidFrom datetime2 GENERATED ALWAYS AS ROW START NOT NULL,
ValidTo datetime2 GENERATED ALWAYS AS ROW END NOT NULL,
PERIOD FOR SYSTEM_TIME (ValidFrom,ValidTo)
)
WITH (SYSTEM_VERSIONING = ON);
Run Code Online (Sandbox Code Playgroud)
系统版本表创建了一个临时表,用于维护数据的历史记录。您可以使用自定义名称WITH (SYSTEM_VERSIONING = ON ( HISTORY_TABLE = dbo.EmployeeHistory ) );
在此链接中,您可以找到有关系统版本时态表的更多详细信息。
正如@NotMe 提到的,历史表可以增长得非常快,所以有几种方法可以解决这个问题。看看这里
| 归档时间: |
|
| 查看次数: |
22476 次 |
| 最近记录: |