我遇到了一个"问题"我无法弄清楚如何解决。我有一个运行大约15秒的查询,当我然后添加一个连接(在选择中没有使用)时,查询实际上加速了大约4秒,即使你没有选择其他连接。我想SQL Server选择一个更快的不同执行计划 - 但是我如何强制它每次选择最快的查询计划?
此查询大约需要15秒:
SELECT
O.Lvl1_Business_Area_Cd
,O.Lvl1_Business_Area_Nm
,O.Lvl2_Division_Cd
,O.Lvl2_Division_Nm
,SUM(F.Economic_Capital) AS Economic_Capital
FROM
Facts.Financials AS F
LEFT JOIN Dimensions.Customer AS C ON F.Customer_Id = C.Customer_Id
LEFT JOIN Dimensions.Organization AS O ON C.CRU_Id = O.CRU_Id
WHERE
F.Year_Month_Id = 201706
AND Lvl1_Business_Area_Cd = 6008000
GROUP BY
O.Lvl1_Business_Area_Cd
,O.Lvl1_Business_Area_Nm
,O.Lvl2_Division_Cd
,O.Lvl2_Division_Nm
此查询大约需要4秒钟:
SELECT
O.Lvl1_Business_Area_Cd
,O.Lvl1_Business_Area_Nm
,O.Lvl2_Division_Cd
,O.Lvl2_Division_Nm
,SUM(F.Economic_Capital) AS Economic_Capital
FROM
Facts.Financials AS F
LEFT JOIN Dimensions.Customer AS C ON F.Customer_Id = C.Customer_Id
LEFT JOIN Dimensions.Organization AS O ON C.CRU_Id = O.CRU_Id
LEFT JOIN Dimensions.Nace AS N ON C.NACE_Id = N.NACE_Id
WHERE
F.Year_Month_Id = 201706
AND Lvl1_Business_Area_Cd = 6008000
GROUP BY
O.Lvl1_Business_Area_Cd
,O.Lvl1_Business_Area_Nm
,O.Lvl2_Division_Cd
,O.Lvl2_Division_Nm
两个查询之间的唯一区别是LEFT JOIN Dimensions.Nace AS N ON C.NACE_Id = N.NACE_Id
。但是,在select语句中没有使用此表中的任何内容。
Facts.Financials有大约60百万。行,Dimensions.Customer~17 mio。 rows,Dimensions.Organization~25.000,Dimensions.Nace~1000行。
Customer_Id = bigint
CRU_Id = bigint
Nace_id = varchar(4)
我对表格有以下看法:
Facts.Financials: Clustered Index (YearMonth, Customer_Id), Non-Clustered (Customer_Id), Non-clustered columnstore index (Economic_Capital)
Dimensions.Customer: Clustered (Customer_Id, CRU_Id), Non-clustered (CRU_Id), Non-clustered (Nace_Id)
Dimensions.Organization: Clustered (CRU_Id), Non-clustered (Lvl1_Cd, Lvl2_Cd, Lvl3_Cd) Include (Lvl1_Nm, Lvl2_Nm, Lvl3_Nm, Lvl4_Cd, Lvl4_Nm, CRU_Id, CRU_Name)
Dimensions.Nace: Clustered (Nace_Id)
这是慢查询的执行计划(15秒)
缓慢的执行计划XML:Slow execution plan
这是快速执行计划(4秒)
快速执行计划XML:Fast execution plan
有人能指出我错过的正确方向吗?我有错误的索引或者这怎么可能发生?
我正在运行SQL Server 2014
答案 0 :(得分:1)
然而,在select语句中没有使用此表中的任何内容。
但是你在join中使用它。所以sql server优化器会选择不同的计划,这里有一些Paul White解释的连接效果: Joining 100 tables
因此即使你没有在选择中使用它,连接可能会有不同的副作用
它可以添加额外的列(来自连接表)
它可以添加额外的行(连接的表可能不止一次匹配源行)
它可以删除行(连接的表可能没有匹配)
它可以引入NULL(对于RIGHT或FULL JOIN)
因此,如果你的加入没有添加任何上述副作用,那么你可能会像另一个那样得到计划
答案 1 :(得分:0)
我将采取相反的观点。您正在寻找业务优先和各年/月的总计。因为该字段" Lvl1_Business_Area_Cd"来自您的组织表,并且在WHERE子句中,它强制您从LEFT JOIN到INNER JOIN的查询。同样,客户表因此将成为财务所需的内部联系。
现在,我还要确保此字段和CRU_ID的索引,因为它是加入客户的基础......所以
create index Lvl1CruID on Organization ( Lvl1_Business_Area_Cd, CRU_ID )
同样,客户和财务之间的加入速度更快
create index CruID_CustomerID on Customer ( Cru_ID, Customer_ID )
通过这种方式,引擎无需转到原始数据页面即可获得从组织到财务的连接条件,以及客户表中的每条记录。
最后,您的财务表索引年/月和客户应该是好的。我将标准移到了JOIN而不是WHERE。
SELECT
O.Lvl1_Business_Area_Cd,
O.Lvl1_Business_Area_Nm,
O.Lvl2_Division_Cd,
O.Lvl2_Division_Nm,
SUM(F.Economic_Capital) Economic_Capital
FROM
Dimensions.Organization O
JOIN Dimensions.Customer C
ON O.CRU_Id = C.CRU_Id
JOIN Facts.Financials F
ON F.Year_Month_Id = 201706
AND C.Customer_Id = F.Customer_Id
WHERE
O.Lvl1_Business_Area_Cd = 6008000
GROUP BY
O.Lvl1_Business_Area_Cd,
O.Lvl1_Business_Area_Nm,
O.Lvl2_Division_Cd,
O.Lvl2_Division_Nm
拥有良好的索引并更好地了解它们如何工作至关重要,尤其是在多个表之间传递以获取基础细节。
现在,如果您想要所有组织,即使他们没有任何财务活动,您也可以将金融联接更改为LEFT JOIN。
通过尝试预聚合方法,这可能会更好..
SELECT
O.Lvl1_Business_Area_Cd,
O.Lvl1_Business_Area_Nm,
O.Lvl2_Division_Cd,
O.Lvl2_Division_Nm,
PreQuery.Economic_Capital
FROM
Dimensions.Organization O
JOIN Dimensions.Customer C
ON O.CRU_Id = C.CRU_Id
JOIN
( select F.Customer_ID,
SUM(F.Economic_Capital) Economic_Capital
from
Facts.Financials F
where
F.Year_Month_Id = 201706
group by
F.Customer_ID ) PreQuery
AND C.Customer_Id = PreQuery.Customer_Id
WHERE
O.Lvl1_Business_Area_Cd = 6008000
答案 2 :(得分:0)
一两天后,SQL Server已经制定了一个新的查询计划,使得两者几乎同样快。还是不知道为什么。
之后我切换到主表上的聚集columstore索引,为我的查询提供更快的响应时间。