-- <<관련 Command >>

-- Query Store 설정 확인

SELECT actual_state, actual_state_desc, readonly_reason, current_storage_size_mb, max_storage_size_mb

FROM sys.database_query_store_options;

-- 실행 계획 확인

SELECT Txt.query_text_id, Txt.query_sql_text, Pl.plan_id, Qry.*

FROM sys.query_store_plan AS Pl JOIN sys.query_store_query AS Qry ON Pl.query_id = Qry.query_id

JOIN sys.query_store_query_text AS Txt ON Qry.query_text_id = Txt.query_text_id ;

--  Query Store 데이터의 메모리 내 부분을 디스크로 플러시합니다.

EXEC sp_query_store_flush_db;

-- 쿼리를 삭제하고 쿼리 저장소에서 관련된 모든 계획과 런타임 통계를 제거합니다. ( Clears the runtime stats for a specific query plan from the query store )

EXEC sp_query_store_remove_query 3;

-- 쿼리 저장소에서 실행계획에 대한 실행 통계 삭제  ( Clears the runtime stats for a specific query plan from the query store )

EXEC sp_query_store_reset_exec_stats 3;

-- 쿼리 저장소에서 단일 계획을 제거합니다.

EXEC sp_query_store_remove_plan 3;

-- 특정 쿼리에 대한 특정 계획을 강제 실행합니다.

-- sp_query_store_force_plan 을 사용하면 쿼리 저장소에서 기록한 계획 만 해당 쿼리의 계획으로 강제 실행할 수 있습니다.

-- 즉, 쿼리에 사용할 수있는 유일한 계획은 쿼리 저장소가 활성화되어있는 동안 해당 쿼리를 실행하는 데 이미 사용 된 계획뿐입니다.

-- QUERY_ID , PLAN_ID 순서

EXEC sp_query_store_force_plan 3, 3;

-- 특정 쿼리에 대해 특정 PLAN 설정 해제 하기  ( Enables unforcing a particular plan for a particular query )

-- QUERY_ID , PLAN_ID 순서

EXEC sp_query_store_unforce_plan 3, 3;

-- 실행 계획 확인

SELECT Txt.query_text_id, Txt.query_sql_text, Pl.plan_id, Qry.*

FROM sys.query_store_plan AS Pl JOIN sys.query_store_query AS Qry ON Pl.query_id = Qry.query_id

JOIN sys.query_store_query_text AS Txt ON Qry.query_text_id = Txt.query_text_id ;

ALTER DATABASE TGTEST SET QUERY_STORE (INTERVAL_LENGTH_MINUTES = 30);

ALTER DATABASE TGTEST SET QUERY_STORE (MAX_STORAGE_SIZE_MB = 200);

ALTER DATABASE TGTEST SET QUERY_STORE

( OPERATION_MODE = READ_WRITE, CLEANUP_POLICY = (STALE_QUERY_THRESHOLD_DAYS = 30)

, DATA_FLUSH_INTERVAL_SECONDS = 300

, MAX_STORAGE_SIZE_MB = 500

, INTERVAL_LENGTH_MINUTES = 15

, SIZE_BASED_CLEANUP_MODE = AUTO

, QUERY_CAPTURE_MODE = AUTO

, MAX_PLANS_PER_QUERY = 1000

, WAIT_STATS_CAPTURE_MODE = ON );

SELECT * FROM sys.database_query_store_options;

-- 수행 횟수가 2보다 작고, 마지막 수행이 24전 보다 오랜 쿼리를 QUERY STORE 에서 삭제하기

DECLARE @id int

DECLARE adhoc_queries_cursor

  CURSOR FOR SELECT q.query_id

  FROM sys.query_store_query_text AS qt JOIN sys.query_store_query AS q

  ON q.query_text_id = qt.query_text_id JOIN sys.query_store_plan AS p

  ON p.query_id = q.query_id JOIN sys.query_store_runtime_stats AS rs

  ON rs.plan_id = p.plan_id

  GROUP BY q.query_id

  HAVING SUM(rs.count_executions) < 2  -- 수행 횟수가 2보다 작고

  AND MAX(rs.last_execution_time) < DATEADD (hour, -24, GETUTCDATE())  -- 마지막 수행 시간이 24간 이전

  ORDER BY q.query_id ;

OPEN adhoc_queries_cursor ;

FETCH NEXT FROM adhoc_queries_cursor INTO @id;

WHILE @@fetch_status = 0

BEGIN PRINT @id EXEC sp_query_store_remove_query @id

FETCH NEXT FROM adhoc_queries_cursor INTO @id

END

CLOSE adhoc_queries_cursor ;

DEALLOCATE adhoc_queries_cursor;

-- 가장 최근까지 수행 쿼리

SELECT TOP 10 qt.query_sql_text, q.query_id, qt.query_text_id, p.plan_id, rs.last_execution_time

