使用哈希流不同运算符进行查询调整更新

New*_*DBA 0 sql-server execution-plan update sql-server-2014 query-performance

需要一些帮助来理解UPDATE以下语句之一的缓慢:-

UPDATE TOP (100) xyz
SET xyz.flag = 1
OUTPUT inserted.Rcode, inserted.EDR, inserted.id, abc.EID,abc.CID,abc.ENID,abc.Cdate
FROM dbo.table1 xyz WITH (UPDLOCK, READPAST)
INNER JOIN dbo.table2 abc WITH (NOLOCK)
on xyz.id=abc.id
WHERE xyz.flag = 0
Run Code Online (Sandbox Code Playgroud)

表1有大约。50 万行,表 2 大约有。500 万行

慢计划

哈希匹配不同流运算符显示黄色警报,消息为:

操作员使用 Tempdb 溢出数据,以溢出级别 4 和 1 个溢出线程执行”

构建残差:

database.dbo.table2.id as abc.id = database.dbo.table2.id as abc.id

我截了屏。不幸的是,由于安全原因,我不能提供更多,甚至不能提供匿名计划。从我的工作站我无法访问互联网,所以我无法让计划浏览器在那里运行。

在此处输入图片说明

通常,对于较小的行子集,它在几秒钟内,就像我们刚刚匹配 10K 行或其他东西一样。但是随着数据量的增加,这似乎是一个临界点,应用程序无法承受 1 分钟的运行时间。从 SSMS 我得到 30 秒,但从应用程序我们有平均。约 50 秒 RCSI 处于测试阶段。

我的好计划没有显示 Hash Match Flow Distinct 运算符,如我的屏幕截图所示,而其余计划保持不变。好的一个在 3 秒左右完成。正如所见,该操作员花费了近 16 秒。我们可以通过适当的索引或查询重写来消除它吗?

表模式

CREATE TABLE dbo.table1
(
    Recid  VARCHAR(128) COLLATE SQL_Latin1_general_CP1_CI_AS NOT NULL,
    Cdate DATETIME NULL,
    flag BIT   NULL DEFAULT (0),
    Rcode INT NULL,
    EDR VARCHAR(255) COLLATE SQL_Latin1_general_CP1_CI_AS NULL,
    id BIGINT NULL
);

CREATE TABLE dbo.table2
(
    ENID BIGINT IDENTITY(1,1) NOT NULL,
    EID VARCHAR(50) COLLATE SQL_Latin1_general_CP1_CI_AS NOT NULL,
    CID VARCHAR(350) COLLATE SQL_Latin1_general_CP1_CI_AS NOT NULL,
    CDate DATETIME NOT NULL DEFAULT(getdate()),
    id BIGINT NOT NULL,

    CONSTRAINT PK_ENID PRIMARY KEY (ENID ASC, EID ASC),
);
Run Code Online (Sandbox Code Playgroud)
-- table1
CREATE INDEX ix_Cdate on dbo.table1 (Cdate) WITH (FILLFACTOR=100);
CREATE CLUSTERED INDEX ix_Recid on dbo.table1 (Recid) WITH (FILLFACTOR=80);

-- table2
CREATE INDEX ix_ENID_id on dbo.table2 (ENID,id) WITH (FILLFACTOR=100);
Run Code Online (Sandbox Code Playgroud)

变化

我所做的更改和一些数字:

  • 添加提示OPTION (QUERYTRACEON 4138)- 平均。执行时间比原来的 50 秒减少了 7 秒,但应用程序团队似乎无权在代码中执行此操作。需要进一步检查这一点。

  • OPTION (ORDER GROUP)给出了相同的结果。50秒所以没有改善。

  • 按照建议添加索引:

CREATE INDEX i ON dbo.table2 (id) INCLUDE (CID, CDate);

那里没有太多改进。平均 45 秒和计划类似于此问题中所附的计划(顶级计划)。

在每次测试之前和之后,我确保计划不是从以前的缓存计划中生成的。

快速计划

对于两个表中相同数量的行,附加更快且没有任何数据或查询更改的计划仍然很快。应用团队全天不断提交上述查询,以通过完成 TOP 100 来完成批次。根据一些小费数字有一个计划更改,以下是好的计划的外观:

在此处输入图片说明

编辑:- 一切都没有改变,没有代码更改或添加任何索引,正如我在尝试添加提示 (FORCESEEK) 时所建议的那样,它给了我以下错误

由于此查询中定义的提示,查询处理器无法生成查询计划。在不指定任何提示且不使用 SET FORCEPLAN 的情况下重新提交查询。

Pau*_*ite 8

你有三个主要问题:

  1. 没有有用的索引来支持 上的连接id
  2. TOP (100)介绍一排的目标,所以估计可能太低。
  3. UPDATE不确定性

table2 中的多行可以匹配 on id,因此不清楚 table2 中的哪个匹配行应该用于为OUTPUT子句提供值。聚合用于在 table2 上分组id并为其他列选择ANY匹配值。由于行目标,聚合是Flow Distinct

ANY在非确定性UPDATE语句中使用聚合需要非常小心,因为您可能会得到不正确的结果

问题中没有足够的细节来提出高质量的建议,但是:

  1. 添加一个索引,如 CREATE INDEX i ON dbo.table2 (id) INCLUDE (CID, CDate);
  2. 使用OPTION (QUERYTRACEON 4138)禁用该行的目标,或OPTION (ORDER GROUP)使用流聚合,而不是哈希的。
  3. 如何修复不确定性UPDATE取决于数据关系。关键点是从源中最多识别出一行与每个目标行匹配。通常,这将涉及唯一索引或约束,或使用ROW_NUMBERTOP (1)

第 2 步可能是必要的,也可能不是。我添加它是为了完整性。

您可能会发现通过以这种形式编写查询更容易可视化问题并调整查询:

UPDATE TOP (100) 
    xyz WITH (UPDLOCK, READPAST)
SET xyz.flag = 1
OUTPUT 
    inserted.Rcode, inserted.EDR, inserted.id, 
    abc.EID, abc.CID, abc.ENID, abc.Cdate
FROM dbo.table1 AS xyz
CROSS APPLY
(
    -- At most one source row per target row
    SELECT TOP (1) 
        abc.* 
    FROM dbo.table2 AS abc
    WHERE
        abc.id = xyz.id
    -- ORDER BY something to choose the one row
) AS abc
WHERE 
    xyz.flag = 0;
Run Code Online (Sandbox Code Playgroud)

执行计划:

预期计划形状

我可能不会打扰 table1 上的过滤索引,但是如果您确实想尝试它,这似乎很合适:

CREATE INDEX i 
ON dbo.table1 (Recid) 
INCLUDE (id, flag) 
WHERE flag = 0;
Run Code Online (Sandbox Code Playgroud)

如果您想继续使用问题中给出的更新语法而不正确解决所有潜在问题,您可能会发现这更快:

UPDATE TOP (100) xyz
SET xyz.flag = 1
OUTPUT inserted.Rcode, inserted.EDR, inserted.id, abc.EID,abc.CID,abc.ENID,abc.Cdate
FROM dbo.table1 xyz WITH (UPDLOCK, READPAST)
INNER JOIN dbo.table2 abc WITH (NOLOCK, FORCESEEK)
on xyz.id=abc.id
WHERE xyz.flag = 0;
Run Code Online (Sandbox Code Playgroud)