SQL从一个表中选择记录集,将每个记录连接到匹配1列的第二个表的前1个记录,按第二个表中的列排序

时间:2013-07-23 23:04:04

标签: sql-server

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

这是情况。我有一张表,列出了所有员工和他们被分配到的建筑物,加上培训时间,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(非唯一,非聚集)
table2:sequence_ssn(唯一的,非聚集的)

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

无法更改数据库结构或表2的运行方式。相信我是否会这样做。我能做出哪些索引可以帮助加快速度吗?

我查看了执行计划,它在表2上有一个聚簇索引扫描(18%),然后是计算标量(0%),然后是一个急切的假脱机(59%),然后是一个过滤器(0) %),然后排在前面(14%)。 这是78%的执行,所以我知道它是在获取名称的部分,只是不确定更好(更快)的方式来做到这一点。

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

提前致谢。

2 个答案:

答案 0 :(得分: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>);

编辑:

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

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)上的索引应该有助于提升绩效。

答案 1 :(得分:0)

尝试使用一些临时表而不是表变量。不确定你正在使用什么样的系统,但我运气很好。临时表实际写入驱动器,因此您不会在内存中持有和处理这么多。根据其他系统的使用情况,这可能会有所帮助。

使用#Tablename而不是@Tablename简单定义临时表。将名称排序子查询放在临时表中,然后触发其他所有内容并与其建立连接。

请务必将表格放在最后。当它断开连接时,它会在SP的末尾丢弃该表,但是最好让它告诉它安全的一面。