【问题标题】:Poor performance with stacked joins堆叠连接性能不佳
【发布时间】:2017-07-14 10:31:47
【问题描述】:

我不确定我能否提供足够详细的答案,但我的公司在使用较旧的 mssql 视图时遇到了性能问题。我已将其范围缩小到正确的外部连接,但我不熟悉每个连接都没有“ON”的连接之后的连接结构,如下面的代码 sn-p 所示。

我如何编写下面的连接以提高性能或使用 Join Tablename on Field1 = field2 格式的更简单格式?

  FROM    dbo.tblObject AS tblObject_2
            JOIN dbo.tblProspectB2B PB ON PB.Object_ID = tblObject_2.Object_ID
            RIGHT OUTER JOIN dbo.tblProspectB2B_CoordinatorStatus
            RIGHT OUTER JOIN dbo.tblObject
            INNER JOIN dbo.vwDomain_Hierarchy
            INNER JOIN dbo.tblContactUser
            INNER JOIN dbo.tblProcessingFile WITH ( NOLOCK )
            LEFT OUTER JOIN dbo.enumRetentionRealization AS RR ON RR.RetentionRealizationID = dbo.tblProcessingFile.RetentionLeadTypeID
            INNER JOIN dbo.tblLoan
            INNER JOIN dbo.tblObject AS tblObject_1 WITH ( NOLOCK ) ON dbo.tblLoan.Object_ID = tblObject_1.Object_ID ON dbo.tblProcessingFile.Loan_ID = dbo.tblLoan.Object_ID ON dbo.tblContactUser.Object_ID = dbo.tblLoan.ContactOwnerID ON dbo.vwDomain_Hierarchy.Object_ID = tblObject_1.Domain_ID ON dbo.tblObject.Object_ID = dbo.tblLoan.ContactOwnerID ON   dbo.tblProspectB2B_CoordinatorStatus.Object_ID =   dbo.tblLoan.ReferralSourceContactID ON tblObject_2.Object_ID =       dbo.tblLoan.ReferralSourceContactID

【问题讨论】:

  • A RIGHT OUTER JOINLEFT OUTER JOIN 需要一个 ON 子句,就像常规的 JOIN 一样(或者被连接的表之间的关系必须存在于 WHERE 子句中,这被认为是形式不好)。此外,外部联接的工作方式与内部联接不同,因此如果您不确定它们是如何工作的,最好先了解它们,然后再切换到直接的JOININNER JOIN 的简写)。我怀疑在您的 WHERE 子句中,您可以找到正在连接的表之间的关系,此处省略了 ON
  • 在视图中,您确实有两个选择,将查询重写为使用子查询连接而不是连接到整个表,或者确保正确的索引到位(索引视图或基础表)。跨度>
  • 此视图上没有 Where 子句。以前的 DBA 在许多视图(和存储过程)上广泛使用了这种堆叠连接系统。我以前从未见过它。我总是看到每个“JOIN”都有一个“ON”,所以我有点难过。我很确定转向派生查询是要走的路,但我没有得到这个堆叠连接的东西。
  • @stevenackley,因为这个可怕的查询调用了另一个视图(这本身就是一个非常糟糕的做法),所以这个视图不可能是可索引的(至少在 SQL Server 中它不会)
  • @HLGEM 视图本身不可索引,但基础表是

标签: sql


【解决方案1】:

您最后的INNER JOIN 有许多ON 语句。根据this 问答,这样的语法相当于嵌套子查询。

【讨论】:

  • 只是连接语法的一种变体(100% 标准 SQL):FROM a JOIN b JOIN c ON b.id = c.id ON a.id = c.idFROM b JOIN c ON b.id = c.id JOIN a ON a.id = c.id 完全相同,连接在逻辑上按照 ON 的顺序执行。
【解决方案2】:

这是我见过的最糟糕的查询之一。由于我无法弄清楚如果没有基础数据它应该如何工作,这就是我向你建议的。

首先找到一个好的贷款样本,并针对这个视图编写一个查询以返回 where loan_id = ... 现在您有了一个数据集,您可以检查您的更改,这比可能返回的数百万条记录更容易。确保这些结果有意义(正确连接到 tbl_objects 让我感到困扰,因为返回所有对象记录没有意义)

现在开始使用您认为应该是第一个表的内容编写查询(我建议贷款是第一个表,如果不是,那么第一个表是左连接到贷款的对象))和贷款的 where 子句ID。 检查您的结果,您是否获得了与添加 where 子句的视图查询相同的贷款信息?

然后一次添加一个连接,看看它对查询的影响以及结果是否似乎偏离了轨道。一旦您找到了一个查询,该查询在所有添加的表中都给出了相同的结果,那么您可以尝试检查其他几个贷款 ID。一旦这些已经签出,然后运行不带 where 子句的整个查询并检查视图结果(如果它是一个很大的数字,您可能只需要查看记录计数是否匹配并目视检查(在这两件事上使用 order by为了确保您的结果顺序相同)。在此过程中,请尝试仅使用左连接,而不是右连接和左连接的组合(可以单独使用内部连接)。 我在复杂查询中养成了先做所有内连接然后左连接的习惯。我从不在生产代码中使用右连接。 现在您可以进行性能调优了。

我想对对象的右连接会导致一个问题,因为它返回整个表,并且该表名的性质以及与同一个表的其他连接使我相信他可能想要一个左连接。在不知道数据的含义的情况下,很难确定。因此,首先,如果您为一个贷款 ID 返回太多记录,那么请考虑真正的问题是否是随着表的增长,返回太多记录已成为问题。

还要考虑到您通常可以查看视图并将其替换为代码以获得相同的结果。视图调用视图是一种糟糕的技术,通常会导致性能问题。通常,其他视图之上的视图调用相同的表,因此您最终会在不需要时多次加入它们。

根据您拥有的数据库后端检查您的解释计划或执行计划。对此的分析应该显示您可能缺少索引的位置。

还要确保查询中的每个表都是必需的。当您加入视图时尤其如此。该视图可以连接到其他 12 个表,但您只需要其中一个表的数据,它可以连接到您的一个表。确保您没有使用 select * 而是只返回视图实际需要的字段。您有内部联接,因此,根据定义, select * 将返回您不需要的字段。

如果您选择的视图部分有一个独特的部分,那么请考虑是否可以通过更改为派生表或添加 where 子句来清除您获得的多个记录。要查看导致倍数的原因,您可能需要暂时使用 select * 来查看所有列,并找出哪一个不是唯一的并且导致了问题。

整个过程不会轻松或有趣。慢慢来,仔细而有条不紊地工作,您最终会得到一个可理解和可维护的查询。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-02-27
    • 2014-06-20
    • 2017-06-23
    • 2016-01-06
    • 2018-10-19
    • 1970-01-01
    • 2013-03-12
    • 1970-01-01
    相关资源
    最近更新 更多