这是我在这里的第一个问题,所以如果我违反任何规则,我会道歉。
这是情况。我有一张表,列出了所有员工和他们被分配到的建筑物,加上培训时间,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控件的网页完成的。它有一个范围,起始索引,所有这一切,用户需要永远更新他们的数据。 我可以更改更新过程的完成方式,但我想我会首先询问查询。
提前致谢。
答案 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的末尾丢弃该表,但是最好让它告诉它安全的一面。