Execution plans for mere mortals
-
Upload
mike-lawell -
Category
Data & Analytics
-
view
63 -
download
1
Transcript of Execution plans for mere mortals
Who Am I?Mike Lawell
Mike LawellPrincipal Consultant
Twitter: @SQLDiver
Blog: SQLServerAssociates.com
LinkedIn: LinkedIn.com/in/MikeLawell
On The InsideRelational Engine
Optimizer
Command Parser
Query Executor
SNI
User
Storage Engine
Buffer Manager
Access Methods
Transaction Manager
SQL OS
Buffer Pool
Borrowed from Bradley Ball’s “SQL Internals, Recovery Models, & Backups” presentation
Plan Cache
Data Cache
Data
Query ParsingRelation Engine
1. Is the query syntactically correct?
SELECT [Nmae], [Number] FROM [Production].[Product]Msg 207, Level 16, State 1, Line 3
Invalid column name 'Nmae'.
SELCT [Name], [Number] FROM [Production].[Product]Msg 156, Level 15, State 1, Line 3
Incorrect syntax near the keyword 'FROM'.
2. No Errors Output as Parse Tree to Algebrizer
Query Parsing
SELECT [Nmae], [Number] FROM [Production].[Product]
Msg 207, Level 16, State 1, Line 3Invalid column name 'Nmae'.
Algebrizer
1. Resolve the Names of the objects (tables, columns, etc.)
2. Resolve the data types for the objects being accessed.
3. Finds aggregate operations (min, max, group by) and performs an operation called aggregate binding.
If the names of the objects, columns aren’t found an error is output and the process halts.
If it is successful the algebrizer outputs the query processor tree to the query optimizer. The query hash is generated and passed with the query processor tree.
Query Optimizer
1. The query optimizer checks the hash against the plan cache. If it exists use it and the optimization step is skipped and plan is passed to query execution.
2. The query optimizer uses the query processor tree and the statistics to determine an estimated execution plan.
3. The optimizer goes through a process of using different types of joins and indexes and assigns a cost to each step by CPU and I/O and determines a cost for each possible estimated plan it generates.
4. Once an acceptable estimated plan is found it is stored in the plan cache then sent to the query execution.
Query Execution
Storage Engine
1. The storage engine determines if a recompile was triggered causing a change in the estimated execution plan.
2. SQL Server determines based upon cost whether it exceeds the threshold for parallelism changing the estimated plan for parallelism.
3. Statistics that have changed causing a change in the estimated plan.
SQL Server returns the results.
Cardinality EstimationThe optimization process depends on a cost estimation of each physical operator and the estimated number of records (cardinality estimation) to be processed.
DBCC SHOW_STATISTICS ("[Sales].[SalesOrderDetailEnlarged]", [_WA_Sys_00000003_3D7E1B63]);
The accuracy of the cardinality estimation depends upon the distribution of values in one or more columns of a table (statistics).Name Updated Rows Rows Sampled Steps Average key length String Index Filter Expression Unfiltered Rows_WA_Sys_00000003_3D7E1B63 Nov 26 2014 11:26AM 1000 1000 196 24 YES NULL 1000
RANGE_HI_KEY RANGE_ROWS EQ_ROWS DISTINCT_RANGE_ROWS AVG_RANGE_ROWS002EE045-E30 0 1 0 10169388E-8EC 6 1 6 1031622B3-B68 3 1 3 10424F840-4C2 3 1 3 104C4FEAC-7C1 3 1 3 1
Cardinality EstimationThere are multiple factors that can negatively impact the cardinality estimation process:
1. Out of date Statistics
SELECT object_name(object_id) as TableName, name AS stats_name,
STATS_DATE(object_id, stats_id) AS statistics_update_date
FROM sys.stats
WHERE object_id = OBJECT_ID('[Sales].[SalesOrderDetailEnlarged]');
2. Cardinality Estimation Errors
https://www.sqlskills.com/blogs/joe/cardinality-estimation-model-version/
Required Permissions
Server or database roles with permissions:sysadmin, dbcreator or db_owner or showplan
To give showplan rights to the database:GRANT SHOWPLAN TO [username];
Nested LoopNon-blocking
DescriptionFor each row in the top (outer) input, scan the bottom (inner) input, and output matching rows.
Where does it happen: Joins with indexes on join columns.
Nested Loop
SELECT [sod].[CarrierTrackingNumber], [sod].[OrderQty], [soh].[RevisionNumber], [soh].[OrderDateFROM [Sales].[SalesOrderHeader] sohJOIN [Sales].[SalesOrderDetail] sodON sod.[SalesOrderID] = soh.[SalesOrderID]WHERE soh.[OrderDate] = '2013-08-30 00:00:00.000'
Non-blocking
Outer
Inner
Nested LoopSalesOrderID RevisionNumber OrderDate55233 8 2013-08-30 00:00:00.00055234 8 2013-08-30 00:00:00.00055235 8 2013-08-30 00:00:00.00055236 8 2013-08-30 00:00:00.00055237 8 2013-08-30 00:00:00.00055238 8 2013-08-30 00:00:00.00055239 8 2013-08-30 00:00:00.00055240 8 2013-08-30 00:00:00.00055241 8 2013-08-30 00:00:00.00055242 8 2013-08-30 00:00:00.00055243 8 2013-08-30 00:00:00.00055244 8 2013-08-30 00:00:00.00055245 8 2013-08-30 00:00:00.00055246 8 2013-08-30 00:00:00.00055247 8 2013-08-30 00:00:00.00055248 8 2013-08-30 00:00:00.00055249 8 2013-08-30 00:00:00.00055250 8 2013-08-30 00:00:00.00055251 8 2013-08-30 00:00:00.00055252 8 2013-08-30 00:00:00.00055253 8 2013-08-30 00:00:00.000
SalesOrderID ProductID CarrierTrackingNumber OrderQty55233 738 1590-499E-95 555233 792 1590-499E-95 555233 793 1590-499E-95 455233 794 1590-499E-95 455233 795 1590-499E-95 455233 796 1590-499E-95 555233 797 1590-499E-95 655233 798 1590-499E-95 255233 799 1590-499E-95 155233 800 1590-499E-95 155233 801 1590-499E-95 655233 835 1590-499E-95 655233 874 1590-499E-95 855233 875 1590-499E-95 855233 938 1590-499E-95 555233 939 1590-499E-95 555233 940 1590-499E-95 455233 973 1590-499E-95 555233 974 1590-499E-95 155233 975 1590-499E-95 1
Non-blocking
DescriptionMatch rows from two suitably sorted input tables exploiting their sort order.
Where does it happen: Joins with indexes on join columns sorted in an appropriate sort order.
MergeNon-blocking
MergeNon-blocking
SELECT [sod].[CarrierTrackingNumber], [sod].[OrderQty], [soh].[RevisionNumber], [soh].[OrderDate]FROM [Sales].[SalesOrderHeader] sohJOIN [Sales].[SalesOrderDetail] sodON sod.[SalesOrderID] = soh.[SalesOrderID]
MergeNon-blocking
SalesOrderID RevisionNumber OrderDate55233 8 2013-08-30 00:00:00.00055234 8 2013-08-30 00:00:00.00055235 8 2013-08-30 00:00:00.00055236 8 2013-08-30 00:00:00.00055237 8 2013-08-30 00:00:00.00055238 8 2013-08-30 00:00:00.00055239 8 2013-08-30 00:00:00.00055240 8 2013-08-30 00:00:00.00055241 8 2013-08-30 00:00:00.00055242 8 2013-08-30 00:00:00.00055243 8 2013-08-30 00:00:00.00055244 8 2013-08-30 00:00:00.00055245 8 2013-08-30 00:00:00.00055246 8 2013-08-30 00:00:00.00055247 8 2013-08-30 00:00:00.00055248 8 2013-08-30 00:00:00.00055249 8 2013-08-30 00:00:00.00055250 8 2013-08-30 00:00:00.00055251 8 2013-08-30 00:00:00.00055252 8 2013-08-30 00:00:00.00055253 8 2013-08-30 00:00:00.000
SalesOrderID ProductID CarrierTrackingNumber OrderQty55233 738 1590-499E-95 555233 792 1590-499E-95 555233 793 1590-499E-95 455233 794 1590-499E-95 455233 795 1590-499E-95 455233 796 1590-499E-95 555233 797 1590-499E-95 655233 798 1590-499E-95 255233 799 1590-499E-95 155233 800 1590-499E-95 155233 801 1590-499E-95 655233 835 1590-499E-95 655233 874 1590-499E-95 855233 875 1590-499E-95 855233 938 1590-499E-95 555233 939 1590-499E-95 555233 940 1590-499E-95 455233 973 1590-499E-95 555233 974 1590-499E-95 155233 975 1590-499E-95 1
Hash Match
DescriptionUse each row from the top input to build a hash table, and each row from the bottom input to probe into the hash table, outputting all matching rows.
Where does it happen: Missing Index, Missing Where Clause, Where Clause that is non-sargable.
Hash MatchBlocking
SELECT [sod1].[ProductID], [sod1].[CarrierTrackingNumber], [sod1].[UnitPrice], [sod1].[OrderQty]FROM [Sales].[SalesOrderDetail] sod1JOIN [Sales].[SalesOrderDetail] sod2ON [sod1].[ProductID] = [sod2].[ProductID]AND [sod1].[CarrierTrackingNumber] = [sod2].[CarrierTrackingNumber]AND [sod1].[OrderQty] > 5
Hash MatchBlocking
ProductID CarrierTrackingNumber Hash707 4BC2-4C6D-83 -767544381711 2AA4-4D12-9F -767515863715 C811-4D40-80 -767499885715 8FCD-4D55-8F -767496256714 CC2F-4D47-8C -767488607712 BE3B-4D30-A7 -767488106707 9EBF-4C7E-82 -767471833716 F78B-4D68-8E -767470049712 8EBE-4D2C-AB -767459569716 7DE5-4D74-A7 -767455808715 46EF-4D44-A9 -767449380711 9FC2-4D08-A7 -767447057725 6B7D-4E47-B5 -767446438
ProductID CarrierTrackingNumber736 2554-4F68-AB -767556491717 9526-4D73-83 -767556433715 CC2F-4D47-8C -767554143715 2960-4D59-96 -767553412722 9708-4E22-A4 -767551834722 6F5C-4E3C-B5 -767549829717 7E0A-4D6E-9E -767545458707 4BC2-4C6D-83 -767544381716 F108-4D76-8A -767543168711 B900-4D0D-AF -767541067711 67A0-4D10-87 -767539667707 8441-4C6F-89 -767535798739 688B-4F92-AD -767532740
Hash Table 1 Hash Table 2
Parallelism
SELECT [sod1].[ProductID], [sod1].[CarrierTrackingNumber], [sod1].[UnitPrice], [sod1].[OrderQty]FROM [Sales].[SalesOrderDetail] sod1JOIN [Sales].[SalesOrderDetail] sod2ON [sod1].[ProductID] = [sod2].[ProductID]AND [sod1].[CarrierTrackingNumber] = [sod2].[CarrierTrackingNumber]
ParallelismSET STATISTICS IO ONSET STATISTICS TIME ON
SELECT [sod1].[ProductID], [sod1].[CarrierTrackingNumber], [sod1].[UnitPrice], [sod1].[OrderQty]FROM [Sales].[SalesOrderDetail] sod1JOIN [Sales].[SalesOrderDetail] sod2ON [sod1].[ProductID] = [sod2].[ProductID]AND [sod1].[CarrierTrackingNumber] = [sod2].[CarrierTrackingNumber]
SELECT [sod1].[ProductID], [sod1].[CarrierTrackingNumber], [sod1].[UnitPrice], [sod1].[OrderQty]FROM [Sales].[SalesOrderDetail] sod1JOIN [Sales].[SalesOrderDetail] sod2ON [sod1].[ProductID] = [sod2].[ProductID]AND [sod1].[CarrierTrackingNumber] = [sod2].[CarrierTrackingNumber]OPTION(MAXDOP 1)
ParallelismTable 'SalesOrderDetail'. Scan count 18, logical reads 2490, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.Table 'Workfile'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:CPU time = 9593 ms, elapsed time = 2381 ms.
Table 'Workfile'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.Table 'Worktable'. Scan count 0, logical reads 0, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.Table 'SalesOrderDetail'. Scan count 2, logical reads 2490, physical reads 0, read-ahead reads 0, lob logical reads 0, lob physical reads 0, lob read-ahead reads 0.
SQL Server Execution Times:CPU time = 7941 ms, elapsed time = 8372 ms.
Hash AggregateSELECT [sod1].[CarrierTrackingNumber], sum([sod1].[UnitPrice]) UnitPriceTotal,
sum([sod1].[OrderQty]) as OrderQtyTotalFROM [Sales].[SalesOrderDetail] sod1JOIN [Sales].[SalesOrderDetail] sod2ON [sod1].[ProductID] = [sod2].[ProductID]AND [sod1].[CarrierTrackingNumber] = [sod2].[CarrierTrackingNumber]AND [sod1].[OrderQty] > 5GROUP BY [sod1].[CarrierTrackingNumber]
Stream AggregateSELECT [sod1].[CarrierTrackingNumber], sum([sod1].[UnitPrice]) UnitPriceTotal,
sum([sod1].[OrderQty]) as OrderQtyTotalFROM [Sales].[SalesOrderDetail] sod1JOIN [Sales].[SalesOrderDetail] sod2ON [sod1].[ProductID] = [sod2].[ProductID]AND [sod1].[CarrierTrackingNumber] = [sod2].[CarrierTrackingNumber]AND [sod1].[OrderQty] > 5GROUP BY [sod1].[CarrierTrackingNumber]
Sort (spill)
Operator used tempdb to spill data during execution with spill level 1
SELECT [sod].[CarrierTrackingNumber], [sod].[OrderQty], [soh].[RevisionNumber], [soh].[OrderDate]FROM [Sales].[SalesOrderHeader] sohJOIN [Sales].[SalesOrderDetailEnlarged] sodON sod.[SalesOrderID] = soh.[SalesOrderID]WHERE [sod].[CarrierTrackingNumber] > '41D0-42A8-A5'ORDER BY [soh].[RevisionNumber], [soh].[OrderDate]
Sort SpillSELECT TOP 1 [sod].[CarrierTrackingNumber], [sod].[OrderQty], [soh].[RevisionNumber], [soh].[OrderDate]FROM [Sales].[SalesOrderHeader] sohJOIN [Sales].[SalesOrderDetailEnlarged] sodON sod.[SalesOrderID] = soh.[SalesOrderID]WHERE [sod].[CarrierTrackingNumber] > '41D0-42A8-A5'ORDER BY [soh].[RevisionNumber], [soh].[OrderDate]
Implicit ConversionSELECT [sod].[ProductID], [sod].[CarrierTrackingNumber], [sod].[UnitPrice], [sod].[OrderQty], [rowguid]FROM [Sales].[SalesOrderDetail] sodWHERE LEFT([sod].[rowguid],4) = N'FE10'
Cool Tools/Products
SQL Server Query Plan Analysis by Joe Sack
Thank You!Mike LawellPrincipal Consultant
Twitter: @SQLDiver
Blog: SQLServerAssociates.com
LinkedIn: LinkedIn.com/in/MikeLawell
Email: [email protected]