运行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 Addresses
替换JOIN 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.Entities
加入Vendors
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并希望最好,但我更愿意找到更明确的解决方案。
任何建议表示赞赏。
更新2/27/18
将从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
使用聚集索引扫描替换聚集索引搜索以检索a.Fax
和哈希匹配以将此值连接到结果。
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为查询增加了这么多时间。
好的,就像往常一样,没有一个问题,而是两个问题。这是复杂问题分析可能导致错误结论的一个例子。
最主要的问题是递归CTE og
,它返回一个数据透视表,给出组织之间的父/子关系。但是,对执行计划的分析似乎表明问题是优化器中的某种故障与从左连接表返回的数据量有关。这可能完全是由于我无法正确分析执行计划,但在这些情况下SQL Server 2012 SP4如何创建执行计划似乎存在一些问题。
虽然在我们的生产服务器上更为重要,但SQL Server优化递归CTE的问题在我的localhost(运行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性能都得到了改进和一致。现在已经消除了生产服务器上的问题。