SQL ServerでOracleのStatspackみたいな事をやる

要は、揮発性の動的管理ビュー(関数)(DMV)

    • SYS.DM_EXEC_QUERY_STATS
    • SYS.DM_EXEC_SQL_TEXT
    • SYS.DM_EXEC_QUERY_PLAN

のスナップショットをため込んでいこうというお話。

SNAPSHOTテーブル定義

CREATE DATABASE
	PERFSTAT
;

SELECT
	GETDATE() SNAP_DATE,
	T1.*,
	T2.*,
	T3.QUERY_PLAN
INTO
	PERFSTAT.DBO.SNAP_QUERY_PLAN
FROM
	SYS.DM_EXEC_QUERY_STATS T1
	CROSS APPLY
	SYS.DM_EXEC_SQL_TEXT(PLAN_HANDLE) T2
	CROSS APPLY
	SYS.DM_EXEC_QUERY_PLAN(PLAN_HANDLE) T3
WHERE
	1 = 2
;

ALTER TABLE
	PERFSTAT.DBO.SNAP_QUERY_PLAN
ADD CONSTRAINT
	PK_SNAP_QUERY_PLAN
PRIMARY KEY
(
	PLAN_HANDLE,
	STATEMENT_START_OFFSET
)
;

WKテーブル定義

DMVの仕様がMSDN見てもよくわからないところがあるのでとりあえず...

SELECT
	T1.*,
	T2.*,
	T3.QUERY_PLAN
INTO
	PERFSTAT.DBO.SNAP_QUERY_PLAN_WK
FROM
	SYS.DM_EXEC_QUERY_STATS T1
	CROSS APPLY
	SYS.DM_EXEC_SQL_TEXT(PLAN_HANDLE) T2
	CROSS APPLY
	SYS.DM_EXEC_QUERY_PLAN(PLAN_HANDLE) T3
WHERE
	1 = 2
;

ALTER TABLE
	PERFSTAT.DBO.SNAP_QUERY_PLAN_WK
ADD CONSTRAINT
	PK_SNAP_QUERY_PLAN_WK
PRIMARY KEY
(
	PLAN_HANDLE,
	STATEMENT_START_OFFSET
)
;

SNAPSHOTを追加するSQL

MERGE INTO
	PERFSTAT.DBO.SNAP_QUERY_PLAN T1
USING
	(
	SELECT
		*
	FROM
		PERFSTAT.DBO.SNAP_QUERY_PLAN_WK T1
	WHERE
		-- 新しくキャッシュされたステートメントか、最後にSNAPSHOTしてから実行されたステートメント
		NOT EXISTS
		(
		SELECT
			NULL
		FROM
			PERFSTAT.DBO.SNAP_QUERY_PLAN T2
		WHERE
			T1.PLAN_HANDLE = T2.PLAN_HANDLE
			AND
			T1.STATEMENT_START_OFFSET = T2.STATEMENT_START_OFFSET
			AND
			T1.LAST_EXECUTION_TIME = T2.LAST_EXECUTION_TIME
		)
	) T2
ON
	T1.PLAN_HANDLE = T2.PLAN_HANDLE
	AND
	T1.STATEMENT_START_OFFSET = T2.STATEMENT_START_OFFSET
