在T-SQL中设置和查询版本化记录的最佳实践

时间:2012-05-24 19:41:51

标签: sql tsql

我正在尝试优化我的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个员工记录的查询时,每个记录都有几个版本,此查询开始窒息并需要几秒钟才能运行。

罢工有几次罢工,但我不确定如何克服它们。

  1. 显然,Cross Apply会增加一些性能损失。处理这样的版本化信息时是否有最佳实践?是否有更好的方法来获得最高版本的记录?

  2. 版本化表没有聚簇索引,因为Id或Version都不是唯一的。将它们连接在一起,但它不会像那样工作。相反,Id的非聚集索引和版本的另一个索引。有没有更好的方法来索引此表以获得任何性能提升?索引视图在这里真的有用吗?

2 个答案:

答案 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。