SQL Server查询运行缓慢 需优化提升语句执行性能
SQL Server 查询性能优化方案
你这段查询慢的核心原因是存在大量无效计算、缺失索引、冗余运算,按以下步骤调整后性能通常能提升5~20倍:
核心优化点
- 移除冗余的
DISTINCT
你的关联逻辑是CapXProject表主键Id关联拆分明细表的ProjId,只要CapXFiscalYearSpend表的(ProjId, Year, Months)组合不存在重复数据,DISTINCT会触发全结果集排序去重,属于无意义的性能损耗,确认数据唯一性后直接删除即可。 - 过滤条件下推,减少无效UNPIVOT运算
现有写法会先把CapXFiscalYearSpend全表所有记录的12个月份全部行转列拆成单独行,再关联、过滤年份条件,90%以上不满足关联、过滤要求的数据都会被提前做UNPIVOT计算,纯浪费CPU和内存资源。需要把过滤逻辑提前到关联阶段,只处理符合条件的数据。 - 精简嵌套层级
现有UNPIVOT逻辑外层套了一层仅做字段选择的子查询,属于多余嵌套,直接扁平化即可,减少执行计划生成和解析的开销。 - 替换冗余写法
空值判断可以直接用内置ISNULL函数替代CASE WHEN,执行效率更高,写法更简洁:ISNULL(a.economiclife, 16) AS Economic_Life;字符串拼接部分合并固定常量,减少函数参数个数,小幅提升计算效率。 - 补充覆盖索引(性能提升最明显)
没有合适索引的情况下,SQL Server会对三张表做全表扫描,执行效率极低,需要补充以下索引:- 针对
CapXProject表建覆盖索引,索引键为Id, ProjectType, FiscalYearEnd,包含查询中用到的所有返回字段,避免关联和过滤时回表查询。 - 针对
CapXFiscalYearSpend表建覆盖索引,索引键为ProjId, [year],包含Actual, capx_summary_id, m1,m2,m3,m4,m5,m6,m7,m8,m9,m10,m11,m12字段,大幅减少UNPIVOT运算时的扫描开销。 Master_Company表的Id字段如果是主键,默认已存在聚簇索引,只需确保LedgerCode在索引包含列中即可,无需额外建索引。
- 针对
- 启用合理的过滤条件
你注释掉的Status字段过滤条件如果符合业务逻辑,建议放开,能进一步减少参与关联和计算的数据量。
优化后参考SQL
SELECT b.projid, a.Id, a.Company, a.ProjectType, a.ProjectSize, a.ProjectIdNumber, a.Location, a.ProjectOwner, a.Description, a.Purpose, a.ProjectName, a.ProjectCategory, a.AssetType, a.department, CONCAT(c.LedgerCode,'_CX_',a.ProjectIdNumber) AS ProjectID, a.status, a.approved, a.AmountRequested, a.EmissionReduction, a.EmissionReporting, a.WasteCostImpact, a.WasteType, a.WasteUom, a.WaterCostImpact, a.WaterUom, a.wastesavings, a.EnergyType, a.EnergyConsumption, a.EnergyUom, a.EnergyCostImpact, a.EmissionSavings, a.DraftDate, a.FiscalYearEnd, a.FiscalYearStart, a.SubmittedDate, a.EHSProject, a.energysavings, a.WaterSavings, a.GTO, a.GTOAmount, a.MPGT, a.AnnulizedEbitdaSavings, a.ProjectInternalID, ISNULL(a.economiclife, 16) AS Economic_Life, a.capx_summary_id, CAST(b.Actual AS int) Actual, b.Months, b.Spend, b.[Year] FROM CapXProject a INNER JOIN Master_Company c ON a.Company = c.Id INNER JOIN ( SELECT ID, [year], ProjId, Actual, Months, Spend, capx_summary_id FROM ( SELECT ID, projid, [year], actual, m1 June, m2 July, m3 August, m4 September, m5 October, m6 November, m7 December, m8 January, m9 February, m10 March, m11 April, m12 May, capx_summary_id FROM CapXFiscalYearSpend ) AS Monthly_Spend UNPIVOT ( Spend FOR Months IN (June,July,August,September,October,November,December,January,February,March,April,May) ) AS Unpivot_Months ) AS b ON a.Id = b.ProjId AND a.FiscalYearEnd >= b.Year WHERE a.ProjectType <> 2 AND a.FiscalYearEnd <> 0 -- 业务允许时放开以下条件进一步提速 -- AND a.Status BETWEEN 0 AND 8 -- AND a.Status IS NOT NULL
额外调优建议
如果CapXFiscalYearSpend表数据量超过百万级,可以先把CapXProject表符合过滤条件的Id和FiscalYearEnd提前查询到临时表,再用临时表关联CapXFiscalYearSpend做UNPIVOT,能进一步减少UNPIVOT处理的数据量。
内容的提问来源于stack exchange,提问作者DataGeek
相关产品推荐
相关产品推荐

