0

运行 Windows Server 2012、Hyper-V、SQL Server 2012 主动/被动故障转移集群,带两个 8 处理器、60GB 节点、单实例、300 个数据库。此查询产生不一致的结果,运行时间在 10 到 30 秒之间。

DECLARE @OrgID           BigInt = 780246
DECLARE @ActiveOnly      Bit = 0
DECLARE @RestrictToOrgID Bit = 0;

WITH og (OrgID, GroupID) AS
  (
  SELECT ID, ID FROM Common.com.Organizations WHERE ISNULL(ParentID, 0) <> ID 
  UNION ALL
  SELECT o.ID, og.GroupID FROM Common.com.Organizations o JOIN og ON og.OrgID = o.ParentID
  )

   SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
          v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide, 
          a.*
     FROM og
     JOIN books.Organizations           bo ON  bo.CommonID = og.OrgID
     JOIN books.Organizations           po ON  po.CommonID = og.GroupID
     JOIN books.Entities                e  ON   e.OrgID    = po.ID
     JOIN Vendors                       v  ON   v.ID       =  e.ID 
                                           AND (e.OrgID    = bo.ID OR v.DistrictWide = 1)
LEFT JOIN Addresses                     a  ON   a.ID       =  e.AddressID
    WHERE bo.ID = @OrgID
      AND (@ActiveOnly      = 0 OR e.Active = 1)
      AND (@RestrictToOrgID = 0 OR e.OrgID  = @OrgID)
 ORDER BY e.EntityName

替换LEFT JOIN AddressesJOIN Addresses

     JOIN Addresses                     a  ON   a.ID       =  e.AddressID
    WHERE bo.ID = @OrgID
      AND (@ActiveOnly      = 0 OR e.Active = 1)
      AND (@RestrictToOrgID = 0 OR e.OrgID  = @OrgID)
 ORDER BY e.EntityName

或将所选列的长度减少Addresses到小于 100 字节

   SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
          v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide, 
          a.Fax

将执行时间减少到大约 0.5 秒。

此外,使用SELECT DISTINCT和加入books.EntitiesVendors

   SELECT DISTINCT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
          v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide, 
          a.*
     FROM og
     JOIN books.Organizations           bo ON  bo.CommonID = og.OrgID
     JOIN books.Organizations           po ON  po.CommonID = og.GroupID
     JOIN Vendors                       v  
     JOIN books.Entities                e  ON   v.ID       =  e.ID 
                                           ON   e.OrgID    = bo.ID OR (e.OrgID = po.ID AND v.DistrictWide = 1)

将时间减少到大约 0.75 秒。

概括

这些情况表明 SQL Server 实例中存在某种资源限制,导致这些不稳定的结果,我不知道如何诊断它。如果我将有问题的数据库复制到运行 SQL Server 2012 的笔记本电脑上,则不会出现问题。我可以继续更改 SQL 并希望获得最好的结果,但我更愿意找到更明确的解决方案。

任何建议表示赞赏。

2018 年 2 月 27 日更新

未修改查询的执行计划将针对地址表的聚集索引查找显示为问题。 聚集索引查找地址表

将选择的列的长度减少Addresses到小于 100 字节

   SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
          v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide, 
          a.Fax

将 Clustered Index Seek 替换为 Clustered Index Scan 以检索a.Fax和 Hash Match 以将此值加入结果。

哈希匹配

Addresses主键创建如下:

   ALTER TABLE dbo.Addresses 
ADD CONSTRAINT PK_Addresses PRIMARY KEY CLUSTERED (ID ASC)
          WITH (PAD_INDEX = OFF, 
                STATISTICS_NORECOMPUTE = OFF, 
                SORT_IN_TEMPDB         = OFF, 
                IGNORE_DUP_KEY         = OFF, 
                ONLINE                 = OFF, 
                ALLOW_ROW_LOCKS        = ON, 
                ALLOW_PAGE_LOCKS       = ON) 
            ON  PRIMARY

该索引每天都会根据需要进行碎片整理和优化。

到目前为止,我找不到任何有用的信息来解释为什么 Clustered Index Seek 会为查询增加这么多时间。

4

1 回答 1

1

好吧,通常情况下,不是一个问题,而是两个问题。这是一个复杂的问题分析可能导致错误结论的例子。

主要问题原来是递归 CTE og,它返回一个数据透视表,给出组织之间的父/子关系。但是,对执行计划的分析似乎表明问题是优化器中的某种故障,与从左连接表返回的数据量有关。这可能完全是我无法正确分析执行计划的结果,但在这些情况下 SQL Server 2012 SP4 如何创建执行计划似乎确实存在一些问题。

虽然在我们的生产服务器上更为重要,但 SQL Server 对递归 CTE 的优化问题在我的运行 2012 SP4 的本地主机和运行 SP2 的登台服务器上都很明显。但是需要进一步的分析和一些猜测才能看到它。

解决方案

我用数据透视表替换了递归 CTE,并向 Organizations 表添加了一个触发器来维护它。

USE Common
GO

CREATE VIEW com.OrganizationGroupsCTE
AS
  WITH cte (OrgID, GroupID) AS
    (
    SELECT ID, ID FROM com.Organizations WHERE ISNULL(ParentID, 0) <> ID 
    UNION ALL
    SELECT o.ID, cte.GroupID FROM com.Organizations o JOIN cte ON cte.OrgID = o.ParentID
    )

  SELECT OrgID, GroupID FROM cte
GO

CREATE TABLE com.OrganizationGroups 
  (
  OrgID   BIGINT, 
  GroupID BIGINT
  )

INSERT com.OrganizationGroups 
SELECT OrgID, GroupID 
  FROM com.OrganizationGroupsCTE
GO

CREATE TRIGGER TR_OrganizationGroups ON com.Organizations AFTER INSERT,UPDATE,DELETE
AS
  DELETE og 
    FROM com.OrganizationGroups og 
    JOIN deleted                d   ON d.ID IN (og.groupID, og.orgID);

  INSERT com.OrganizationGroups 
  SELECT orgID, groupID 
    FROM inserted               i 
    JOIN OrganizationGroupsCTE  cte ON i.ID IN (cte.orgID, cte.groupID)
GO

修改查询以使用数据透视表后,

   SELECT e.*, v.Type AS VendorType, v.F1099, v.F1099Type, v.TaxID, v.TaxPercent,
          v.ContactName, v.ContactPhone, v.ContactEMail, v.DistrictWide, 
          a.*
     FROM Common.com.OrganizationGroups og
     JOIN books.Organizations           bo ON  bo.CommonID = og.OrgID
     JOIN books.Organizations           po ON  po.CommonID = og.GroupID
     JOIN books.Entities                e  ON   e.OrgID    = po.ID
     JOIN Vendors                       v  ON   v.ID       =  e.ID 
                                           AND (e.OrgID    = bo.ID OR v.DistrictWide = 1)
LEFT JOIN Addresses                     a  ON   a.ID       =  e.AddressID
    WHERE bo.ID = @OrgID
      AND (@ActiveOnly      = 0 OR e.Active = 1)
      AND (@RestrictToOrgID = 0 OR e.OrgID  = @OrgID)
 ORDER BY e.EntityName

SQL Server 的性能在所有三个环境中都得到了改进并且保持一致。生产服务器上的问题现已消除。

SQL Server 性能

于 2019-03-07T19:08:40.270 回答