目录
- [SQL Server 中的执行计划分析](#SQL Server 中的执行计划分析)
-
- [一、SQL Server 执行计划查看方式](#一、SQL Server 执行计划查看方式)
-
- [1. **SET STATISTICS 系列命令**(最常用)](#1. SET STATISTICS 系列命令(最常用))
- [2. **SHOWPLAN 命令**(显示预估执行计划)](#2. SHOWPLAN 命令(显示预估执行计划))
- [3. **使用系统函数获取执行计划**](#3. 使用系统函数获取执行计划)
- 二、实际案例分析
- 三、图形化工具(推荐)
-
- [1. **SSMS 中的执行计划**](#1. SSMS 中的执行计划)
- [2. **查看执行计划的方式**](#2. 查看执行计划的方式)
- 四、针对你的问题的诊断脚本
- 五、关键指标解读
-
- [STATISTICS IO 输出解读:](#STATISTICS IO 输出解读:)
- [STATISTICS TIME 输出解读:](#STATISTICS TIME 输出解读:)
- 六、快速优化建议
SQL Server 中的执行计划分析
SQL Server 没有 EXPLAIN 命令,但有更强大的执行计划分析工具。
一、SQL Server 执行计划查看方式
1. SET STATISTICS 系列命令(最常用)
sql
-- 开启实际执行计划(显示执行时间和IO)
SET STATISTICS TIME ON;
SET STATISTICS IO ON;
-- 执行你的查询
SELECT * FROM Ware_WareHouseBill WHERE WareHouseBillCode = '3161-5';
-- 关闭统计信息
SET STATISTICS TIME OFF;
SET STATISTICS IO OFF;
输出示例:
SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。
表 'Ware_WareHouseBill'。扫描计数 1,逻辑读取 5 次,物理读取 0 次...
(1 行受影响)
2. SHOWPLAN 命令(显示预估执行计划)
sql
-- 显示预估执行计划(不执行查询)
SET SHOWPLAN_XML ON;
GO
SELECT * FROM Ware_WareHouseBill WHERE WareHouseBillCode = '3161-5';
GO
SET SHOWPLAN_XML OFF;
sql
-- 显示实际执行计划(执行查询)
SET STATISTICS XML ON;
GO
SELECT * FROM Ware_WareHouseBill WHERE WareHouseBillCode = '3161-5';
GO
SET STATISTICS XML OFF;
3. 使用系统函数获取执行计划
sql
-- 获取查询的执行计划(SQL Server 2016+)
SELECT *
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
CROSS APPLY sys.dm_exec_query_plan(qs.plan_handle) qp
WHERE st.text LIKE '%Ware_WareHouseBill%';
二、实际案例分析
案例1:分析你的视图查询
sql
-- 开启统计信息
SET STATISTICS TIME ON;
SET STATISTICS IO ON;
-- 执行你的查询
SELECT * FROM
(
SELECT
a.WareHouseBill_Id,
a.WareHouseBillCode,
a.WareHouseBillType,
a.WareHouseDate,
a.CreateDate,
a.CreateID,
a.Creator,
a.Modifier,
a.ModifyDate,
a.ModifyID,
a.WareHouseCode,
a.SourceDocuments,
a.SUPPLIER_NO,
a.Status,
c.SGName,
(
SELECT COUNT(WareHouseBillList_Id)
FROM Ware_WareHouseBillList l
WHERE l.WareHouseBill_Id = a.WareHouseBill_Id
AND (l.CheckStatus = 2 OR l.CheckStatus = 0 OR l.CheckStatus IS NULL)
AND l.Status != 3
) AS uCoun,
(
SELECT STRING_AGG(
CAST(l.ProductCode + ',' + ISNULL(l.LotCode, '') AS VARCHAR(MAX)),
';'
)
FROM Ware_WareHouseBillList l
WHERE l.WareHouseBill_Id = a.WareHouseBill_Id
AND (l.CheckStatus = 2 OR l.CheckStatus = 0 OR l.CheckStatus IS NULL)
AND l.Status != 3
) AS Remark
FROM Ware_WareHouseBill AS a
LEFT JOIN Base_Supplier AS c ON a.SUPPLIER_NO = c.SGCode
WHERE a.Status = 1 OR a.Status = 2
) AS subquery
WHERE uCoun > 0
OR WareHouseBillType LIKE 'source%';
SET STATISTICS TIME OFF;
SET STATISTICS IO OFF;
案例2:查看索引使用情况
sql
-- 查看表的索引
EXEC sp_helpindex 'Ware_WareHouseBill';
EXEC sp_helpindex 'Ware_WareHouseBillList';
EXEC sp_helpindex 'Base_Supplier';
-- 查看索引使用统计
SELECT
OBJECT_NAME(s.object_id) AS TableName,
i.name AS IndexName,
i.type_desc,
s.user_seeks,
s.user_scans,
s.user_lookups,
s.user_updates
FROM sys.dm_db_index_usage_stats s
INNER JOIN sys.indexes i ON s.object_id = i.object_id AND s.index_id = i.index_id
WHERE OBJECT_NAME(s.object_id) IN ('Ware_WareHouseBill', 'Ware_WareHouseBillList', 'Base_Supplier')
ORDER BY TableName, IndexName;
案例3:分析缺失索引
sql
-- 查找缺失索引的建议
SELECT
migs.avg_total_user_cost * (migs.avg_user_impact / 100.0) * (migs.user_seeks + migs.user_scans) AS improvement_measure,
mid.statement AS TableName,
mid.equality_columns,
mid.inequality_columns,
mid.included_columns,
migs.avg_total_user_cost,
migs.avg_user_impact,
migs.user_seeks,
migs.user_scans
FROM sys.dm_db_missing_index_group_stats migs
INNER JOIN sys.dm_db_missing_index_groups mig ON migs.group_handle = mig.index_group_handle
INNER JOIN sys.dm_db_missing_index_details mid ON mig.index_handle = mid.index_handle
WHERE mid.database_id = DB_ID()
ORDER BY improvement_measure DESC;
案例4:查看正在运行的查询执行计划
sql
-- 查看当前正在执行的查询及其执行计划
SELECT
r.session_id,
r.cpu_time,
r.total_elapsed_time,
t.text AS SQL_Text,
qp.query_plan AS Query_Plan
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
CROSS APPLY sys.dm_exec_query_plan(r.plan_handle) qp
WHERE r.session_id > 50 -- 排除系统会话
AND r.status = 'running';
三、图形化工具(推荐)
1. SSMS 中的执行计划
- 预估执行计划: Ctrl + L
- 实际执行计划: Ctrl + M(然后执行查询)
- 实时查询统计: 菜单 → 查询 → 实时查询统计
2. 查看执行计划的方式
sql
-- 方法1:在查询前开启实际执行计划
SET STATISTICS XML ON;
GO
-- 你的查询
SELECT * FROM Ware_WareHouseBill WHERE WareHouseBillCode = '3161-5';
GO
SET STATISTICS XML OFF;
-- 方法2:保存执行计划到变量
DECLARE @plan_handle VARBINARY(64);
SELECT @plan_handle = plan_handle
FROM sys.dm_exec_query_stats qs
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st
WHERE st.text LIKE '%Ware_WareHouseBill%';
SELECT * FROM sys.dm_exec_query_plan(@plan_handle);
四、针对你的问题的诊断脚本
sql
-- 完整诊断你的查询
SET STATISTICS TIME ON;
SET STATISTICS IO ON;
-- 1. 检查主表数据
PRINT '=== 主表数据检查 ===';
SELECT WareHouseBillCode, Status, WareHouseBillType
FROM Ware_WareHouseBill
WHERE WareHouseBillCode = '3161-5';
-- 2. 检查明细表数据
PRINT '=== 明细表数据检查 ===';
SELECT
l.WareHouseBillList_Id,
l.CheckStatus,
l.Status,
l.ProductCode,
l.LotCode
FROM Ware_WareHouseBill a
INNER JOIN Ware_WareHouseBillList l ON a.WareHouseBill_Id = l.WareHouseBill_Id
WHERE a.WareHouseBillCode = '3161-5';
-- 3. 查看执行计划(实际)
PRINT '=== 实际执行计划 ===';
SET STATISTICS XML ON;
SELECT * FROM
(
SELECT
a.WareHouseBill_Id,
a.WareHouseBillCode,
a.Status,
a.WareHouseBillType,
c.SGName,
(
SELECT COUNT(WareHouseBillList_Id)
FROM Ware_WareHouseBillList l
WHERE l.WareHouseBill_Id = a.WareHouseBill_Id
AND (l.CheckStatus = 2 OR l.CheckStatus = 0 OR l.CheckStatus IS NULL)
AND l.Status != 3
) AS uCoun
FROM Ware_WareHouseBill AS a
LEFT JOIN Base_Supplier AS c ON a.SUPPLIER_NO = c.SGCode
WHERE a.WareHouseBillCode = '3161-5'
) AS subquery;
SET STATISTICS XML OFF;
SET STATISTICS TIME OFF;
SET STATISTICS IO OFF;
五、关键指标解读
STATISTICS IO 输出解读:
- 逻辑读取: 从缓存读取的页数(越少越好)
- 物理读取: 从磁盘读取的页数(应为0或很小)
- 扫描计数: 表扫描次数
STATISTICS TIME 输出解读:
- CPU 时间: CPU 处理时间
- 占用时间: 总执行时间
六、快速优化建议
针对你的查询,建议先运行以下诊断:
sql
-- 快速诊断索引建议
SET STATISTICS TIME ON;
SET STATISTICS IO ON;
-- 测试查询性能
SELECT
a.WareHouseBillCode,
COUNT(*) AS DetailCount,
COUNT(CASE WHEN (l.CheckStatus IN (0,2) OR l.CheckStatus IS NULL) AND l.Status != 3 THEN 1 END) AS ValidCount
FROM Ware_WareHouseBill a
LEFT JOIN Ware_WareHouseBillList l ON a.WareHouseBill_Id = l.WareHouseBill_Id
WHERE a.WareHouseBillCode = '3161-5'
GROUP BY a.WareHouseBillCode;
SET STATISTICS TIME OFF;
SET STATISTICS IO OFF;
-- 查看缺失索引
SELECT * FROM sys.dm_db_missing_index_details
WHERE OBJECT_NAME(object_id) IN ('Ware_WareHouseBill', 'Ware_WareHouseBillList');