在 SQL Server 上使用 3 个表连接时性能非常差

Posted

技术标签:

【中文标题】在 SQL Server 上使用 3 个表连接时性能非常差【英文标题】:Very bad performance using 3 tables join on SQL Server 【发布时间】:2017-06-25 15:04:57 【问题描述】:

当我执行涉及以下 3 个表的 SQL 语句时,我遇到了严重的性能问题:

表A表C

特别是,这些表在数据仓库中,中间的表是维度表,而其他表是事实表。 TableA 大约有 900 万条记录,而 TableC 大约有 300 万条记录。维度表(TableB)只有74条记录。

查询的语法很简单,可以看到,其中TableA叫_PG,TableB等于_MDT,Table C叫_FM:

SELECT _MDT.codiceMandato as Customer, SUM(_FM.Totale) AS Revenue,
      SUM(_PG.ErogatoTotale) AS Paid
FROM _PG INNER JOIN
     _MDT 
     ON _PG.idMandato = _MDT.idMandato INNER JOIN
     _FM
     ON _FM.idMandato = _MDT.idMandato
GROUP BY _MDT.codiceMandato

实际上,我从未见过这个查询的结尾 :-( _PG 在 idMandato 和同一个 _FM 表上有一个非聚集索引 _MDT 表在 idMandato 上有一个聚集索引

执行计划如下

正如您所见,瓶颈是由于 Stream Aggregate(成本的 33%)和 Merge Join(成本的 66%)造成的。特别是,流聚合强调了大约 4000 亿的估计行! 我不知道原因,也不知道如何解决这个糟糕的问题。 我使用 SQL Server 2016 SP1 安装的虚拟服务器,Windows Server 2012 Standard 具有 4 个 Cpu 核心和 32 GB 的 RAM,1,5TB 在由具有 SSD 缓存的 SAS 磁盘组成的专用卷上。 希望有人能帮我理解。

提前致谢

【问题讨论】:

编辑您的问题并提供示例数据和所需的结果。目前尚不清楚您的表格到底是什么样子。 尝试将TotaleErogatoTotale 添加为非聚集索引的包含列。 估计可能是正确的 :-) 在两个事实表中,idMandato 可能非常不唯一,而您实际上执行了 M:N-join。根据您的实际问题,您可以在连接之前使用 CTE/派生表对每个事实表求和。 表1:N----1:1-----1:N之间的关系遵循星型模式。 【参考方案1】:

最可能的原因是因为您得到的是二维的笛卡尔积。这不必要地增加了行。解决方法是在做join之前进行聚合。

您还没有提供示例数据,但想法是这样的:

SELECT m.codiceMandato as Customer, f.revenue, p.Paid
FROM _MDT m INNER JOIN
     (SELECT p.idMandato, SUM(p.ErogatoTotale) AS Paid
      FROM _PG p
      GROUP BY p.idMandato
     ) p
     ON p.idMandato = m.idMandato INNER JOIN
     (SELECT f.idMandato, SUM(f.Totale) AS Revenue
      FROM _FM f
      GROUP BY f.idMandato
     ) f
     ON f.idMandato = m.idMandato;

我不能 100% 确定这会解决问题,因为您的数据结构不清楚。

【讨论】:

不需要最终的 GROUP BY。 @dnoeth 。 . .谢谢。【参考方案2】:

您可以尝试在不聚合的情况下在 TableA 和 TableC 之间进行子查询,然后将此子查询与 TableB 连接并应用 GROUP BY:

SELECT _MDT.codiceMandato, SUM(A.Totale) AS Revenue, sum( A.ErogatoTotale) 
AS Paid
FROM ( SELECT m.idMandato, _FM.Totale, _PG.ErogatoTotale  FROM _PG 
INNER JOIN _FM  
    ON _FM.idMandato = _MDT.idMandato ) A
    INNER JOIN _MDT       ON A.idMandato = _MDT.idMandato 
GROUP BY _MDT.codiceMandato

【讨论】:

以上是关于在 SQL Server 上使用 3 个表连接时性能非常差的主要内容,如果未能解决你的问题,请参考以下文章

sql server 语句如何将3个表合并成一个表?

视图中四个表的 SQL Server 条件连接

SQL Server 使用 2 个表(外键?) ASP.NET

如何将两个表与 SQL Server 中第二个表中引用同一列的两列连接起来

sql删除使用内连接超过3个表

sql server 中union的用法