1

我正在尝试优化我的 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. 显然,交叉应用增加了一些性能损失。在处理这样的版本信息时是否有最佳实践?有没有更好的方法来获得最高版本的记录?

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

4

2 回答 2

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 语句中将其添加为默认值,使列不为空,然后您始终可以使用该语句:

where getdate() between EffDate and EndDate

正如 Martin 在他的评论中指出的那样,coalesce() 可能会阻碍索引的使用(它在 SQL Server 中如此),而这没有这个问题。

这被称为缓慢变化的维度。Ralph Kimball 在他关于数据仓库的书中详细讨论了这个概念。

于 2012-05-24T19:59:28.387 回答
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_employees。

于 2012-05-24T19:47:05.157 回答