合并连接性能调优

Dav*_*ett 3 performance sql-server execution-plan query-performance

我一直在研究供应商 SQL 查询的性能问题,通常在这个供应商那里我可以看到可以提高性能的索引,但是这个查询目前在性能调整方面有点超出我的范围。希望在这个请求之后我会学到一些新东西。

SQL 的问题似乎是合并连接(内部连接),我不确定尝试和提高这些性能的最佳方法。

我真的很感激有人指出我正确的方向,或者如果我可以提供更多信息,请告诉我。

这是执行计划SQL 查询

由于我尝试应用索引,因此该索引并不多,但它们似乎没有减少合并连接的效果。因此,我给您的计划是使用供应商的默认索引。

更新 1:我添加了索引来查询,但它们几乎没有影响,这里是索引和新计划

新的 SQL 计划索引

我还更新了查询中涉及的所有表的统计信息。

使用选项(散列连接)将运行时间从 30 秒减少到 15 秒。但是,这是从屏幕后面运行的供应商 SQL,因此我无法强制执行此操作。

服务器上的 MAXDOP 设置为 0,成本阈值为 50,所以不确定为什么它不想并行。

更新 2:似乎在表 atstsehourdetmap 上具有以下唯一索引会导致问题。如果我删除这些,那么查询会在不到一秒的时间内运行。

创建唯一索引 [aiatstsehourdetmap2] ON [dbo].[atstsehourdetmap] ( [oid] ) ;

创建唯一索引 [aiatstsehourdetmap1] ON [dbo].[atstsehourdetmap] ( [tsehourdet_id] ) ;

任何想法为什么?

Pau*_*ite 8

SQL 的问题似乎是合并连接...

优化器的估计是 Merge Join 将占运行查询的成本的 85.8%,但您应该始终以合理的怀疑程度对待这些数字。

它们只是基于相当简单的成本模型的估计,该模型可帮助优化器在备选方案之间做出一般选择,但这些数字几乎可以肯定与特定硬件上执行计划的性能特征无关。成本估算是优化器认为成本高昂的广泛暗示,但没有替代技术和经验丰富的数据库专业人员的分析。

也就是说,在这种情况下优化器对合并连接的高成本估计的主要原因是连接被假定为多对多。这是因为数据库模式不提供任何约束或唯一索引来保证一对多关系。假设的多对多连接也是连接输出基数估计值高得离谱的原因。如果信息不完整,优化器会产生不准确的估计。

该问题没有为所涉及的表提供模式 DDL,但我们可以从执行计划中推断出一些列,以及从连接中推断出的关系。大部分数据类型是未知的,当然我们无法知道外键或其他约束。

在我写这个答案时对问题的更新指出查询不能更改,但为了分析起见,我已经将其重写为至少使用现代JOIN语法:

SELECT
    a.wf_state, 
    b.oid, 
    p.wf_user_id, 
    p.error_no, 
    item_followup = 1,
    p.element_type, 
    selected = 0,
    p.version_no, 
    p.p_description, 
    p.s_description, 
    p.proc_node_id, 
    p.step_node_id, 
    p.distr_type, 
    f.project, 
    d.reg_period, 
    d.resource_id, 
    d.last_update, 
    a.used_hrs 
FROM dbo.awfenquiry AS p
JOIN dbo.atstsehourdetmap AS b
    ON b.oid = p.oid
JOIN dbo.atstsehourdet AS a
    ON a.tsehourdet_id = b.tsehourdet_id
JOIN dbo.atstsegldetail AS c
    ON c.tsegldetail_id = a.tsegldetail_id
JOIN dbo.atstseheader AS d
    ON d.tseheader_id = c.tseheader_id
JOIN dbo.atschargecode AS e
    ON e.client = c.client
    AND e.chargecode_id = c.chargecode_id
JOIN dbo.atsproject AS f
    ON f.project = e.project
    AND f.client = e.client
WHERE
    p.element_type = N'TS' 
    AND a.client = N'GB' 
    AND 
    (
        a.wf_state = N'U' 
        OR p.distr_type = N'U' 
    );
Run Code Online (Sandbox Code Playgroud)

