Skip to content

Commit 4758620

Browse files
authored
Merge pull request #553 from Microsoft/pmasl
Added Query Store demo
2 parents 5ee198f + 68a5d6f commit 4758620

8 files changed

Lines changed: 272 additions & 0 deletions
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
USE [AdventureWorks2016_EXT]
2+
GO
3+
4+
/* (1) Do cardinality analysis when suspect on ad-hoc workloads*/
5+
SELECT COUNT(*) AS CountQueryTextRows FROM sys.query_store_query_text;
6+
SELECT COUNT(*) AS CountQueryRows FROM sys.query_store_query;
7+
SELECT COUNT(DISTINCT query_hash) AS CountDifferentQueryRows FROM sys.query_store_query;
8+
SELECT COUNT(*) AS CountPlanRows FROM sys.query_store_plan;
9+
SELECT COUNT(DISTINCT query_plan_hash) AS CountDifferentPlanRows FROM sys.query_store_plan;
10+
11+
/* (2) Get Compile Vs Execution times: ad-hoc workloads tend to spend lot of time in compilation*/
12+
EXEC sp_GetCompilAndExecutionTotalTime
13+
14+
/* (3) See query pattern*/
15+
SELECT TOP 10 * FROM sys.query_store_query_text
16+
17+
18+
/* (4) I'm not getting new queries?
19+
Look at Query Store parameters - is Query Store in READ_ONLY mode?
20+
*/
21+
SELECT current_storage_size_mb, max_storage_size_mb, desired_state, desired_state_desc, actual_state, actual_state_desc, readonly_reason, flush_interval_seconds,
22+
interval_length_minutes, stale_query_threshold_days, max_plans_per_query, query_capture_mode, query_capture_mode_desc, size_based_cleanup_mode,
23+
size_based_cleanup_mode_desc, actual_state_additional_info
24+
FROM sys.database_query_store_options
25+
26+
ALTER DATABASE [AdventureWorks2016_EXT] SET QUERY_STORE CLEAR;
27+
ALTER DATABASE [AdventureWorks2016_EXT] SET QUERY_STORE = ON (OPERATION_MODE = READ_WRITE);
28+
GO
29+
30+
/* (5) How do we fix the Auto-Param problem?*/
31+
32+
/* At the query level: apply the plan guide for selected query template */
33+
DECLARE @stmt nvarchar(max);
34+
DECLARE @params nvarchar(max);
35+
EXEC sp_get_query_template
36+
N'select * from part p join partdetails pp on p.partid = pp.partid where p.partid = 46911',
37+
@stmt OUTPUT,
38+
@params OUTPUT;
39+
40+
EXEC sp_create_plan_guide
41+
N'TemplateGuide1',
42+
@stmt,
43+
N'TEMPLATE',
44+
NULL,
45+
@params,
46+
N'OPTION(PARAMETERIZATION FORCED)';
47+
48+
/*(6) Alternative (at the database level): force parametrization for all queries*/
49+
ALTER DATABASE [AdventureWorks2016_EXT] SET PARAMETERIZATION FORCED;
50+
51+
/* Run analysis query (1), (2) again to see results of parametrization */
52+
53+
/*(7) Reset the DB state*/
54+
ALTER DATABASE [AdventureWorks2016_EXT] SET PARAMETERIZATION SIMPLE;
55+
GO
56+
EXEC sp_control_plan_guide N'DROP', N'TemplateGuide1';
57+
GO
58+
ALTER DATABASE [AdventureWorks2016_EXT] SET QUERY_STORE CLEAR;
59+
ALTER DATABASE [AdventureWorks2016_EXT] SET QUERY_STORE = ON (OPERATION_MODE = READ_WRITE);
60+
SELECT * FROM sys.database_query_store_options
61+
GO
670 Bytes
Binary file not shown.
390 KB
Loading
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
**Query Store demo**
2+
3+
This demo shows capabilities of Query Store. Usually we demo 3-4 scenarios:
4+
1. How to turn on and initially configure Query Store
5+
2. How Query Store collects & exposed data
6+
3. Detecting and fixing query with plan choice regression
7+
4. Detecting and fixing workload that is candidate for auto-parametrization
8+
9+
**Prerequisites**
10+
11+
Restore AdventureWorks2016_EXT database from the provided BAK at https://github.com/Microsoft/sql-server-samples/releases/tag/adventureworks.
12+
13+
Turn ON and Configure Query Store
14+
15+
After restoring the database, go to Properties / Query Store tab.
16+
17+
![Query Store in SSMS](../QS_SSMS.png)
18+
19+
Use docs content to walk through main config settings: https://docs.microsoft.com//sql/relational-databases/performance/best-practice-with-the-query-store#Configure
20+
21+
**How Query Store Works**
22+
Open ShowBasics.sql script and execute queries individually:
23+
- Run simple `SELECT * FROM` Part
24+
- Show where query ends in sys.query_store_query_text, sys.query_store_query, sys.query_store_plan, sys.query_store_runtime_stats
25+
- Use custom view `vw_QueryStoreCompileInfo` to get info more easily. The main point here is: people can write their own scripts combining Query Store views
26+
- Execute the same user query from the proc, using sp_executesql, trigger and show that containing object defines query identity in QDS (each instance of the same query text becomes separate query that can be monitored and tuned independently)
27+
- Show what happens with query that gets auto-parametrized. It cannot be searched using the original query text because QDS stores query as parametrized. Hopefully, sys.fn_stmt_sql_handle_from_sql_stmt can be used to track down query using original query text
28+
- Run `vw_QueryStoreRuntimeInfo` (again custom view) to show main runtime stats combined with query/plan info
29+
30+
**Query with plan regression**
31+
1. Run QueryStoreSimpleDemo.exe with option R or option S
32+
2. Open SSMS, analyze and explain - two execution plans that SQL Server use alternately (switches between 2 plan almost randomly). This is known as Parameter Sniffing problem - plan gets generated based on parameter available at the compilation time. When compilation happens frequently and randomly and data is skewed (not all parameter values are uniformly distributed PSP is likely to occur and degradations are common)
33+
3. Force better plan, explain what happens (SSMS)
34+
4. Summarize benefits for DBA – fixing performance quickly without knowing details about the query. Fully transparent to running apps
35+
Detect and fix ad hoc workload that is candidate for parametrization
36+
1. Run QueryStoreSimpleDemo.exe with option P and let it work for some time (15-20 sec)
37+
2. Open “Auto-Param Analysis.sql” and run queries from groups (1) and (2). What we see is:
38+
a. Large number of queries / plan entries, small number of different query/plan hashes indicates queries that are not parametrized although they are good candidates
39+
b. Relatively big compile time shows that system wastes resources on compilation instead of execution
40+
3. Open SSMS Top Resource Consuming queries – if you increase number of presented queries to 50 you’ll see that majority of queries has similar /negligible consumption – there’s nothing user can optimize/tune. This is what we call “death by a thousand of cuts”
41+
4. Run (3) query to see query text pattern – it becomes obvious that queries differ only by provided literal value
42+
5. If you run (1) you’ll notice that numbers do not change although workload is running. (4) gives us the answer – Query Store went to READ_ONLY due to large number of queries / plans. This is another point you should make: ad-hoc queries are not bad for SQL Server & execution but also for Query Store as it goes to RO mode which means we do not operate with latest facts!
43+
6. Run (5) to parametrize query and clear Query Store. Workload is still running!
44+
7. Run (1) again to see numbers now: ration between count(queries) and count(distinct query_hash) is now near to 1.
45+
8. Open Open SSMS Top Resource Consuming queries: you’ll see dozen of different queries to tune
46+
9. (6) show alternative solution – applying forced parametrization for the entire DB. Just mention, as a possible solution
47+
10. Run (7) to reset DB to initial state.
48+
49+
50+
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*Clear Query Store and procedure cache*/
2+
ALTER DATABASE AdventureWorks2016_EXT SET QUERY_STORE CLEAR;
3+
ALTER DATABASE AdventureWorks2016_EXT SET QUERY_STORE = ON (QUERY_CAPTURE_MODE = ALL);
4+
DBCC FREEPROCCACHE
5+
GO
6+
USE AdventureWorks2016_EXT;
7+
GO
8+
9+
/*Run simple query - what data is collected and where does it go to?*/
10+
SELECT * FROM Part;
11+
12+
SELECT * FROM sys.query_store_query_text;
13+
SELECT * FROM sys.query_store_query;
14+
SELECT * FROM sys.query_store_plan;
15+
SELECT * FROM sys.query_store_runtime_stats;
16+
17+
/*
18+
Combine all info
19+
vw_QueryStoreCompileInfo is custom view (created for presentation)
20+
21+
*/
22+
SELECT * FROM vw_QueryStoreCompileInfo
23+
WHERE query_sql_text = 'SELECT * FROM Part'
24+
25+
/*The same query from the proc*/
26+
DROP PROCEDURE IF EXISTS sp_GetParts
27+
GO
28+
29+
CREATE PROCEDURE sp_GetParts
30+
AS
31+
SELECT * FROM Part;
32+
GO
33+
34+
EXEC sp_GetParts;
35+
36+
/*Again the same query, from sp_executesql*/
37+
EXEC sp_executesql N'SELECT * FROM Part'
38+
39+
SELECT * FROM vw_QueryStoreCompileInfo
40+
WHERE query_sql_text = 'SELECT * FROM Part'
41+
42+
/*Finally trigger*/
43+
DROP TRIGGER IF EXISTS dbo.OnPartInsert
44+
GO
45+
46+
CREATE TRIGGER dbo.OnPartInsert
47+
ON dbo.Part
48+
AFTER INSERT
49+
AS
50+
BEGIN
51+
-- SET NOCOUNT ON added to prevent extra result sets from
52+
-- interfering with SELECT statements.
53+
SET NOCOUNT ON;
54+
55+
SELECT * FROM Part;
56+
57+
END
58+
GO
59+
60+
INSERT INTO Part VALUES (3000020, 'Part_300020');
61+
62+
SELECT * FROM vw_QueryStoreCompileInfo
63+
WHERE query_sql_text = 'SELECT * FROM Part'
64+
65+
/*What happens with parametrized query?*/
66+
SELECT * FROM Part WHERE PartId = 5;
67+
68+
SELECT * FROM vw_QueryStoreCompileInfo
69+
WHERE query_sql_text = 'SELECT * FROM Part = 5'
70+
71+
/* Check sys.query_store_query_text */
72+
73+
SELECT * FROM sys.query_store_query_text;
74+
75+
/*Try sys.fn_stmt_sql_handle_from_sql_stmt this instead*/
76+
SELECT * FROM sys.fn_stmt_sql_handle_from_sql_stmt
77+
('SELECT * FROM Part WHERE PartId = 5', NULL)
78+
79+
/*Changed searched criteria*/
80+
SELECT V.* FROM vw_QueryStoreCompileInfo V
81+
JOIN sys.fn_stmt_sql_handle_from_sql_stmt
82+
('SELECT * FROM Part WHERE PartId = 5', NULL) F
83+
ON V.statement_sql_handle = F.statement_sql_handle
84+
85+
/*Get runtime info for the queries*/
86+
SELECT * FROM vw_QueryStoreRuntimeInfo
87+
WHERE query_sql_text = 'SELECT * FROM Part'
88+
ORDER BY start_time DESC
89+
90+
SELECT * FROM vw_QueryStoreRuntimeInfo V
91+
JOIN sys.fn_stmt_sql_handle_from_sql_stmt
92+
('SELECT * FROM Part WHERE PartId = 5', NULL) F
93+
ON V.statement_sql_handle = F.statement_sql_handle
94+
ORDER BY start_time DESC
95+
96+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
USE [AdventureWorks2016_EXT]
2+
GO
3+
4+
DROP PROCEDURE IF EXISTS sp_GetCompilAndExecutionTotalTime
5+
GO
6+
7+
CREATE PROCEDURE sp_GetCompilAndExecutionTotalTime
8+
AS
9+
10+
DECLARE @totalCompiles int
11+
DECLARE @totalExecutions int
12+
DECLARE @totalCompileTime decimal(18,4)
13+
DECLARE @totalExecutionTime decimal(18,4)
14+
15+
SELECT @totalCompiles = SUM(count_compiles),
16+
@totalCompileTime = SUM(count_compiles * avg_compile_duration / 1000.)
17+
FROM sys.query_store_plan;
18+
19+
SELECT @totalExecutions = SUM(count_executions),
20+
@totalExecutionTime = SUM(count_executions * avg_duration / 1000.)
21+
FROM sys.query_store_runtime_stats
22+
23+
SELECT @totalCompiles AS TotalCompiles, @totalExecutions AS TotalExecutions,
24+
@totalCompileTime AS TotalCompileTime, @totalExecutionTime AS TotalDurationTime
25+
GO
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
USE [AdventureWorks2016_EXT]
2+
GO
3+
4+
DROP VIEW IF EXISTS [vw_QueryStoreCompileInfo];
5+
GO
6+
7+
CREATE VIEW [dbo].[vw_QueryStoreCompileInfo]
8+
AS
9+
SELECT qt.query_text_id, q.query_id, p.plan_id, qt.query_sql_text, s.name AS ContainingSchema, o.name AS ContainingObject, q.query_hash, qt.statement_sql_handle, q.is_internal_query,
10+
q.query_parameterization_type_desc, q.count_compiles AS query_count_compiles, p.query_plan_hash, p.count_compiles AS plan_count_compiles, p.last_compile_start_time, p.engine_version,
11+
p.compatibility_level, p.query_plan, p.is_trivial_plan, p.is_parallel_plan, p.is_forced_plan
12+
FROM sys.query_store_query_text AS qt INNER JOIN
13+
sys.query_store_query AS q ON qt.query_text_id = q.query_text_id INNER JOIN
14+
sys.query_store_plan AS p ON q.query_id = p.query_id LEFT OUTER JOIN
15+
sys.objects AS o ON q.object_id = o.object_id LEFT OUTER JOIN
16+
sys.schemas AS s ON s.schema_id = o.schema_id
17+
GO
18+
19+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
USE [AdventureWorks2016_EXT]
2+
GO
3+
4+
DROP VIEW IF EXISTS [dbo].[vw_QueryStoreRuntimeInfo]
5+
GO
6+
7+
CREATE VIEW [dbo].[vw_QueryStoreRuntimeInfo]
8+
AS
9+
SELECT qt.query_text_id, q.query_id, p.plan_id, qt.query_sql_text, s.name AS ContainingSchema, o.name AS ContainingObject, qt.statement_sql_handle, rsi.start_time, rsi.end_time, rs.execution_type_desc,
10+
rs.count_executions, rs.avg_duration, rs.max_duration, rs.avg_cpu_time, rs.max_cpu_time, rs.avg_logical_io_reads, rs.max_logical_io_reads, rs.avg_physical_io_reads, rs.max_physical_io_reads,
11+
rs.avg_logical_io_writes, rs.max_logical_io_writes, rs.avg_query_max_used_memory, rs.max_query_max_used_memory, rs.avg_rowcount, rs.max_rowcount, rs.avg_dop, rs.max_dop
12+
FROM sys.query_store_query_text AS qt INNER JOIN
13+
sys.query_store_query AS q ON qt.query_text_id = q.query_text_id INNER JOIN
14+
sys.query_store_plan AS p ON q.query_id = p.query_id LEFT OUTER JOIN
15+
sys.objects AS o ON q.object_id = o.object_id LEFT OUTER JOIN
16+
sys.schemas AS s ON s.schema_id = o.schema_id INNER JOIN
17+
sys.query_store_runtime_stats AS rs ON rs.plan_id = p.plan_id INNER JOIN
18+
sys.query_store_runtime_stats_interval AS rsi ON rsi.runtime_stats_interval_id = rs.runtime_stats_interval_id
19+
GO
20+
21+

0 commit comments

Comments
 (0)