FROM sys.query_store_query_text AS qt JOIN sys.query_store_query AS q

ON qt.query_text_id = q.query_text_id JOIN sys.query_store_plan AS p

ON q.query_id = p.query_id JOIN sys.query_store_runtime_stats AS rs

ON p.plan_id = rs.plan_id

ORDER BY rs.last_execution_time DESC;

-- 자주 수행되는 쿼리

SELECT q.query_id, qt.query_text_id, qt.query_sql_text, SUM(rs.count_executions) AS total_execution_count

FROM sys.query_store_query_text AS qt JOIN sys.query_store_query AS q

ON qt.query_text_id = q.query_text_id JOIN sys.query_store_plan AS p

ON q.query_id = p.query_id JOIN sys.query_store_runtime_stats AS rs

ON p.plan_id = rs.plan_id

GROUP BY q.query_id, qt.query_text_id, qt.query_sql_text

ORDER BY total_execution_count DESC;

-- 최근 1시간 동안 평균 수행 시간이 가장 오래 걸린 쿼리

SELECT TOP 10 rs.avg_duration, qt.query_sql_text, q.query_id, qt.query_text_id, p.plan_id

      , GETUTCDATE() AS CurrentUTCTime, rs.last_execution_time

FROM sys.query_store_query_text AS qt JOIN sys.query_store_query AS q

ON qt.query_text_id = q.query_text_id JOIN sys.query_store_plan AS p

ON q.query_id = p.query_id JOIN sys.query_store_runtime_stats AS rs

ON p.plan_id = rs.plan_id

WHERE rs.last_execution_time > DATEADD(hour, -1, GETUTCDATE())

ORDER BY rs.avg_duration DESC;

-- 최근 24시간 내에 IO가 가장 높은 쿼리 10

SELECT TOP 10 rs.avg_physical_io_reads, qt.query_sql_text, q.query_id, qt.query_text_id, p.plan_id

  , rs.runtime_stats_id, rsi.start_time, rsi.end_time, rs.avg_rowcount, rs.count_executions

FROM sys.query_store_query_text AS qt JOIN sys.query_store_query AS q

ON qt.query_text_id = q.query_text_id JOIN sys.query_store_plan AS p

ON q.query_id = p.query_id JOIN sys.query_store_runtime_stats AS rs

ON p.plan_id = rs.plan_id JOIN sys.query_store_runtime_stats_interval AS rsi

ON rsi.runtime_stats_interval_id = rs.runtime_stats_interval_id

WHERE rsi.start_time >= DATEADD(hour, -24, GETUTCDATE())

ORDER BY rs.avg_physical_io_reads DESC;

-- 실행계획이 여러개인 쿼리

WITH Query_MultPlans AS

( SELECT COUNT(*) AS cnt, q.query_id

  FROM sys.query_store_query_text AS qt JOIN sys.query_store_query AS q

  ON qt.query_text_id = q.query_text_id JOIN sys.query_store_plan AS p

  ON p.query_id = q.query_id

  GROUP BY q.query_id

  HAVING COUNT(distinct plan_id) > 1 )

SELECT q.query_id, object_name(object_id) AS ContainingObject, query_sql_text, plan_id

  , p.query_plan AS plan_xml, p.last_compile_start_time, p.last_execution_time

FROM Query_MultPlans AS qm JOIN sys.query_store_query AS q

ON qm.query_id = q.query_id JOIN sys.query_store_plan AS p

ON q.query_id = p.query_id JOIN sys.query_store_query_text qt

ON qt.query_text_id = q.query_text_id

ORDER BY query_id, plan_id;

-- 실행계획 변경으로 성능 저하 쿼리 (  수행이 48 이내,  실행계획이 변경되며, 평균 수행 시간이 더 오래 걸린 쿼리 )

SELECT qt.query_sql_text, q.query_id, qt.query_text_id, rs1.runtime_stats_id AS runtime_stats_id_1

    ,rsi1.start_time AS interval_1, p1.plan_id AS plan_1, rs1.avg_duration AS avg_duration_1

    ,rs2.avg_duration AS avg_duration_2, p2.plan_id AS plan_2, rsi2.start_time AS interval_2

    , rs2.runtime_stats_id AS runtime_stats_id_2

FROM sys.query_store_query_text AS qt JOIN sys.query_store_query AS q

ON qt.query_text_id = q.query_text_id JOIN sys.query_store_plan AS p1

ON q.query_id = p1.query_id JOIN sys.query_store_runtime_stats AS rs1

ON p1.plan_id = rs1.plan_id JOIN sys.query_store_runtime_stats_interval AS rsi1

ON rsi1.runtime_stats_interval_id = rs1.runtime_stats_interval_id JOIN sys.query_store_plan AS p2

ON q.query_id = p2.query_id JOIN sys.query_store_runtime_stats AS rs2

