我正在尝试优化我的SQL查询,我总是回到这个问题,我希望能够深入了解如何最好地优化它。
为简洁起见,我们假设我有一个简单的员工表:
tbl_employees
Id HiredDateTime
------------------
1 ...
2 ...
在每个员工的另一张表格中都有版本信息:
tbl_emplyees_versioned
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个员工记录的查询时,每个记录都有几个版本,此查询开始窒息并需要几秒钟才能运行。
罢工有几次罢工,但我不确定如何克服它们。
显然,Cross Apply会增加一些性能损失。处理这样的版本化信息时是否有最佳实践?是否有更好的方法来获得最高版本的记录?
版本化表没有聚簇索引,因为Id或Version都不是唯一的。将它们连接在一起,但它不会像那样工作。相反,Id的非聚集索引和版本的另一个索引。有没有更好的方法来索引此表以获得任何性能提升?索引视图在这里真的有用吗?
答案 0 :(得分:1)
我认为构建数据的最佳方法是使用开始日期和结束日期。因此,原始表的数据结构如下所示:
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在他关于数据仓库的书中详细讨论了这个概念。
答案 1 :(得分:0)
以下是一种可以查看每位员工最新版本的方法:
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和Version之间的一个索引很可能也有帮助。
另外,请注意,如果您选择的字段不在tbl_employees_versioned中,则只需要加入tbl_employe。