WHEN MATCHED THEN
	UPDATE SET
		T1.[SNAP_DATE] = GETDATE(),
		T1.[SQL_HANDLE] = T2.[SQL_HANDLE],
		T1.[STATEMENT_START_OFFSET] = T2.[STATEMENT_START_OFFSET],
		T1.[STATEMENT_END_OFFSET] = T2.[STATEMENT_END_OFFSET],
		T1.[PLAN_GENERATION_NUM] = T2.[PLAN_GENERATION_NUM],
		T1.[PLAN_HANDLE] = T2.[PLAN_HANDLE],
		T1.[CREATION_TIME] = T2.[CREATION_TIME],
		T1.[LAST_EXECUTION_TIME] = T2.[LAST_EXECUTION_TIME],
		T1.[EXECUTION_COUNT] = T2.[EXECUTION_COUNT],
		T1.[TOTAL_WORKER_TIME] = T2.[TOTAL_WORKER_TIME],
		T1.[LAST_WORKER_TIME] = T2.[LAST_WORKER_TIME],
		T1.[MIN_WORKER_TIME] = T2.[MIN_WORKER_TIME],
		T1.[MAX_WORKER_TIME] = T2.[MAX_WORKER_TIME],
		T1.[TOTAL_PHYSICAL_READS] = T2.[TOTAL_PHYSICAL_READS],
		T1.[LAST_PHYSICAL_READS] = T2.[LAST_PHYSICAL_READS],
		T1.[MIN_PHYSICAL_READS] = T2.[MIN_PHYSICAL_READS],
		T1.[MAX_PHYSICAL_READS] = T2.[MAX_PHYSICAL_READS],
		T1.[TOTAL_LOGICAL_WRITES] = T2.[TOTAL_LOGICAL_WRITES],
		T1.[LAST_LOGICAL_WRITES] = T2.[LAST_LOGICAL_WRITES],
		T1.[MIN_LOGICAL_WRITES] = T2.[MIN_LOGICAL_WRITES],
		T1.[MAX_LOGICAL_WRITES] = T2.[MAX_LOGICAL_WRITES],
		T1.[TOTAL_LOGICAL_READS] = T2.[TOTAL_LOGICAL_READS],
		T1.[LAST_LOGICAL_READS] = T2.[LAST_LOGICAL_READS],
		T1.[MIN_LOGICAL_READS] = T2.[MIN_LOGICAL_READS],
		T1.[MAX_LOGICAL_READS] = T2.[MAX_LOGICAL_READS],
		T1.[TOTAL_CLR_TIME] = T2.[TOTAL_CLR_TIME],
		T1.[LAST_CLR_TIME] = T2.[LAST_CLR_TIME],
		T1.[MIN_CLR_TIME] = T2.[MIN_CLR_TIME],
		T1.[MAX_CLR_TIME] = T2.[MAX_CLR_TIME],
		T1.[TOTAL_ELAPSED_TIME] = T2.[TOTAL_ELAPSED_TIME],
		T1.[LAST_ELAPSED_TIME] = T2.[LAST_ELAPSED_TIME],
		T1.[MIN_ELAPSED_TIME] = T2.[MIN_ELAPSED_TIME],
		T1.[MAX_ELAPSED_TIME] = T2.[MAX_ELAPSED_TIME],
		T1.[QUERY_HASH] = T2.[QUERY_HASH],
		T1.[QUERY_PLAN_HASH] = T2.[QUERY_PLAN_HASH],
		T1.[DBID] = T2.[DBID],
		T1.[OBJECTID] = T2.[OBJECTID],
		T1.[NUMBER] = T2.[NUMBER],
		T1.[ENCRYPTED] = T2.[ENCRYPTED],
		T1.[TEXT] = T2.[TEXT],
		T1.[QUERY_PLAN] = T2.[QUERY_PLAN]
WHEN NOT MATCHED THEN
	INSERT VALUES (
		GETDATE(),
		T2.[SQL_HANDLE],
		T2.[STATEMENT_START_OFFSET],
		T2.[STATEMENT_END_OFFSET],
		T2.[PLAN_GENERATION_NUM],
		T2.[PLAN_HANDLE],
		T2.[CREATION_TIME],
		T2.[LAST_EXECUTION_TIME],
		T2.[EXECUTION_COUNT],
		T2.[TOTAL_WORKER_TIME],
		T2.[LAST_WORKER_TIME],
		T2.[MIN_WORKER_TIME],
		T2.[MAX_WORKER_TIME],
		T2.[TOTAL_PHYSICAL_READS],
		T2.[LAST_PHYSICAL_READS],
		T2.[MIN_PHYSICAL_READS],
		T2.[MAX_PHYSICAL_READS],
		T2.[TOTAL_LOGICAL_WRITES],
		T2.[LAST_LOGICAL_WRITES],
		T2.[MIN_LOGICAL_WRITES],
		T2.[MAX_LOGICAL_WRITES],
		T2.[TOTAL_LOGICAL_READS],
		T2.[LAST_LOGICAL_READS],
		T2.[MIN_LOGICAL_READS],
		T2.[MAX_LOGICAL_READS],
		T2.[TOTAL_CLR_TIME],
		T2.[LAST_CLR_TIME],
		T2.[MIN_CLR_TIME],
		T2.[MAX_CLR_TIME],
		T2.[TOTAL_ELAPSED_TIME],
		T2.[LAST_ELAPSED_TIME],
		T2.[MIN_ELAPSED_TIME],
		T2.[MAX_ELAPSED_TIME],
		T2.[QUERY_HASH],
		T2.[QUERY_PLAN_HASH],
		T2.[DBID],
		T2.[OBJECTID],
		T2.[NUMBER],
		T2.[ENCRYPTED],
		T2.[TEXT],
		T2.[QUERY_PLAN]
	)
;

確認用。

SELECT
	SNAP_DATE,
	PLAN_HANDLE,
	LAST_EXECUTION_TIME,
	EXECUTION_COUNT,
	TEXT
FROM
	PERFSTAT.DBO.SNAP_QUERY_PLAN
ORDER BY
	SNAP_DATE DESC,
	PLAN_HANDLE,
	STATEMENT_START_OFFSET
;


XML型のQUERY_PLAN列って、Management Studioから照会すると実行プランをすぐに可視化してくれて便利すぎる。

Work! Enjoy it!