@@ -95,13 +95,13 @@ OPTION (RECOMPILE);
95
95
SELECT @log_size_mb = AVG (((mf .size * 8 ) / 1024 .))
96
96
FROM sys .master_files AS mf
97
97
WHERE mf .database_id = DB_ID (@DatabaseName)
98
- AND mf .type_desc = ' LOG'
98
+ AND mf .type_desc = ' LOG';
99
99
100
100
/* Grab avg tempdb file size*/
101
101
SELECT @avg_tempdb_data_file = AVG (((mf .size * 8 ) / 1024 .))
102
102
FROM sys .master_files AS mf
103
103
WHERE mf .database_id = DB_ID (' tempdb' )
104
- AND mf .type_desc = ' ROWS'
104
+ AND mf .type_desc = ' ROWS';
105
105
106
106
107
107
/* Help section*/
@@ -173,8 +173,8 @@ IF ( (SELECT SERVERPROPERTY ('EDITION')) = 'SQL Azure' )
173
173
SELECT @msg = N ' Sorry, sp_BlitzQueryStore doesn'' t work on Azure Data Warehouse, or Azure Databases with DB compatibility < 130.' + REPLICATE (CHAR (13 ), 7933 );
174
174
PRINT @msg;
175
175
RETURN ;
176
- END
177
- END
176
+ END ;
177
+ END ;
178
178
ELSE IF ( (SELECT PARSENAME (CONVERT (NVARCHAR (128 ), SERVERPROPERTY (' PRODUCTVERSION' )), 4 ) ) < 13 )
179
179
BEGIN
180
180
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(*)
200
200
RAISERROR (' Checking database validity' , 0 , 1 ) WITH NOWAIT ;
201
201
202
202
IF (@is_azure_db = 1 )
203
- SET @DatabaseName = DB_NAME ()
203
+ SET @DatabaseName = DB_NAME ();
204
204
ELSE
205
205
BEGIN
206
206
@@ -251,11 +251,11 @@ END;
251
251
252
252
/* Check database compat level*/
253
253
254
- RAISERROR (' Checking database compatibility level' , 0 , 1 ) WITH NOWAIT
254
+ RAISERROR (' Checking database compatibility level' , 0 , 1 ) WITH NOWAIT ;
255
255
256
256
SELECT @compatibility_level = d .compatibility_level
257
257
FROM sys .databases AS d
258
- WHERE d .name = @DatabaseName
258
+ WHERE d .name = @DatabaseName;
259
259
260
260
RAISERROR (' The @DatabaseName you specified ([%s])is running in compatibility level ([%d]).' , 0 , 1 , @DatabaseName, @compatibility_level) WITH NOWAIT ;
261
261
@@ -610,6 +610,8 @@ CREATE TABLE #working_warnings
610
610
is_big_log BIT ,
611
611
is_big_tempdb BIT ,
612
612
is_paul_white_electric BIT ,
613
+ implicit_conversion_info XML ,
614
+ cached_execution_parameters XML ,
613
615
warnings NVARCHAR (4000 )
614
616
INDEX ww_ix_ids CLUSTERED (plan_id, query_id, query_hash, sql_handle )
615
617
);
@@ -757,7 +759,22 @@ CREATE TABLE #warning_results
757
759
FindingsGroup NVARCHAR (50 ),
758
760
Finding NVARCHAR (200 ),
759
761
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)
761
778
);
762
779
763
780
/* Sets up WHERE clause that gets used quite a bit*/
@@ -878,8 +895,8 @@ IF (@ExportToExcel = 1 OR @SkipXML = 1)
878
895
IF @StoredProcName IS NOT NULL
879
896
BEGIN
880
897
881
- DECLARE @sql NVARCHAR (MAX )
882
- DECLARE @out INT
898
+ DECLARE @sql NVARCHAR (MAX );
899
+ DECLARE @out INT ;
883
900
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' ;
884
901
885
902
@@ -904,16 +921,16 @@ IF @StoredProcName IS NOT NULL
904
921
BEGIN
905
922
906
923
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.';
908
925
RAISERROR (@msg, 0 , 1 ) WITH NOWAIT ;
909
926
910
- SELECT @msg AS [Blue Flowers, Blue Flowers, Blue Flowers]
927
+ SELECT @msg AS [Blue Flowers, Blue Flowers, Blue Flowers];
911
928
912
929
RETURN ;
913
930
914
- END
931
+ END ;
915
932
916
- END
933
+ END ;
917
934
918
935
919
936
@@ -1460,7 +1477,7 @@ SELECT ' + QUOTENAME(@DatabaseName, '''') + N' AS database_name, wp.plan_id, wp.
1460
1477
((qsrs.last_query_max_used_memory * 8 ) / 1024.),
1461
1478
((qsrs.min_query_max_used_memory * 8 ) / 1024.),
1462
1479
((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,' ;
1464
1481
1465
1482
IF @new_columns = 1
1466
1483
BEGIN
@@ -1474,8 +1491,8 @@ SELECT ' + QUOTENAME(@DatabaseName, '''') + N' AS database_name, wp.plan_id, wp.
1474
1491
((qsrs.last_tempdb_space_used * 8 ) / 1024.),
1475
1492
((qsrs.min_tempdb_space_used * 8 ) / 1024.),
1476
1493
((qsrs.max_tempdb_space_used * 8 ) / 1024.)
1477
- '
1478
- END
1494
+ ' ;
1495
+ END ;
1479
1496
IF @new_columns = 0
1480
1497
BEGIN
1481
1498
SET @sql_select + = N'
@@ -1491,8 +1508,8 @@ SELECT ' + QUOTENAME(@DatabaseName, '''') + N' AS database_name, wp.plan_id, wp.
1491
1508
NULL,
1492
1509
NULL,
1493
1510
NULL
1494
- '
1495
- END
1511
+ ' ;
1512
+ END ;
1496
1513
SET @sql_select + =
1497
1514
N' FROM #working_plans AS wp
1498
1515
JOIN ' + QUOTENAME (@DatabaseName) + N' .sys.query_store_query AS qsq
@@ -2153,7 +2170,7 @@ SELECT DISTINCT
2153
2170
c .n .value (' (/p:StmtSimple/@StatementEstRows)[1]' , ' FLOAT' ) AS estimated_rows
2154
2171
FROM #statements AS s
2155
2172
CROSS 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 ;
2157
2174
2158
2175
UPDATE b
2159
2176
SET b .estimated_rows = er .estimated_rows
@@ -2652,6 +2669,159 @@ JOIN is_paul_white_electric ipwe
2652
2669
ON ipwe .sql_handle = b .sql_handle
2653
2670
OPTION (RECOMPILE );
2654
2671
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
+
2655
2825
RAISERROR (N ' General query dispositions: frequent executions, long running, etc.' , 0 , 1 ) WITH NOWAIT ;
2656
2826
2657
2827
WITH XMLNAMESPACES(' http://schemas.microsoft.com/sqlserver/2004/07/showplan' AS p)
@@ -2862,9 +3032,10 @@ BEGIN
2862
3032
2863
3033
RAISERROR (N ' Returning regular results' , 0 , 1 ) WITH NOWAIT ;
2864
3034
3035
+
2865
3036
WITH x AS (
2866
3037
SELECT 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 ,
2868
3039
wm .total_duration , wm .avg_duration , wm .total_logical_io_reads , wm .avg_logical_io_reads ,
2869
3040
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 ,
2870
3041
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;
2893
3064
2894
3065
WITH x AS (
2895
3066
SELECT 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 ,
2897
3068
wm .total_duration , wm .avg_duration , wm .total_logical_io_reads , wm .avg_logical_io_reads ,
2898
3069
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 ,
2899
3070
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, *
3955
4126
FROM #est_rows AS er
3956
4127
OPTION (RECOMPILE );
3957
4128
4129
+ SELECT ' #stored_proc_info' AS table_name, *
4130
+ FROM #stored_proc_info AS spi
4131
+ OPTION (RECOMPILE );
4132
+
3958
4133
END ;
3959
4134
3960
4135
END TRY
0 commit comments