【问题标题】:Slow WHERE EXISTS in SQL ServerSQL Server 中的 WHERE EXISTS 慢
【发布时间】:2019-05-14 01:55:19
【问题描述】:

我有一个使用 WHERE EXISTS 的删除查询,但速度很慢。

delete 
from dwload.dbo.TF_Full_Backup_AT
where exists (select *
              from dwload.dbo.TFTimesheetTemp t
              inner join dwload.dbo.TFEmpTemp e on t.EmployeeCode = e.EmployeeCode
              where dwload.dbo.TF_Full_Backup_AT.TFSourceID = e.TFSourceID
                and dwload.dbo.TF_Full_Backup_AT.StartTime = t.StartTime
                and dwload.dbo.TF_Full_Backup_AT.EndTime = t.EndTime
                and dwload.dbo.TF_Full_Backup_AT.ActivityCode = t.ActivityCode
                and dwload.dbo.TF_Full_Backup_AT.PaymentCode = t.PaymentCode
                and dwload.dbo.TF_Full_Backup_AT.BranchCode = t.BranchCode)

我怎样才能重写这个查询以更快地运行?

【问题讨论】:

  • exists括号内的查询也慢吗?
  • @Hogan - 如果选择位于 exists 子句中,则无关紧要。
  • 至少您应该include the actual Execution Plan,您可以使用Paste the Plan 并在您的问题中分享链接。另外try to read it yourself,也许您可​​以通过查询找出性能问题。
  • @Hogan:因为它是IF EXISTS() 的一部分,所以没有数据被读取-所以SELECT * SELECT 1 没有区别 b>
  • 您要从中删除的表有多大?多少个索引/触发器?这可能很慢,因为在单个操作中记录所有删除活动所涉及的 I/O,而不是与用于查找要删除的行的查询有关。见this post

标签: sql-server tsql where exists


【解决方案1】:

试试下面的代码(我认为你使用的是 MS SQL)

delete 
from dwload.dbo.TF_Full_Backup_AT
from dwload.dbo.TFTimesheetTemp t
              inner join dwload.dbo.TFEmpTemp e on t.EmployeeCode = e.EmployeeCode
INNER JOIN 
dwload.dbo.TF_Full_Backup_AT
ON              
 dwload.dbo.TF_Full_Backup_AT.TFSourceID = e.TFSourceID
                and dwload.dbo.TF_Full_Backup_AT.StartTime = t.StartTime
                and dwload.dbo.TF_Full_Backup_AT.EndTime = t.EndTime
                and dwload.dbo.TF_Full_Backup_AT.ActivityCode = t.ActivityCode
                and dwload.dbo.TF_Full_Backup_AT.PaymentCode = t.PaymentCode
                and dwload.dbo.TF_Full_Backup_AT.BranchCode = t.BranchCode

说明:
您首先编写要删除的内容 然后你写 JOIN 表达式 那应该更快

【讨论】:

  • 应该不会对性能产生任何影响。真正的原因尚不清楚,所以这个答案纯属猜测。只有在 OP 发布查询计划之后,我们/您才能更好地了解导致性能瓶颈的原因。
  • 编写内部连接后,即使您不仔细查看查询计划,您也可以更好地查看索引、搜索等内容
【解决方案2】:

很难从您的查询中看出发生了什么。

试试这样的:

DECLARE @ToDelete TABLE
(
    TF_Full_Backup_AT_ID INT PRIMARY KEY
)

DECLARE @LastTimeStamp DATETIME = GETUTCDATE()

INSERT INTO @ToDelete (TF_Full_Backup_AT_ID)
SELECT DISTINCT b.TF_Full_Backup_AT_ID
from TF_Full_Backup_AT b
    JOIN dwload.dbo.TFTimesheetTemp t ON b.StartTime = t.StartTime
                                        and b.EndTime = t.EndTime
                                        and b.ActivityCode = t.ActivityCode
                                        and b.PaymentCode = t.PaymentCode
                                        and b.BranchCode = t.BranchCode
    inner join dwload.dbo.TFEmpTemp e on t.EmployeeCode = e.EmployeeCode
where dwload.dbo.TF_Full_Backup_AT.TFSourceID = e.TFSourceID

PRINT 'Time taken to populate the temporary table: ' + CONVERT(varchar, CAST((GETUTCDATE()-@LastTimeStamp) as time(3)))
SET @LastTimeStamp = GETUTCDATE()

DELETE b
FROM @ToDelete d
    JOIN dwload.dbo.TF_Full_Backup_AT b ON b.TF_Full_Backup_AT_ID = d.TF_Full_Backup_AT_ID


PRINT 'Time taken to delete the rows: ' + CONVERT(varchar, CAST((GETUTCDATE()-@LastTimeStamp) as time(3)))

注意:我假设您的桌子上有一个主键 TF_Full_Backup_AT_ID

如果您发现实际删除(而不是查找要删除哪一行的查询)花费大量时间,则您的查询无能为力。可以批量删除,例如:

WHILE 1 = 1
    BEGIN
        DELETE TOP (5000) b
            FROM @ToDelete d
                JOIN dwload.dbo.TF_Full_Backup_AT b ON b.TF_Full_Backup_AT_ID = d.TF_Full_Backup_AT_ID

        IF @@ROWCOUNT = 0
            BREAK

    END

【讨论】:

  • 谢谢凯文。 `dwload.dbo.TFTimesheetTemp 不幸的是没有密钥,它由我从系统时钟中获取的 API 填充。我可以通过将开始日期和时间的整数值与员工代码连接来探索给它一个键;这应该唯一标识每一行
  • 您可以添加一个 int 标识列,该列会自动填充以获取唯一键。但是,根据您上面的 cmets,问题不是查询,而是删除。
猜你喜欢
  • 1970-01-01
  • 2019-05-18
  • 1970-01-01
  • 2013-04-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多