基于 SELECT TOP 1 的 UPDATE 查询性能缓慢

Posted

技术标签:

【中文标题】基于 SELECT TOP 1 的 UPDATE 查询性能缓慢【英文标题】:Slow Performance in UPDATE Query Based on SELECT TOP 1 【发布时间】:2018-05-29 15:01:06 【问题描述】:

我正在尝试提高运行时间较长的查询的性能,并且希望能提供任何有关我需要采取不同措施以使性能合理的指针。 (索引和估计的执行计划信息包括在下面。)

正在更新的表 table1 的记录数不足 1000 条,但正在查询的表的记录数在 1 亿条范围内。奇怪的是,我对最旧值和最新值使用几乎相同的查询,但对最新值的查询会在两秒内返回。这是对最旧值的查询;运行时间超过两分钟:

UPDATE table1
SET firstVal = (
                    SELECT TOP 1 val 
                    FROM table2 
                    WHERE table1.ID = ID
                    AND valID = 123
                    ORDER BY entryDate
                    )

查询最近的,两秒后返回:

UPDATE table1
SET lastVal = (
                    SELECT TOP 1 val 
                    FROM table2  
                    WHERE table1.ID = ID
                    AND valID = 123
                    ORDER BY entryDate DESC
                    )

索引:

CREATE NONCLUSTERED INDEX [table2_IX9] ON [dbo].[table2]
(
    [valID] ASC,
    [entryDate] ASC,
    [ID] ASC
)
INCLUDE (   [val]) WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [AB]
GO

索引搜索(非集群)成本:68% 最高成本:29%

这个查询可以用更好的方式来优化吗?我需要在索引方面有所不同吗?提前感谢您的任何指导!

【问题讨论】:

entryDate 中有 NULL 值吗? 好问题!但没有 我在这里可能是错的,但尝试将索引从 valID, entryDate, ID 更改为 valId, ID, entryDate(或添加第二个索引) - 我假设你想要 2 列where 子句在您的索引中 order by 之一之前,否则,您的搜索无法完全按原样使用索引...但我是 SQL 新手... 感谢您的建议。我会研究它,但我需要一段时间才能真正看到这是否有效,因为我的测试环境并没有这么多,我必须对生产环境进行更改控制 约翰对索引的评论是正确的 【参考方案1】:

这对 cme​​ts 来说太长了,但这样可能会更快:

UPDATE T1
SET firstVal = T2.val
FROM Table1 T1
     --This might need to be an OUTER APPLY, but impossible to tell from your post
     CROSS APPLY (SELECT TOP 1 ca.val
                  FROM Table2 ca
                  WHERE ca.ID = T1.ID
                    AND ca.valID = 123
                  ORDER BY ca.entryDate) T2;

您拥有它的方式可能意味着查询是针对每一行单独运行的,而不是作为数据集运行的。这种方式将强制查询作为数据集运行。

【讨论】:

谢谢!我相信你是对的。现在就试试你的想法 感谢您的想法。不幸的是,估计的执行计划和实际运行表明这个查询与我之前的版本基本相同:(【参考方案2】:

我最终做了什么:

SELECT ID, val, entryDate
INTO #tmpT2
FROM table2 
WHERE ID IN (SELECT ID FROM table1)
AND valID = 123

然后稍作调整,将原始查询中的 #tmpT2 替换为 table2:

UPDATE table1
SET firstVal = (
                    SELECT TOP 1 val 
                    FROM #tmpT2 
                    WHERE table1.ID = ID
                    ORDER BY entryDate
                    )

为什么:刚刚意识到索引到位看起来与它应该的完全一样,并且 table1 非常小,我可以尝试简单地使用临时表并检索 valID = 123 的所有 ID、vals 和 entryDates 到临时表。这一步需要一秒钟,并且使用原始查询是瞬时的。感谢您花时间思考这个问题并提供建议!

【讨论】:

以上是关于基于 SELECT TOP 1 的 UPDATE 查询性能缓慢的主要内容,如果未能解决你的问题,请参考以下文章

基于 TOP 的 SQL UPDATE

基于 Access 中保存的 SELECT 查询的 UPDATE 查询?

SQL Inject漏洞手工测试:基于报错的信息获取(select/delete/update/insert)

sql update 怎样更新只定条数

myql update from 语句

SQL Update TOP Update Order by 和output的使用