在T-SQL中设置和查询版本记录的最佳实践
我正在尝试优化我的SQL查询,我总是回到这个问题上来,我希望能够深入了解如何最好地优化它 为简洁起见,假设我有一个简单的employee表: tbl_员工在T-SQL中设置和查询版本记录的最佳实践,sql,tsql,Sql,Tsql,我正在尝试优化我的SQL查询,我总是回到这个问题上来,我希望能够深入了解如何最好地优化它 为简洁起见,假设我有一个简单的employee表: tbl_员工 Id HiredDateTime ------------------ 1 ... 2 ... 在另一个表中为每个员工设置了信息版本: Select Id, Name, HourlyWage, Version FROM ( Select E.Id, V.Name, V.Hourly
Id HiredDateTime
------------------
1 ...
2 ...
在另一个表中为每个员工设置了信息版本:
Select Id, Name, HourlyWage, Version
FROM (
Select E.Id, V.Name, V.HourlyWage, V.Version,
row_number() OVER (PARTITION BY V.ID ORDER BY V.Version DESC) as nRow
From tbl_employees E
Inner Join tbl_employees_versioned V ON E.Id = V.Id
) A
WHERE A.nRow = 1
tbl_员工版本化
Id Version Name HourlyWage
-------------------------------
1 1 Bob 10
1 2 Bob 20
1 3 Bob 30
2 1 Dan 10
2 2 Dan 20
以下是在视图中检索最新版本记录的方式:
Select tbl_employees.Id, employees_LatestVersion.Name, employees_LatestVersion.HourlyWage, employees_LatestVersion.Version
From tbl_employees
Inner Join tbl_employees_versioned
ON tbl_employees.Id = tbl_employees_versioned.Id
CROSS APPLY
(SELECT Id, Max(Version) AS Version
FROM tbl_employees_versioned AS employees_LatestVersion
WHERE Id = tbl_employees_versioned.Id
GROUP BY Id) AS employees_LatestVersion
要获得这样的响应:
Id Version Name HourlyWage
-------------------------------
1 3 Bob 30
2 2 Dan 20
当拉取一个有500多个雇员记录的查询时,每个记录都有几个版本,这个查询开始阻塞,需要几秒钟才能运行
有几次一拍即中,但我不知道如何克服它们
这里有一种方法可以查看每个员工的最新版本:
Select Id, Name, HourlyWage, Version
FROM (
Select E.Id, V.Name, V.HourlyWage, V.Version,
row_number() OVER (PARTITION BY V.ID ORDER BY V.Version DESC) as nRow
From tbl_employees E
Inner Join tbl_employees_versioned V ON E.Id = V.Id
) A
WHERE A.nRow = 1
我怀疑这将比您以前的解决方案表现得更好。在tbl_employees_versioned中跨Id和版本的一个索引很可能也会有所帮助
另外,请注意,如果您选择的字段不在tbl_employees(tbl_employees(tbl_员工)版本中,则只需加入tbl_employees(tbl_员工)。以下是一种查看每个员工最新版本的方法:
Select Id, Name, HourlyWage, Version
FROM (
Select E.Id, V.Name, V.HourlyWage, V.Version,
row_number() OVER (PARTITION BY V.ID ORDER BY V.Version DESC) as nRow
From tbl_employees E
Inner Join tbl_employees_versioned V ON E.Id = V.Id
) A
WHERE A.nRow = 1
我怀疑这将比您以前的解决方案表现得更好。在tbl_employees_versioned中跨Id和版本的一个索引很可能也会有所帮助
另外,请注意,如果您选择的字段不在tbl_employees版本中,您只需要加入tbl_employees。我认为构建数据的最佳方法是使用开始日期和结束日期。因此,原始表的数据结构如下所示:
create table tbl_EmployeesHistory (
EmployeeHistoryId int,
EffDate date not null,
EndDate date,
-- Fields that describe the employee during this time
)
然后,您可以使用以下视图查看当前版本:
create view vw_Employees as
select *
from tbl_EmployeesHistory
where EndDate is NULL
在某些情况下,如果允许将来的结束日期,where条款将是:
where coalesce(EndDate, getdate()) >= getdate()
或者,在这种情况下,您可以将EndDate默认为将来的某个日期,例如“01-o1-9999”。您可以在create table语句中将其作为默认值添加,使列不为null,然后始终可以使用该语句:
where getdate() between EffDate and EndDate
正如Martin在他的评论中指出的那样,coalesce()可能会妨碍索引的使用(在SQL Server中是这样),而这并没有这个问题
这称为缓慢变化的维度。拉尔夫·金博尔(Ralph Kimball)在他关于数据仓库的书中详细讨论了这个概念。我认为构建数据的最佳方法是使用开始日期和结束日期。因此,原始表的数据结构如下所示:
create table tbl_EmployeesHistory (
EmployeeHistoryId int,
EffDate date not null,
EndDate date,
-- Fields that describe the employee during this time
)
然后,您可以使用以下视图查看当前版本:
create view vw_Employees as
select *
from tbl_EmployeesHistory
where EndDate is NULL
在某些情况下,如果允许将来的结束日期,where条款将是:
where coalesce(EndDate, getdate()) >= getdate()
或者,在这种情况下,您可以将EndDate默认为将来的某个日期,例如“01-o1-9999”。您可以在create table语句中将其作为默认值添加,使列不为null,然后始终可以使用该语句:
where getdate() between EffDate and EndDate
正如Martin在他的评论中指出的那样,coalesce()可能会妨碍索引的使用(在SQL Server中是这样),而这并没有这个问题
这称为缓慢变化的维度。拉尔夫·金博尔(Ralph Kimball)在其有关数据仓库的著作中详细讨论了这一概念。为什么历史版本与当前版本在同一张表中?你不能把它们放在一个归档/审计表中吗?如果还不算太晚,你可以用一个主表和一个归档表来代替。主表将具有最新版本,存档表将具有所有以前(和已删除)的版本。我想99.99%的查询负载是针对最新版本的。为了支持能够查看过去.01%时间的查询,像这样的版本控制会增加复杂性并降低性能。每个员工都进行了版本控制,以便UI可以列出每个编辑的更改,并还原到特定版本是必要的。我认为在用新版本覆盖之前,只附加版本记录而不是将记录复制到存档表更容易。不是这样吗?@Levitikon-不需要花费太多的精力就可以找到一个触发器,它可以同时存档行的先前版本,并且可以避免您的所有其他查询都必须遍历这些冗余数据。您还可以考虑将审核表与当前数据中使用的一个单独的文件组进行比较。为什么历史版本与当前版本处于同一表中?你不能把它们放在一个归档/审计表中吗?如果还不算太晚,你可以用一个主表和一个归档表来代替。主表将具有最新版本,存档表将具有所有以前(和已删除)的版本。我想99.99%的查询负载是针对最新版本的。为了支持能够查看过去.01%时间的查询,像这样的版本控制会增加复杂性并降低性能。每个员工都进行了版本控制,以便UI可以列出每个编辑的更改,并还原到特定版本是必要的。我认为在用新版本覆盖之前,只附加版本记录而不是将记录复制到存档表更容易。不是这样吗?@Levitikon-不需要花费太多的精力就可以找到一个触发器,它可以同时存档行的先前版本,并且可以避免您的所有其他查询都必须遍历这些冗余数据。哟