ON p2.plan_id = rs2.plan_id JOIN sys.query_store_runtime_stats_interval AS rsi2

ON rsi2.runtime_stats_interval_id = rs2.runtime_stats_interval_id

WHERE rsi1.start_time > DATEADD(hour, -48, GETUTCDATE())

AND rsi2.start_time > rsi1.start_time

AND p1.plan_id      <> p2.plan_id

AND rs2.avg_duration > 2*rs1.avg_duration

ORDER BY q.query_id, rsi1.start_time, rsi2.start_time;

-- 가장 오래 대기한 쿼리

SELECT TOP 10 qt.query_text_id, q.query_id, p.plan_id, sum(total_query_wait_time_ms) AS sum_total_wait_ms

FROM sys.query_store_wait_stats ws JOIN sys.query_store_plan p

ON ws.plan_id = p.plan_id JOIN sys.query_store_query q

ON p.query_id = q.query_id JOIN sys.query_store_query_text qt

ON q.query_text_id = qt.query_text_id

GROUP BY qt.query_text_id, q.query_id, p.plan_id

ORDER BY sum_total_wait_ms DESC

--- 최근 1시간, 24시간 쿼리 성능 비교

--- "Recent" workload - last 1 hour

DECLARE @recent_start_time datetimeoffset;

DECLARE @recent_end_time datetimeoffset;

SET @recent_start_time = DATEADD(hour, -1, SYSUTCDATETIME());  -- 1시간전

SET @recent_end_time = SYSUTCDATETIME();

--- "History" workload

DECLARE @history_start_time datetimeoffset;

DECLARE @history_end_time datetimeoffset;

SET @history_start_time = DATEADD(hour, -24, SYSUTCDATETIME()); -- 24시간전

SET @history_end_time = SYSUTCDATETIME();

WITH hist AS

( SELECT p.query_id query_id, CONVERT(float, SUM(rs.avg_duration*rs.count_executions)) total_duration

    , SUM(rs.count_executions) count_executions

    , COUNT(distinct p.plan_id) num_plans

    FROM sys.query_store_runtime_stats AS rs JOIN sys.query_store_plan p

    ON p.plan_id = rs.plan_id

    WHERE (rs.first_execution_time >= @history_start_time AND rs.last_execution_time < @history_end_time)

      OR (rs.first_execution_time <= @history_start_time AND rs.last_execution_time > @history_start_time)

      OR (rs.first_execution_time <= @history_end_time AND rs.last_execution_time > @history_end_time)

    GROUP BY p.query_id )

, recent AS

  ( SELECT p.query_id query_id, CONVERT(float, SUM(rs.avg_duration*rs.count_executions)) total_duration

  , SUM(rs.count_executions) count_executions, COUNT(distinct p.plan_id) num_plans

  FROM sys.query_store_runtime_stats AS rs JOIN sys.query_store_plan p

  ON p.plan_id = rs.plan_id

  WHERE (rs.first_execution_time >= @recent_start_time AND rs.last_execution_time < @recent_end_time)

  OR (rs.first_execution_time <= @recent_start_time AND rs.last_execution_time > @recent_start_time)

  OR (rs.first_execution_time <= @recent_end_time AND rs.last_execution_time > @recent_end_time)

  GROUP BY p.query_id )

SELECT results.query_id query_id, results.query_text query_text

, results.additional_duration_workload additional_duration_workload

, results.total_duration_recent total_duration_recent

, results.total_duration_hist total_duration_hist

, ISNULL(results.count_executions_recent, 0) count_executions_recent

, ISNULL(results.count_executions_hist, 0) count_executions_hist

FROM ( SELECT hist.query_id query_id, qt.query_sql_text query_text

    , ROUND(CONVERT(float, recent.total_duration/ recent.count_executions-hist.total_duration/hist.count_executions) *(recent.count_executions), 2) AS additional_duration_workload, ROUND(recent.total_duration, 2) total_duration_recent

    , ROUND(hist.total_duration, 2) total_duration_hist, recent.count_executions count_executions_recent, hist.count_executions count_executions_hist

    FROM hist JOIN recent ON hist.query_id = recent.query_id JOIN sys.query_store_query AS q

    ON q.query_id = hist.query_id JOIN sys.query_store_query_text AS qt

    ON q.query_text_id = qt.query_text_id ) AS results

    WHERE additional_duration_workload > 0

    ORDER BY additional_duration_workload DESC OPTION (MERGE JOIN);

 

'Database > SQL Server' 카테고리의 다른 글

TEMPDB DBFILE 삭제  (0) 2020.08.29
index  (0) 2020.08.29
동적쿼리  (0) 2020.08.29
VLF 개수에 따른 성능 차이  (0) 2020.08.29
In-Memory Demo  (0) 2020.08.28

+ Recent posts