@@ -95,13 +95,13 @@ OPTION (RECOMPILE);
9595SELECT @log_size_mb = AVG (((mf .size * 8 ) / 1024 .))
9696FROM sys .master_files AS mf
9797WHERE mf .database_id = DB_ID (@DatabaseName)
98- AND mf .type_desc = ' LOG'
98+ AND mf .type_desc = ' LOG';
9999
100100/* Grab avg tempdb file size*/
101101SELECT @avg_tempdb_data_file = AVG (((mf .size * 8 ) / 1024 .))
102102FROM sys .master_files AS mf
103103WHERE mf .database_id = DB_ID (' tempdb' )
104- AND mf .type_desc = ' ROWS'
104+ AND mf .type_desc = ' ROWS';
105105
106106
107107/* Help section*/
@@ -173,8 +173,8 @@ IF ( (SELECT SERVERPROPERTY ('EDITION')) = 'SQL Azure' )
173173 SELECT @msg = N ' Sorry, sp_BlitzQueryStore doesn'' t work on Azure Data Warehouse, or Azure Databases with DB compatibility < 130.' + REPLICATE (CHAR (13 ), 7933 );
174174 PRINT @msg;
175175 RETURN ;
176- END
177- END
176+ END ;
177+ END ;
178178ELSE IF ( (SELECT PARSENAME (CONVERT (NVARCHAR (128 ), SERVERPROPERTY (' PRODUCTVERSION' )), 4 ) ) < 13 )
179179 BEGIN
180180 SELECT @msg = N ' Sorry, sp_BlitzQueryStore doesn'' t work on versions of SQL prior to 2016.' + REPLICATE (CHAR (13 ), 7933 );
@@ -200,7 +200,7 @@ IF ( SELECT COUNT(*)
200200RAISERROR (' Checking database validity' , 0 , 1 ) WITH NOWAIT ;
201201
202202IF (@is_azure_db = 1 )
203- SET @DatabaseName = DB_NAME ()
203+ SET @DatabaseName = DB_NAME ();
204204ELSE
205205BEGIN
206206
@@ -251,11 +251,11 @@ END;
251251
252252/* Check database compat level*/
253253
254- RAISERROR (' Checking database compatibility level' , 0 , 1 ) WITH NOWAIT
254+ RAISERROR (' Checking database compatibility level' , 0 , 1 ) WITH NOWAIT ;
255255
256256SELECT @compatibility_level = d .compatibility_level
257257FROM sys .databases AS d
258- WHERE d .name = @DatabaseName
258+ WHERE d .name = @DatabaseName;
259259
260260RAISERROR (' The @DatabaseName you specified ([%s])is running in compatibility level ([%d]).' , 0 , 1 , @DatabaseName, @compatibility_level) WITH NOWAIT ;
261261
@@ -610,6 +610,8 @@ CREATE TABLE #working_warnings
610610 is_big_log BIT ,
611611 is_big_tempdb BIT ,
612612 is_paul_white_electric BIT ,
613+ implicit_conversion_info XML ,
614+ cached_execution_parameters XML ,
613615 warnings NVARCHAR (4000 )
614616 INDEX ww_ix_ids CLUSTERED (plan_id, query_id, query_hash, sql_handle )
615617);
@@ -757,7 +759,22 @@ CREATE TABLE #warning_results
757759 FindingsGroup NVARCHAR (50 ),
758760 Finding NVARCHAR (200 ),
759761 URL NVARCHAR (200 ),
760- Details NVARCHAR (4000 )
762+ Details NVARCHAR (4000 )
763+ );
764+
765+ DROP TABLE IF EXISTS #stored_proc_info;
766+
767+ CREATE TABLE #stored_proc_info
768+ (
769+ sql_handle VARBINARY (64 ),
770+ query_hash BINARY (8 ),
771+ variable_name NVARCHAR (128 ),
772+ variable_datatype NVARCHAR (128 ),
773+ compile_time_value NVARCHAR (128 ),
774+ proc_name NVARCHAR (300 ),
775+ column_name NVARCHAR (128 ),
776+ converted_to NVARCHAR (128 ),
777+ INDEX tf_ix_ids CLUSTERED (sql_handle , query_hash)
761778);
762779
763780/* Sets up WHERE clause that gets used quite a bit*/
@@ -878,8 +895,8 @@ IF (@ExportToExcel = 1 OR @SkipXML = 1)
878895IF @StoredProcName IS NOT NULL
879896 BEGIN
880897
881- DECLARE @sql NVARCHAR (MAX )
882- DECLARE @out INT
898+ DECLARE @sql NVARCHAR (MAX );
899+ DECLARE @out INT ;
883900 DECLARE @proc_params NVARCHAR (MAX ) = N ' @sp_StartDate DATETIME2, @sp_EndDate DATETIME2, @sp_MinimumExecutionCount INT, @sp_MinDuration INT, @sp_StoredProcName NVARCHAR(128), @sp_PlanIdFilter INT, @sp_QueryIdFilter INT, @i_out INT OUTPUT' ;
884901
885902
@@ -904,16 +921,16 @@ IF @StoredProcName IS NOT NULL
904921 BEGIN
905922
906923 SET @msg = N ' We couldn'' t find the Stored Procedure ' + QUOTENAME (@StoredProcName) + N ' in the Query Store views for ' + QUOTENAME (@DatabaseName) + N ' between ' + CONVERT (NVARCHAR (30 ), ISNULL (@StartDate, DATEADD (DAY , - 7 , DATEDIFF (DAY , 0 , SYSDATETIME () ))) ) + N ' and ' + CONVERT (NVARCHAR (30 ), ISNULL (@EndDate, SYSDATETIME ())) +
907- ' . Try removing schema prefixes or adjusting dates. If it was executed from a different database context, try searching there instead.'
924+ ' . Try removing schema prefixes or adjusting dates. If it was executed from a different database context, try searching there instead.';
908925 RAISERROR (@msg, 0 , 1 ) WITH NOWAIT ;
909926
910- SELECT @msg AS [Blue Flowers, Blue Flowers, Blue Flowers]
927+ SELECT @msg AS [Blue Flowers, Blue Flowers, Blue Flowers];
911928
912929 RETURN ;
913930
914- END
931+ END ;
915932
916- END
933+ END ;
917934
918935
919936
@@ -1460,7 +1477,7 @@ SELECT ' + QUOTENAME(@DatabaseName, '''') + N' AS database_name, wp.plan_id, wp.
14601477 ((qsrs.last_query_max_used_memory * 8 ) / 1024.),
14611478 ((qsrs.min_query_max_used_memory * 8 ) / 1024.),
14621479 ((qsrs.max_query_max_used_memory * 8 ) / 1024.),
1463- qsrs.avg_rowcount, qsrs.last_rowcount, qsrs.min_rowcount, qsrs.max_rowcount,'
1480+ qsrs.avg_rowcount, qsrs.last_rowcount, qsrs.min_rowcount, qsrs.max_rowcount,' ;
14641481
14651482 IF @new_columns = 1
14661483 BEGIN
@@ -1474,8 +1491,8 @@ SELECT ' + QUOTENAME(@DatabaseName, '''') + N' AS database_name, wp.plan_id, wp.
14741491 ((qsrs.last_tempdb_space_used * 8 ) / 1024.),
14751492 ((qsrs.min_tempdb_space_used * 8 ) / 1024.),
14761493 ((qsrs.max_tempdb_space_used * 8 ) / 1024.)
1477- '
1478- END
1494+ ' ;
1495+ END ;
14791496 IF @new_columns = 0
14801497 BEGIN
14811498 SET @sql_select + = N'
@@ -1491,8 +1508,8 @@ SELECT ' + QUOTENAME(@DatabaseName, '''') + N' AS database_name, wp.plan_id, wp.
14911508 NULL,
14921509 NULL,
14931510 NULL
1494- '
1495- END
1511+ ' ;
1512+ END ;
14961513SET @sql_select + =
14971514N' FROM #working_plans AS wp
14981515JOIN ' + QUOTENAME (@DatabaseName) + N' .sys.query_store_query AS qsq
@@ -2153,7 +2170,7 @@ SELECT DISTINCT
21532170 c .n .value (' (/p:StmtSimple/@StatementEstRows)[1]' , ' FLOAT' ) AS estimated_rows
21542171FROM #statements AS s
21552172CROSS APPLY s .statement .nodes (' /p:StmtSimple' ) AS c(n)
2156- WHERE c .n .exist(' /p:StmtSimple[@StatementEstRows > 0]' ) = 1
2173+ WHERE c .n .exist(' /p:StmtSimple[@StatementEstRows > 0]' ) = 1 ;
21572174
21582175 UPDATE b
21592176 SET b .estimated_rows = er .estimated_rows
@@ -2652,6 +2669,159 @@ JOIN is_paul_white_electric ipwe
26522669ON ipwe .sql_handle = b .sql_handle
26532670OPTION (RECOMPILE );
26542671
2672+ IF EXISTS ( SELECT 1
2673+ FROM #working_warnings AS ww
2674+ WHERE ww .implicit_conversions = 1
2675+ OR ww .proc_or_function_name <> N ' Statement' )
2676+ BEGIN
2677+
2678+ RAISERROR (N ' Getting information about implicit conversions and stored proc parameters' , 0 , 1 ) WITH NOWAIT ;
2679+
2680+ WITH XMLNAMESPACES ( ' http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
2681+ , variables_types
2682+ AS (
2683+
2684+ -- WITH XMLNAMESPACES ( 'http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
2685+ SELECT
2686+ qp .query_hash ,
2687+ qp .sql_handle ,
2688+ b .proc_or_function_name AS proc_name,
2689+ q .n .value (' @Column' , ' NVARCHAR(128)' ) AS variable_name,
2690+ q .n .value (' @ParameterDataType' , ' NVARCHAR(128)' ) AS variable_datatype,
2691+ q .n .value (' @ParameterCompiledValue' , ' NVARCHAR(1000)' ) AS compile_time_value
2692+ FROM #query_plan AS qp
2693+ JOIN #working_warnings AS b
2694+ ON b .query_hash = qp .query_hash
2695+ CROSS APPLY qp .query_plan .nodes (' //p:QueryPlan/p:ParameterList/p:ColumnReference' ) AS q(n)
2696+ WHERE b .implicit_conversions = 1 ),
2697+ convert_implicit
2698+ AS (
2699+ -- WITH XMLNAMESPACES ( 'http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p )
2700+ SELECT
2701+ qp .query_hash ,
2702+ qp .sql_handle ,
2703+ b .proc_or_function_name AS proc_name,
2704+ qq .c .value (' @Expression' , ' NVARCHAR(128)' ) AS expression,
2705+ SUBSTRING (
2706+ qq .c .value (' @Expression' , ' NVARCHAR(128)' ), -- Original Expression
2707+ CHARINDEX (' @' , qq .c .value (' @Expression' , ' NVARCHAR(128)' )), -- Charindex of @+1
2708+ CHARINDEX (' ]' ,
2709+ qq .c .value (' @Expression' , ' NVARCHAR(128)' ), -- Charindex of end bracket
2710+ CHARINDEX (' @' , qq .c .value (' @Expression' , ' NVARCHAR(128)' )) + 1 -- Starting at the Charindex of the @ +1
2711+ ) - CHARINDEX (' @' , qq .c .value (' @Expression' , ' NVARCHAR(128)' ))) AS variable_name,
2712+ SUBSTRING (
2713+ qq .c .value (' @Expression' , ' NVARCHAR(128)' ), -- Original Expression
2714+ CHARINDEX (' ].[' , qq .c .value (' @Expression' , ' NVARCHAR(128)' )) + 3 , -- Charindex of ].[ + 3
2715+ CHARINDEX (' ]' ,
2716+ qq .c .value (' @Expression' , ' NVARCHAR(128)' ), -- Charindex of end bracket
2717+ CHARINDEX (' ].[' , qq .c .value (' @Expression' , ' NVARCHAR(128)' )) + 3 -- Starting at the Charindex of ].[ + 3
2718+ ) - CHARINDEX (' ].[' , qq .c .value (' @Expression' , ' NVARCHAR(128)' )) - 3 ) AS column_name,
2719+ SUBSTRING (
2720+ qq .c .value (' @Expression' , ' NVARCHAR(128)' ), -- Original Expression
2721+ CHARINDEX (' (' , qq .c .value (' @Expression' , ' NVARCHAR(128)' )) + 1 , -- Charindex of ( + 1
2722+ CHARINDEX (' ,' ,
2723+ qq .c .value (' @Expression' , ' NVARCHAR(128)' ), -- Charindex of comma
2724+ CHARINDEX (' (' , qq .c .value (' @Expression' , ' NVARCHAR(128)' )) + 1 -- Starting at the Charindex of ( + 1
2725+ ) - CHARINDEX (' (' , qq .c .value (' @Expression' , ' NVARCHAR(128)' )) - 1 ) AS converted_to
2726+ FROM #query_plan AS qp
2727+ JOIN #working_warnings AS b
2728+ ON b .query_hash = qp .query_hash
2729+ CROSS APPLY qp .query_plan .nodes (' //p:QueryPlan/p:Warnings/p:PlanAffectingConvert' ) AS qq(c)
2730+ WHERE qq .c .exist(' @ConvertIssue[.="Seek Plan"]' ) = 1
2731+ AND qp .query_hash IS NOT NULL
2732+ AND b .implicit_conversions = 1 )
2733+ INSERT #stored_proc_info ( query_hash, sql_handle , variable_name, variable_datatype, compile_time_value, proc_name, column_name, converted_to )
2734+ SELECT DISTINCT
2735+ COALESCE (vt .query_hash , ci .query_hash ) AS query_hash,
2736+ COALESCE (vt .sql_handle , ci .sql_handle ) AS sql_handle ,
2737+ COALESCE (vt .variable_name , ci .variable_name ) AS variable_name,
2738+ COALESCE (vt .variable_datatype , ci .converted_to ) AS variable_datatype,
2739+ COALESCE (vt .compile_time_value , ' *declared in proc*' ) AS compile_time_value,
2740+ COALESCE (vt .proc_name , ci .proc_name ) AS proc_name,
2741+ ci .column_name ,
2742+ ci .converted_to
2743+ FROM variables_types AS vt
2744+ RIGHT JOIN convert_implicit AS ci
2745+ ON (ci .variable_name = vt .variable_name
2746+ AND ci .query_hash = vt .query_hash )
2747+ OPTION (RECOMPILE );
2748+
2749+ WITH precheck AS (
2750+ SELECT
2751+ spi .sql_handle ,
2752+ spi .proc_name ,
2753+ CONVERT (XML ,
2754+ N ' <?ClickMe -- '
2755+ + @cr + @lf
2756+ + N ' The '
2757+ + CASE WHEN spi .proc_name <> ' Statement'
2758+ THEN N ' stored procedure ' + spi .proc_name
2759+ ELSE N ' Statement'
2760+ END
2761+ + N ' had the following implicit conversions: '
2762+ + CHAR (10 )
2763+ + STUFF ((
2764+ SELECT DISTINCT
2765+ @cr + @lf
2766+ + N ' The variable '
2767+ + spi2 .variable_name
2768+ + N ' has a data type of '
2769+ + spi2 .variable_datatype
2770+ + N ' which caused implicit conversion on the column '
2771+ + spi2 .column_name
2772+ + CASE WHEN spi2 .compile_time_value = ' *declared in proc*'
2773+ THEN N ' and is a declared variable.'
2774+ ELSE N ' and is a parameter of the stored procedure.'
2775+ END
2776+ FROM #stored_proc_info AS spi2
2777+ WHERE spi .sql_handle = spi2 .sql_handle
2778+ FOR XML PATH (N ' ' ), TYPE ).value (N ' .[1]' , N ' NVARCHAR(MAX)' ), 1 , 1 , N ' ' )
2779+ + CHAR (10 )
2780+ + N ' -- ?>'
2781+ ) AS implicit_conversion_info,
2782+ CONVERT (XML ,
2783+ N ' <?ClickMe -- '
2784+ + @cr + @lf
2785+ + N ' EXEC '
2786+ + spi .proc_name
2787+ + N ' '
2788+ + STUFF ((
2789+ SELECT DISTINCT N ' , '
2790+ + spi2 .variable_name
2791+ + N ' = '
2792+ + CASE WHEN spi2 .compile_time_value = ' NULL'
2793+ THEN spi2 .compile_time_value
2794+ ELSE QUOTENAME (spi2 .compile_time_value , ' '' ' )
2795+ END
2796+ FROM #stored_proc_info AS spi2
2797+ WHERE spi .sql_handle = spi2 .sql_handle
2798+ AND spi2 .proc_name <> ' Statement'
2799+ AND spi2 .compile_time_value <> ' *declared in proc*'
2800+ FOR XML PATH (N ' ' ), TYPE ).value (N ' .[1]' , N ' NVARCHAR(MAX)' ), 1 , 1 , N ' ' )
2801+ + @cr + @lf
2802+ + N ' -- ?>'
2803+ ) AS cached_execution_parameters
2804+ FROM #stored_proc_info AS spi
2805+ GROUP BY spi .sql_handle , spi .proc_name
2806+ )
2807+ UPDATE b
2808+ SET b .implicit_conversion_info = pk .implicit_conversion_info ,
2809+ b .cached_execution_parameters = pk .cached_execution_parameters
2810+ FROM #working_warnings AS b
2811+ JOIN precheck pk
2812+ ON pk .sql_handle = b .sql_handle
2813+ AND b .implicit_conversions = 1
2814+ OPTION (RECOMPILE );
2815+
2816+ END ; -- End implicit conversion information gathering
2817+
2818+ UPDATE b
2819+ SET b .implicit_conversion_info = CASE WHEN b .implicit_conversion_info IS NULL THEN ' <?NoNeedToClickMe -- N/A --?>' ELSE b .implicit_conversion_info END ,
2820+ b .cached_execution_parameters = CASE WHEN b .cached_execution_parameters IS NULL THEN ' <?NoNeedToClickMe -- N/A --?>' ELSE b .cached_execution_parameters END
2821+ FROM #working_warnings AS b
2822+ OPTION (RECOMPILE );
2823+
2824+
26552825RAISERROR (N ' General query dispositions: frequent executions, long running, etc.' , 0 , 1 ) WITH NOWAIT ;
26562826
26572827WITH XMLNAMESPACES(' http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
@@ -2862,9 +3032,10 @@ BEGIN
28623032
28633033RAISERROR (N ' Returning regular results' , 0 , 1 ) WITH NOWAIT ;
28643034
3035+
28653036WITH x AS (
28663037SELECT wpt .database_name , ww .query_cost , wm .plan_id , wm .query_id , wpt .query_sql_text , wm .proc_or_function_name , wpt .query_plan_xml , ww .warnings , wpt .pattern ,
2867- wm .parameter_sniffing_symptoms , wpt .top_three_waits , wm .count_executions , wm .count_compiles , wm .total_cpu_time , wm .avg_cpu_time ,
3038+ wm .parameter_sniffing_symptoms , wpt .top_three_waits , ww . implicit_conversion_info , ww . cached_execution_parameters , wm .count_executions , wm .count_compiles , wm .total_cpu_time , wm .avg_cpu_time ,
28683039 wm .total_duration , wm .avg_duration , wm .total_logical_io_reads , wm .avg_logical_io_reads ,
28693040 wm .total_physical_io_reads , wm .avg_physical_io_reads , wm .total_logical_io_writes , wm .avg_logical_io_writes , wm .total_rowcount , wm .avg_rowcount ,
28703041 wm .total_query_max_used_memory , wm .avg_query_max_used_memory , wm .total_tempdb_space_used , wm .avg_tempdb_space_used ,
@@ -2893,7 +3064,7 @@ RAISERROR(N'Returning results for failed queries', 0, 1) WITH NOWAIT;
28933064
28943065WITH x AS (
28953066SELECT wpt .database_name , ww .query_cost , wm .plan_id , wm .query_id , wpt .query_sql_text , wm .proc_or_function_name , wpt .query_plan_xml , ww .warnings , wpt .pattern ,
2896- wm .parameter_sniffing_symptoms , wpt .last_force_failure_reason_desc , wpt .top_three_waits , wm .count_executions , wm .count_compiles , wm .total_cpu_time , wm .avg_cpu_time ,
3067+ wm .parameter_sniffing_symptoms , wpt .last_force_failure_reason_desc , wpt .top_three_waits , ww . implicit_conversion_info , ww . cached_execution_parameters , wm .count_executions , wm .count_compiles , wm .total_cpu_time , wm .avg_cpu_time ,
28973068 wm .total_duration , wm .avg_duration , wm .total_logical_io_reads , wm .avg_logical_io_reads ,
28983069 wm .total_physical_io_reads , wm .avg_physical_io_reads , wm .total_logical_io_writes , wm .avg_logical_io_writes , wm .total_rowcount , wm .avg_rowcount ,
28993070 wm .total_query_max_used_memory , wm .avg_query_max_used_memory , wm .total_tempdb_space_used , wm .avg_tempdb_space_used ,
@@ -3955,6 +4126,10 @@ SELECT '#est_rows' AS table_name, *
39554126FROM #est_rows AS er
39564127OPTION (RECOMPILE );
39574128
4129+ SELECT ' #stored_proc_info' AS table_name, *
4130+ FROM #stored_proc_info AS spi
4131+ OPTION (RECOMPILE );
4132+
39584133END ;
39594134
39604135END TRY
0 commit comments