简单但有问题的更新查询

Gre*_*icz 3 performance sql-server sql-server-2012 performance-tuning

我有一个相当简单的更新/查询,多年来一直让我感到非常悲伤。

最简单的形式是:

update VillageSemaphore
set TimeStamp = getdate() 
        where VillageID in (@X, @Y)
Run Code Online (Sandbox Code Playgroud)

但是,在某些存储过程中,查询还包括此“OR VillageID in (...)”子查询

update VillageSemaphore
set TimeStamp = getdate() 
        where VillageID in (@X, @Y)

        OR VillageID in  ( -- this subquery can return many rows, many different VillageIDs
        select VSU.SupportingVillageID 
        from VillageSupportUnits VSU
        where SupportedVillageID = @Z       
            and VSU.UnitCount <> 0
            )
Run Code Online (Sandbox Code Playgroud)

请注意,此 OR 可以返回多个村庄 ID,而不仅仅是一个 @Z。这个版本的查询,有时会运行很长时间。没有索引重建,统计重建有帮助。当 Villages 表的内容被删除并重新填充时,它运行缓慢。在这种情况下,行数将只有几百行。我从来不知道这是为什么,而且总是忍受它。

但是,最近我正在查看查询计划:

在此处输入图片说明

与实际行数 (2) 相比,估计的行数 (4000) 似乎很大。

我创建了这个统计数据,但它没有帮助

CREATE STATISTICS [stat_x] ON [VillageSU]([UnitCount], [VillageID])
Run Code Online (Sandbox Code Playgroud)

所以我的问题:有什么建议为什么会这样,我可以做些什么来改善这一点?

作为参考,该表如下所示:

CREATE TABLE VillageSemaphore(
    VillageID    int         NOT NULL,
    TimeStamp    datetime    NOT NULL,
    CONSTRAINT PK97 PRIMARY KEY CLUSTERED (VillageID)
)
Run Code Online (Sandbox Code Playgroud)

更新:按照 srutzky 的建议尝试此版本的查询

CREATE TABLE #VillagesToLock (VillageID INT NOT NULL);
insert into #VillagesToLock values (@X)
insert into #VillagesToLock values (@Y)
insert into #VillagesToLock select VSU.SupportingVillageID 
        from VillageSupportUnits VSU
        where SupportedVillageID = @Z       
            and VSU.UnitCount <> 0

update VillageSemaphore set TimeStamp = getdate() 
    where VillageID in (select VillageID from #VillagesToLock)
Run Code Online (Sandbox Code Playgroud)

这是到目前为止的结果:http : //screencast.com/t/96KafTPoNGM - 查询计划看起来更好。

查询的成本也从 3% 下降到 1%,所以这看起来不错。3% 可能看起来不多,但这是一个 2500 行的存储过程!

问题:我无法将 #VillagesToLock.VillageID 设为 PK,因为它不是唯一的。我希望 #VillagesToLock 通常不超过 2-10 行。VillageSemaphore 可能有数千行。在这种情况下,是否值得在 #VillagesToLock 上放置索引?

11 月 24 日更新 我已经实施了这个替代方案 在此处输入图片说明

查询计划确实看起来好多了 在此处输入图片说明

感谢所有有时间帮助我的人!

Aar*_*and 6

虽然我不相信这是查询本身的问题(当它运行缓慢时你检查阻塞了吗?你检查运行时发生的等待类型),IN并且OR可能是一个有问题的模式来优化. 您是否考虑过将其分解为多个语句?

UPDATE dbo.VillageSemaphoreset 
  SET [TimeStamp] = GETDATE() -- TimeStamp is a terrible column name btw 
  WHERE VillageID = @X;

UPDATE dbo.VillageSemaphoreset 
  SET [TimeStamp] = GETDATE()
  WHERE VillageID = @Y;

IF (whatever condition leads you to "sometimes add this OR")
BEGIN
  UPDATE v 
    SET [TimeStamp] = GETDATE()
    FROM dbo.VillageSemaphoreset AS v
    WHERE VillageID = @Z
    AND EXISTS 
    (
      SELECT 1 FROM dbo.VillageSU AS vs
      WHERE vs.VillageID = v.VillageID
    );
END
Run Code Online (Sandbox Code Playgroud)

这可能会解决估计问题,但我同意 Max,UnitCount无论如何,带有前导列的统计数据对这些查询的估计没有帮助。