您可以进行的主要改进之一是为每个表提供一个聚集索引。几乎所有表都受益于聚集索引,无论是出于空间管理的原因,还是仅仅因为它提供了额外的“免费”索引。在许多情况下,聚集索引也将是主键,尽管这不是必需的。所有的表都应该有一个键。许多人会争辩说,一张桌子没有一张桌子是不正确的。作为起点,我根据我的最佳猜测为每个表指定了一个主键。默认情况下,主键是集群的。

将所有这些放在一起,模式的第一遍是:

CREATE TABLE dbo.awfenquiry -- p
(
    oid integer PRIMARY KEY,
    element_type nvarchar(50) NULL,
    wf_user_id integer NULL,
    error_no integer NULL,
    version_no integer NULL,
    p_description nvarchar(50) NULL,
    s_description nvarchar(50) NULL,
    proc_node_id integer NULL,
    step_node_id integer NULL,
    distr_type nvarchar(10) NULL
);

CREATE TABLE dbo.atstsehourdetmap -- b
(
    oid integer NOT NULL,
    tsehourdet_id integer NOT NULL,

    PRIMARY KEY (oid, tsehourdet_id)
);

CREATE TABLE dbo.atstsehourdet -- a
(
    tsehourdet_id integer PRIMARY KEY,
    tsegldetail_id integer NULL,
    wf_state nvarchar(10) NULL,
    used_hrs integer NULL,
    client nvarchar(10) NULL
);

CREATE TABLE dbo.atstsegldetail -- c
(
    tsegldetail_id integer PRIMARY KEY,
    tseheader_id integer NULL,
    client nvarchar(10) NULL,
    chargecode_id integer NULL
);

CREATE TABLE dbo.atstseheader -- d
(
    tseheader_id integer PRIMARY KEY,
    reg_period integer NULL, 
    resource_id integer NULL, 
    last_update datetime2 NULL, 
);

CREATE TABLE dbo.atschargecode -- e
(
    chargecode_id integer NOT NULL,
    client nvarchar(10) NOT NULL,
    project integer NULL,

    PRIMARY KEY (chargecode_id, client)
);

CREATE TABLE dbo.atsproject -- f
(
    project integer NOT NULL,
    client nvarchar(10) NOT NULL,

    PRIMARY KEY (project, client)
);
Run Code Online (Sandbox Code Playgroud)

也没有提供统计信息,但我们至少可以看到执行计划中的整体表基数。以下语句为上面的表定义提供了此信息,但没有关于值分布的任何信息:

UPDATE STATISTICS dbo.awfenquiry WITH ROWCOUNT = 51826;
UPDATE STATISTICS dbo.atstsehourdetmap WITH ROWCOUNT = 2748620;
UPDATE STATISTICS dbo.atstsehourdet WITH ROWCOUNT = 2743040;
UPDATE STATISTICS dbo.atstsegldetail WITH ROWCOUNT = 1223270;
UPDATE STATISTICS dbo.atstseheader WITH ROWCOUNT = 328822;
UPDATE STATISTICS dbo.atschargecode WITH ROWCOUNT = 23983;
UPDATE STATISTICS dbo.atsproject WITH ROWCOUNT = 18169;
Run Code Online (Sandbox Code Playgroud)

根据查询,除了已经添加的键之外,几乎没有明显的索引候选。可能有用的两个是:

CREATE NONCLUSTERED INDEX i 
ON dbo.awfenquiry (element_type);

CREATE NONCLUSTERED INDEX i 
ON dbo.atstsehourdet (client) 
INCLUDE (wf_state, tsegldetail_id, used_hrs);
Run Code Online (Sandbox Code Playgroud)

除了(现在是一对多)合并连接之外,由此产生的执行计划显然与原始执行计划相比并没有很大的改进,但它可能是朝着正确方向迈出的一步:

在此处输入图片说明

所有的表都相对较小,所以我接下来可能会检查查询执行是否被另一个进程阻塞,是否与其他查询争夺资源,或者是否在不堪重负的存储系统中挣扎。对于这项任务来说,15 秒似乎完全不合理。