1

这是我在这里的第一个问题,所以如果我违反任何规则,我深表歉意。

这是情况。我有一张表格,列出了所有员工和他们被分配到的大楼,加上培训时间,以 ssn 作为 id 列,我有另一个表格列出了公司中的所有员工,也有 ssn,但包括姓名,和其他个人数据。第二个表包含每个员工在不同时间点的多条记录。我需要做的是从某个建筑物中选择第一个表中的所有记录,然后从第二个表中获取最新名称,并允许结果集按返回的任何列排序。

我有这个,它工作正常,只是很慢。
表格的一个非常简化的版本是:

table1 (ssn CHAR(9), buildingNumber CHAR(7), trainingHours(DEC(5,2)) (7200 rows)
table2 (ssn CHAR(9), fName VARCHAR(20), lName VARCHAR(20), sequence INT) (708,000 rows)

表 2 中的序列列是一个数字,对应于输入这些记录的预定日期,数字越大,条目越新。每个员工都有几条记录是常见的/预期的。但有几个可能没有最新的(即'8')。

我的 SProc 是:

@BuildingNumber CHAR(7), @SortField VARCHAR(25)
BEGIN
DECLARE @returnValue TABLE(ssn CHAR(9), buildingNumber CAHR(7), fname VARCHAR(20), lName VARCHAR(20), rowNumber INT)

INSERT INTO @returnValue(...) 
    SELECT(ssn,buildingNum,fname,lname,rowNum)
        FROM SELECT(...,CASE @SortField Row_Number() OVER (PARTITION BY buildingNumber ORDER BY {sortField column} END AS RowNumber)
           FROM table1 a
               OUTER APPLY(SELECT TOP 1 fName,lName FROM table2 WHERE ssn = a.ssn ORDER BY sequence DESC) AS e
           where buildingNumber = @BuildingNumber

SELECT * from @returnValue ORDER BY RowNumber
END

我有以下索引:

table1: buildingNumber(non-unique,nonclustered)
table2: sequence_ssn(unique,nonclustered)

就像我说的那样,这让我得到了正确的结果集,但它相当慢。有没有更好的方法来做到这一点?

无法更改数据库结构或表 2 的运行方式。相信我,如果它会完成。是否有任何索引可以帮助加快速度?

我查看了执行计划,它对表 2 进行了聚集索引扫描(18%),然后是计算标量(0%),然后是急切假脱机(59%),然后是过滤器(0%),然后是前 n 个排序(14%)。这是执行的 78%,所以我知道它在获取名称的部分中,只是不确定更好(更快)的方法。

我问的原因是表 1 需要用当前数据更新。这是通过带有 radgrid 控件的网页完成的。它有一个范围、起始索引等等,而且用户需要永远更新他们的数据。我可以更改更新过程的完成方式,但我想我会先询问查询。

提前致谢。

4

2 回答 2

1

我会用窗口函数来解决这个问题。这个想法是为表中重复的记录分配一个序列号(我认为table2),例如最近的记录的值为1。然后选择这个作为最近的记录:

select t1.*, t2.*
from table1 t1 join
     (select t2.*,
             row_number() over (partition by ssn order by sequence desc) as seqnum
      from table2 t2
     ) t2
     on t1.ssn = t1.ssn and t2.seqnum = 1
where t1.buildingNumber = @BuildingNumber;

我的第二个建议是使用用户定义的函数而不是存储过程:

create function XXX (
    @BuildingNumber int
)
returns table as
return (
    select t1.ssn, t1.buildingNum, t2.fname, t2.lname, rowNum
    from table1 t1 join
         (select t2.*,
                 row_number() over (partition by ssn order by sequence desc) as seqnum
          from table2 t2
         ) t2
         on t1.ssn = t1.ssn and t2.seqnum = 1
    where t1.buildingNumber = @BuildingNumber;
);

(这没有排序的逻辑,因为这似乎不是问题的中心焦点。)

然后,您可以将其称为:

select *
from dbo.XXX(<building number>);

编辑:

以下可能会进一步加快速度,因为您只选择了一小部分(ish)员工:

select *
from (select t1.*, t2.*,  row_number() over (partition by ssn order by sequence desc) as seqnum
      from table1 t1 join
           table2 t2
           on t1.ssn = t1.ssn
      where t1.buildingNumber = @BuildingNumber
     ) t
where seqnum = 1;

最后,我怀疑以下可能是最快的:

select t1.*, t2.*,  row_number() over (partition by ssn order by sequence desc) as seqnum
from table1 t1 join
     table2 t2
     on t1.ssn = t1.ssn
where t1.buildingNumber = @BuildingNumber and
      t2.sequence = (select max(sequence) from table2 t2a where t2a.ssn = t1.ssn)

在所有这些情况下,索引table2(ssn, sequence)应该有助于提高性能。

于 2013-07-24T00:47:35.357 回答
0

尝试使用一些临时表而不是表变量。不确定您正在使用哪种系统,但我运气不错。临时表实际上会写入驱动器,因此您不会在内存中保存和处理这么多。根据其他系统的使用情况,这可能会奏效。

使用#Tablename 而不是@Tablename 简单地定义临时表。将名称排序子查询放在临时表中,然后再启动其他所有内容并对其进行连接。

只要确保在最后放下桌子。当它断开连接时,它会在 SP 的末尾丢弃表,但为了安全起见,最好让它丢弃。

于 2013-07-24T00:15:23.347 回答