SlideShare a Scribd company logo
Uncovering SQL Server Query Problems with
Execution Plans
Tony Davis
tony.davis@red-gate.com
@tonytheeditor
#SQLintheCityUK
About Me
Books
SQL Server transaction log
SQL Server Source Control
Speaker
#SQLintheCityUK
SELECT ProductID ,
ProductNumber ,
dbo.LineItemTotal(ProductID)
AS SumTotal
FROM Production.Product p
GO
SELECT bth.ProductID ,
bth.TransactionDate ,
bth.Quantity ,
bth.ActualCost
FROM dbo.bigTransactionHistory bth
WHERE bth.TransactionDate >= '20100701'
AND bth.TransactionDate
<= CURRENT_TIMESTAMP
ORDER BY bth.TransactionDate DESC;
GO
Why Bother Learning Execution Plans?
WITH Totals
AS ( SELECT DATEADD(m, u.theMonth, 0) AS TheMonth ,
SUM(CASE WHEN u.theCol = 'DateJoined'
THEN u.Registrations
ELSE 0
END) AS PeopleJoined ,
SUM(CASE WHEN u.theCol = 'DateLeft'
THEN u.Registrations
ELSE 0
END) AS PeopleLeft
FROM ( SELECT DATEDIFF(MONTH, 0, DateJoined)
AS DateJoined ,
DATEDIFF(MONTH, 0, DateLeft)
AS DateLeft ,
COUNT(*) AS Registrations
FROM dbo.Registrations2
GROUP BY DATEDIFF(MONTH, 0, DateJoined) ,
DATEDIFF(MONTH, 0, DateLeft)
) AS d UNPIVOT ( theMonth FOR theCol
IN ( d.DateJoined,
d.DateLeft ) ) AS u
GROUP BY u.theMonth
HAVING SUM(CASE WHEN u.theCol = 'DateJoined'
THEN u.Registrations
ELSE 0
END) > 0
)
SELECT TheMonth ,
PeopleJoined ,
PeopleLeft ,
SUM(PeopleJoined - PeopleLeft) OVER ( ORDER BY TheMonth
ROWS UNBOUNDED PRECEDING ) AS CurrentSubscribers
FROM Totals;
From Query to Execution plan
SELECT bth.ProductID ,
bth.TransactionDate ,
bth.Quantity ,
bth.ActualCost
FROM dbo.bigTransactionHistory bth
WHERE bth.TransactionDate >= '20100701'
AND bth.TransactionDate <=
CURRENT_TIMESTAMP
ORDER BY bth.TransactionDate DESC;
GO
#SQLintheCityUK
Plan
1
Plan
2
Plan
3
Plan
4
Plan
57
Plan
n?
Algebrizer
(Query Binding)
Syntax Checking
(Query Parsing)
Query
Optimization
Plan
Query
Execution
Engine
Plan
Cache
Metadata
Table/index structures
SELECT bth.ProductID ,
bth.TransactionDate ,
bth.Quantity ,
bth.ActualCost
FROM dbo.bigTransactionHistory bth
WHERE bth.TransactionDate >= '20100701'
AND bth.TransactionDate <= CURRENT_TIMESTAMP
ORDER BY bth.TransactionDate DESC;
GO
Index/Column Statistics
Volume/distribution of data
#SQLintheCityUK
Getting the Execution Plan
.NET Code Profiler
- e.g. ANTS Performance Profiler
Get plans for previously executed
queries from the plan cache
- e.g. SQL Trace or Extended Events
- Using sys.dm_exec_cached_plans
Plans in cache contain no runtime
information

#SQLintheCityUK
#SQLintheCityUK
Getting the Execution Plan
.NET Code Profiler
- e.g. ANTS Performance Profiler
Get plans for previously executed
queries from the plan cache
- e.g. SQL Trace or Extended Events
- Using sys.dm_exec_cached_plans
Plans in cache contain no runtime
information
 SET
During testing, request the plan for
a query
• “estimated” plan – no runtime
• “actual” plan - with runtime
There is only 1 plan!

#SQLintheCityUK
#SQLintheCityUK
Execution Plans for Developers
• Don’t examine plan for every query
• Gather stats, focus on critical, frequent, resource-
intensive queries
• Sometimes the code logic is just wrong
– rip it up and start again!
• Sometimes the basic logic is fine
– But some subtler problem causes poor execution performance
– This is where execution plans can help!
#SQLintheCityUK
• With SQL Server:
• We submit SQL describing the data set we
want
• The Optimizer decides how to execute it
• Some developers bring imperative approach line-
by-line control to SQL Server…
Problem: Row-by-row strategy
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
Solution: set-based approach
– Take the list to the supermarket ;)
– Define single statement (if possible) to return required
data set
• Make as few ‘passes’ through the base tables as possible
• Aggregate early
• Reduce the working set to as few rows as possible
#SQLintheCityUK
WITH Totals
AS ( SELECT DATEADD(m, u.theMonth, 0) AS TheMonth ,
SUM(CASE WHEN u.theCol = 'DateJoined'
THEN u.Registrations
ELSE 0
END) AS PeopleJoined ,
SUM(CASE WHEN u.theCol = 'DateLeft'
THEN u.Registrations
ELSE 0
END) AS PeopleLeft
FROM ( SELECT DATEDIFF(MONTH, 0, DateJoined)
AS DateJoined ,
DATEDIFF(MONTH, 0, DateLeft)
AS DateLeft ,
COUNT(*) AS Registrations
FROM dbo.Registrations2
GROUP BY DATEDIFF(MONTH, 0, DateJoined) ,
DATEDIFF(MONTH, 0, DateLeft)
) AS d UNPIVOT ( theMonth FOR theCol
IN ( d.DateJoined,
d.DateLeft ) ) AS u
GROUP BY u.theMonth
HAVING SUM(CASE WHEN u.theCol = 'DateJoined'
THEN u.Registrations
ELSE 0
END) > 0
)
SELECT TheMonth ,
PeopleJoined ,
PeopleLeft ,
SUM(PeopleJoined - PeopleLeft) OVER ( ORDER BY TheMonth
ROWS UNBOUNDED PRECEDING ) AS CurrentSubscribers
FROM Totals;
Classic Running Total Problem
A Registrations table containing a list of
subscribers, with the dates that the subscribers
joined and left
#SQLintheCityUK
WITH Totals
AS ( SELECT DATEADD(m, u.theMonth, 0) AS TheMonth ,
SUM(CASE WHEN u.theCol = 'DateJoined'
THEN u.Registrations
ELSE 0
END) AS PeopleJoined ,
SUM(CASE WHEN u.theCol = 'DateLeft'
THEN u.Registrations
ELSE 0
END) AS PeopleLeft
FROM ( SELECT DATEDIFF(MONTH, 0, DateJoined)
AS DateJoined ,
DATEDIFF(MONTH, 0, DateLeft)
AS DateLeft ,
COUNT(*) AS Registrations
FROM dbo.Registrations2
GROUP BY DATEDIFF(MONTH, 0, DateJoined) ,
DATEDIFF(MONTH, 0, DateLeft)
) AS d UNPIVOT ( theMonth FOR theCol
IN ( d.DateJoined,
d.DateLeft ) ) AS u
GROUP BY u.theMonth
HAVING SUM(CASE WHEN u.theCol = 'DateJoined'
THEN u.Registrations
ELSE 0
END) > 0
)
SELECT TheMonth ,
PeopleJoined ,
PeopleLeft ,
SUM(PeopleJoined - PeopleLeft) OVER ( ORDER BY TheMonth
ROWS UNBOUNDED PRECEDING ) AS CurrentSubscribers
FROM Totals;
A single ‘pass’
of the base table
Early
Aggregation
10000001690
Execution Plan
#SQLintheCityUK
Problem: Excessive logical reads
• “Read 1000 pages to return 100 rows”
– 1 page = 1 logical read
– Goal: return data with few logical reads as possible
– Sometimes we force SQL Server to read more pages than necessary (excessive IO)
Inefficient data access paths (indexes)
Inefficient SQL forcing SQL Server to do
excessive work

#SQLintheCityUK
Name
(Index Key)
A - Z
+ LocationID
Name
(Index Key)
A - E
+ LocationID
Name
(Index Key)
F - I
+ LocationID
Name
(Index Key)
J - M
+ LocationID
Name
(Index Key)
N - R
+ LocationID
Name
(Index Key)
S - V
+ LocationID
Name
(Index Key)
W - Z
+ LocationID
Root Node
Intermediate Level
Leaf Level
Name
(Index Key)
A - M
+ LocationID
Name
(Index Key)
N - Z
+ LocationID
WHERE
l.Name ='Paint';Non-clustered Index
#SQLintheCityUK
Name
(Index Key)
A - Z
+ LocationID
Name
(Index Key)
A - E
+ LocationID
Name
(Index Key)
F - I
+ LocationID
Name
(Index Key)
J - M
+ LocationID
Name
(Index Key)
N - R
+ LocationID
Name
(Index Key)
S - V
+ LocationID
Name
(Index Key)
W - Z
+ LocationID
Root Node
Intermediate Level
Leaf Level
Name
(Index Key)
A - M
+ LocationID
Name
(Index Key)
N - Z
+ LocationID
WHERE
l.Name ='Paint';Non-clustered Index
#SQLintheCityUK
LocationID = 6;
(ProductID,
LocationID)
(Clustering Key)
1 to 12000
(ProductID,
LocationID)
1-6000
(ProductID,
LocationID)
6001-12000
Data rows
for
ProductID
1-2000
Data rows
for
ProductID
2001-4000
Data rows
for
ProductID
4001-6000
Data rows
for
ProductID
6001-8000
Data rows
for
ProductID
80001-10000
Data rows
for
ProductID
10001-12000
Root Node
Intermediate Level
Leaf Level(502,6) (4325,6) (10324,6)(7865,6)
Clustered Index
#SQLintheCityUK
LocationID = 6;
(ProductID,
LocationID)
(Clustering Key)
1 to 12000
(ProductID,
LocationID)
1-6000
(ProductID,
LocationID)
6001-12000
Data rows
for
ProductID
1-2000
Data rows
for
ProductID
2001-4000
Data rows
for
ProductID
4001-6000
Data rows
for
ProductID
6001-8000
Data rows
for
ProductID
80001-10000
Data rows
for
ProductID
10001-12000
Root Node
Intermediate Level
Leaf Level
Clustered Index
#SQLintheCityUK
LocationID = 6;
(ProductID,
LocationID)
(Clustering Key)
1 to 12000
(ProductID,
LocationID)
1-6000
(ProductID,
LocationID)
6001-12000
Data rows
for
ProductID
1-2000
Data rows
for
ProductID
2001-4000
Data rows
for
ProductID
4001-6000
Data rows
for
ProductID
6001-8000
Data rows
for
ProductID
80001-10000
Data rows
for
ProductID
10001-12000
Root Node
Intermediate Level
Leaf Level
#SQLintheCityUK
#SQLintheCityUK
ProductID
(Index Key)
A - Z
+ SalesOrderID
ProductID
(Index Key)
1 - 150
+ SalesOrderID
ProductID
(Index Key)
151 - 300
+ SalesOrderID
ProductID
(Index Key)
301 - 450
+ SalesOrderID
ProductID
(Index Key)
451 - 600
+ SalesOrderID
ProductID
(Index Key)
601 - 750
+ SalesOrderID
ProductID
(Index Key)
751 - 900
+ SalesOrderID
Root Node
Intermediate Level
Leaf Level
ProductID
(Index Key)
A - M
+ SalesOrderID
ProductID
(Index Key)
N - Z
+ SalesOrderID
Lookup
CarrierTrackingNumber
in clustered index
for each row
Key Lookup
SELECT ProductID ,
SalesOrderID ,
CarrierTrackingNumber
FROM Sales.SalesOrderDetail
WHERE ProductID = 709;
#SQLintheCityUK
#SQLintheCityUK
#SQLintheCityUK
• For small tables, scanning an index is very
efficient operation
• BUT…for critical and frequently-executed
queries:
– Provide covering non-clustered indexes that the
queries can seek
– Minimize logical reads (IO) required to gather the
data
Solution: optimize indexes for workloadSolution: optimize indexes for workload
#SQLintheCityUK
–Worst Strategy: No indexes.
Query performance will be terrible
–Second worst: Index on every column; same
column participating in numerous indexes
Data modification performance will be awful
–Don’t do “SELECT *”
Makes effective indexing VERY hard!
Solution: optimize indexes for workloadSolution: optimize indexes for workload
#SQLintheCityUK
• Don’t index query by query
• Goal: small set of indexes to help most important and frequent queries
• Analyze the workload as a whole
– Profiler and Database Engine Tuning Advisor
• Good start point…
• Not necessarily good end point
– Refine recommendations using Missing Index (and other) DMVs
• What is the write : read ratio (>1?) for the index?
• How many plans currently associated with index?
• How often are those plans used?
• How many reads might the missing index have helped? Don’t create if this number is small…
– See Performance Tuning with SQL Server Dynamic Management Views (free eBook)
• http://bit.ly/1jVG3IW
Solution: optimize indexes for workload
“The explicit data type conversion
has rendered the predicate non-SARGable!”
Problem: Non-SARGable Predicates
#SQLintheCityUK
Beware of SARG-ability
• Non-SARGable means that the optimizer can’t
use the expression in a seek operation
• Cause: use of function directly on column in
WHERE or JOIN
– Various incarnations
– All lead to excessive IO (scans when seeks should be viable)
#SQLintheCityUK
SARG-able predicate
…WHERE FirstName = 'bob';  SARGable
FirstName LastName
Aaron Bertrand
Aaron Darrenovsky
Amber Smith
Apple Paltrow
Bob Duffy
Bob Carolgees
Chris Christofferson
…etc...
#SQLintheCityUK
Non SARGable predicate
…WHERE REVERSE(FirstName) = 'bob';
FirstName LastName
Aaron Bertrand
Aaron Darrenovsky
Amber Smith
Apple Paltrow
Bob Duffy
Bob Carolgees
Chris Christofferson
…etc...
REVERSE ('Aaron') = 'bob' ?
#SQLintheCityUK
Non SARG-able predicate
…WHERE REVERSE(FirstName) = 'bob';
FirstName LastName
Aaron Bertrand
Aaron Darrenovsky
Amber Smith
Apple Paltrow
Bob Duffy
Bob Carolgees
Chris Christofferson
…etc...
REVERSE ('Aaron') = 'bob' ?
#SQLintheCityUK
Non SARGable predicate
…WHERE REVERSE(FirstName) = 'bob';
FirstName LastName
Aaron Bertrand
Aaron Darrenovsky
Amber Smith
Apple Paltrow
Bob Duffy
Bob Carolgees
Chris Christofferson
…etc...
REVERSE ('Amber') = 'bob' ?
 Non-SARGable
#SQLintheCityUK
#SQLintheCityUK
Other common explicit conversions
-- Using functions like LTRIM, RTRIM etc.
-- often happens where people don't trust
consistency of data inputs
SELECT bp.ProductID ,
bp.Name ,
bp.ProductNumber
FROM dbo.bigProduct bp
WHERE LTRIM(bp.ProductNumber) LIKE 'AR%';
GO
SELECT bp.ProductID ,
bp.Name ,
bp.ProductNumber
FROM dbo.bigProduct bp
WHERE bp.ProductNumber LIKE N'%1000';
GO
#SQLintheCityUK
Other causes of Non-SARG
• Implicit Data type conversions
– Variable data type doesn’t match column type
– SQL Server uses CONVERT_IMPLICIT
– Problem if column is lower precedence type
• Data type precedence order: http://bit.ly/1ENOwjZ
• Misuse of User Defined Functions (UDFs)
#SQLintheCityUK
• Rewrite the query to avoid direct use of
function on column
• Use helper functions
• Avoid data type mismatches
• Be careful when using UDFs!
– Convert scalar UDFs to inline TVFs
– http://bit.ly/1oN9ysK
Solutions to Non-SARGability
#SQLintheCityUK
• One query size fits all
• Leads to wildly inaccurate estimations and
inappropriate execution plans
Problem: Overly Generic SQL
#SQLintheCityUK
#SQLintheCityUK
• Write SQL and stored procedures for a specific, defined
purpose
• Various ‘workarounds’ though none without drawbacks
– Recompile on each execution
– Dynamic SQL
– See Gail Shaw’s “How to Confuse the Query Optimizer”
(http://bit.ly/1Mb8Rnp)
Solutions to Generic SQL
#SQLintheCityUK
Other issues that cause
poor estimations
• Stale statistics
• Problems with parameter sniffing
#SQLintheCityUK
Very wild estimation!
#SQLintheCityUK
Conclusions
• Plans tell us exactly how SQL Server executed your
query
– Focus on critical and frequently-executed queries
– Use plans to:
• spot common mistakes in the code
• Uncover problems with inefficient indexing
– Work with rather than against SQL Server
#SQLintheCityUK
Thank you to…
• Grant Fritchey - @GFritchey
• Hugo Kornelis - @Hugo_Kornelis
• Gail Shaw - @SQLintheWild
• Rodney Landrum - @SQLBeat
• Phil Factor - @Phil_Factor
#SQLintheCityUK

More Related Content

What's hot (20)

PPT
Sql server performance tuning and optimization
Manish Rawat
 
PPTX
Before you optimize: Understanding Execution Plans
Timothy Corey
 
PPT
Sql server performance tuning
ngupt28
 
PPTX
Database Performance Tuning
Arno Huetter
 
PPT
Sql Server Performance Tuning
Bala Subra
 
PPTX
Top 10 tips for Oracle performance (Updated April 2015)
Guy Harrison
 
PDF
SQL Server Optimization Checklist
Grant Fritchey
 
PPTX
Stored procedure tuning and optimization t sql
nishantdavid9
 
PPTX
Sql server performance tuning
Jugal Shah
 
PPTX
Oracle performance tuning_sfsf
Mao Geng
 
PPTX
The Plan Cache Whisperer - Performance Tuning SQL Server
Jason Strate
 
PPTX
Database Testing
Siva Kotilingam Pallikonda
 
PPTX
Advanced integration services on microsoft ssis 1
Skillwise Group
 
PPTX
Oracle Performance Tuning Training | Oracle Performance Tuning
OracleTrainings
 
PPTX
Oracle Database Performance Tuning Basics
nitin anjankar
 
PDF
Unit Testing SQL Server
Giovanni Scerra ☃
 
PPT
Oracle SQL Tuning
Alex Zaballa
 
PDF
Proactive performance monitoring with adaptive thresholds
John Beresniewicz
 
PDF
SQL Server Performance Tuning Baseline
► Supreme Mandal ◄
 
PDF
Oracle db performance tuning
Simon Huang
 
Sql server performance tuning and optimization
Manish Rawat
 
Before you optimize: Understanding Execution Plans
Timothy Corey
 
Sql server performance tuning
ngupt28
 
Database Performance Tuning
Arno Huetter
 
Sql Server Performance Tuning
Bala Subra
 
Top 10 tips for Oracle performance (Updated April 2015)
Guy Harrison
 
SQL Server Optimization Checklist
Grant Fritchey
 
Stored procedure tuning and optimization t sql
nishantdavid9
 
Sql server performance tuning
Jugal Shah
 
Oracle performance tuning_sfsf
Mao Geng
 
The Plan Cache Whisperer - Performance Tuning SQL Server
Jason Strate
 
Database Testing
Siva Kotilingam Pallikonda
 
Advanced integration services on microsoft ssis 1
Skillwise Group
 
Oracle Performance Tuning Training | Oracle Performance Tuning
OracleTrainings
 
Oracle Database Performance Tuning Basics
nitin anjankar
 
Unit Testing SQL Server
Giovanni Scerra ☃
 
Oracle SQL Tuning
Alex Zaballa
 
Proactive performance monitoring with adaptive thresholds
John Beresniewicz
 
SQL Server Performance Tuning Baseline
► Supreme Mandal ◄
 
Oracle db performance tuning
Simon Huang
 

Similar to Uncovering SQL Server query problems with execution plans - Tony Davis (20)

PPTX
Do You Have the Time
Michael Antonovich
 
PPTX
Chris Seebacher Portfolio
guest3ea163
 
PPT
Kevin Bengtson Portfolio
Kbengt521
 
PDF
When to NoSQL and when to know SQL
Simon Elliston Ball
 
PPTX
Relational Database to Apache Spark (and sometimes back again)
Ed Thewlis
 
PPT
My Portfolio
guest0ff0f54d
 
PPT
My Portfolio
dmcglasson
 
PPTX
Business Intelligence Portfolio
Chris Seebacher
 
PDF
Social media analytics using Azure Technologies
Koray Kocabas
 
PDF
Lucian Precup - Back to the Future: SQL 92 for Elasticsearch? - NoSQL matters...
NoSQLmatters
 
PDF
Real Time Analytics with Apache Cassandra - Cassandra Day Munich
Guido Schmutz
 
PPTX
U-SQL Query Execution and Performance Tuning
Michael Rys
 
PPTX
Moving from SQL Server to MongoDB
Nick Court
 
PPTX
SQL Server 2008 Portfolio
lilredlokita
 
PDF
SQLBits Module 2 RStats Introduction to R and Statistics
Jen Stirrup
 
PPTX
Data stax webinar cassandra and titandb insights into datastax graph strategy...
DataStax
 
DOCX
CMS Project Phase II InstructionsIn this phase, you will create t.docx
mary772
 
PPT
Greg Lewis SQL Portfolio
gregmlewis
 
PPTX
Back to the future : SQL 92 for Elasticsearch @nosqlmatters Paris
Lucian Precup
 
PPTX
SQL Windowing
Sandun Perera
 
Do You Have the Time
Michael Antonovich
 
Chris Seebacher Portfolio
guest3ea163
 
Kevin Bengtson Portfolio
Kbengt521
 
When to NoSQL and when to know SQL
Simon Elliston Ball
 
Relational Database to Apache Spark (and sometimes back again)
Ed Thewlis
 
My Portfolio
guest0ff0f54d
 
My Portfolio
dmcglasson
 
Business Intelligence Portfolio
Chris Seebacher
 
Social media analytics using Azure Technologies
Koray Kocabas
 
Lucian Precup - Back to the Future: SQL 92 for Elasticsearch? - NoSQL matters...
NoSQLmatters
 
Real Time Analytics with Apache Cassandra - Cassandra Day Munich
Guido Schmutz
 
U-SQL Query Execution and Performance Tuning
Michael Rys
 
Moving from SQL Server to MongoDB
Nick Court
 
SQL Server 2008 Portfolio
lilredlokita
 
SQLBits Module 2 RStats Introduction to R and Statistics
Jen Stirrup
 
Data stax webinar cassandra and titandb insights into datastax graph strategy...
DataStax
 
CMS Project Phase II InstructionsIn this phase, you will create t.docx
mary772
 
Greg Lewis SQL Portfolio
gregmlewis
 
Back to the future : SQL 92 for Elasticsearch @nosqlmatters Paris
Lucian Precup
 
SQL Windowing
Sandun Perera
 
Ad

More from Red Gate Software (20)

PDF
The future of DevOps: fully left-shifted deployments with version control and...
Red Gate Software
 
PDF
Embracing DevOps through database migrations with Flyway
Red Gate Software
 
PPTX
Database DevOps for Managed Service Providers
Red Gate Software
 
PDF
Mizuho Financial: Launching our Database DevOps journey
Red Gate Software
 
PDF
7 steps to effective SQL Server monitoring
Red Gate Software
 
PDF
Level up your deployments for SQL Source Control
Red Gate Software
 
PDF
Key findings from the 2020 state of database dev ops report
Red Gate Software
 
PPTX
Extend DevOps to Your SQL Server Databases
Red Gate Software
 
PDF
2019 year in review slides
Red Gate Software
 
PPTX
What we learned at PASS Summit in 2019
Red Gate Software
 
PPTX
Quality in Software Development: Anglia Ruskin University
Red Gate Software
 
PPTX
How SQL Change Automation helps you deliver value faster
Red Gate Software
 
PPTX
DevOps essentials from Abel Wang and Steve Jones
Red Gate Software
 
PPTX
Successfully migrating existing databases to Azure
Red Gate Software
 
PPTX
The Ultimate Guide to Choosing and Implementing the Right Monitoring Tool
Red Gate Software
 
PDF
Everything You Need to Know About the 2019 DORA Accelerate State of DevOps Re...
Red Gate Software
 
PDF
Using Redgate, AKS and Azure to bring DevOps to your database
Red Gate Software
 
PDF
Using Redgate, AKS and Azure to bring DevOps to your Database
Red Gate Software
 
PDF
How to Pitch a Software Development Initiative and Ignite Culture Change
Red Gate Software
 
PDF
Taming the Wild West
Red Gate Software
 
The future of DevOps: fully left-shifted deployments with version control and...
Red Gate Software
 
Embracing DevOps through database migrations with Flyway
Red Gate Software
 
Database DevOps for Managed Service Providers
Red Gate Software
 
Mizuho Financial: Launching our Database DevOps journey
Red Gate Software
 
7 steps to effective SQL Server monitoring
Red Gate Software
 
Level up your deployments for SQL Source Control
Red Gate Software
 
Key findings from the 2020 state of database dev ops report
Red Gate Software
 
Extend DevOps to Your SQL Server Databases
Red Gate Software
 
2019 year in review slides
Red Gate Software
 
What we learned at PASS Summit in 2019
Red Gate Software
 
Quality in Software Development: Anglia Ruskin University
Red Gate Software
 
How SQL Change Automation helps you deliver value faster
Red Gate Software
 
DevOps essentials from Abel Wang and Steve Jones
Red Gate Software
 
Successfully migrating existing databases to Azure
Red Gate Software
 
The Ultimate Guide to Choosing and Implementing the Right Monitoring Tool
Red Gate Software
 
Everything You Need to Know About the 2019 DORA Accelerate State of DevOps Re...
Red Gate Software
 
Using Redgate, AKS and Azure to bring DevOps to your database
Red Gate Software
 
Using Redgate, AKS and Azure to bring DevOps to your Database
Red Gate Software
 
How to Pitch a Software Development Initiative and Ignite Culture Change
Red Gate Software
 
Taming the Wild West
Red Gate Software
 
Ad

Recently uploaded (20)

PDF
Humans vs AI Call Agents - Qcall.ai's Special Report
Udit Goenka
 
PDF
The Rise of Sustainable Mobile App Solutions by New York Development Firms
ostechnologies16
 
PDF
What Is an Internal Quality Audit and Why It Matters for Your QMS
BizPortals365
 
PDF
Writing Maintainable Playwright Tests with Ease
Shubham Joshi
 
PPTX
IObit Uninstaller Pro 14.3.1.8 Crack Free Download 2025
sdfger qwerty
 
PDF
Code Once; Run Everywhere - A Beginner’s Journey with React Native
Hasitha Walpola
 
PDF
AI Software Development Process, Strategies and Challenges
Net-Craft.com
 
PPTX
Avast Premium Security crack 25.5.6162 + License Key 2025
HyperPc soft
 
PDF
Which Hiring Management Tools Offer the Best ROI?
HireME
 
PDF
capitulando la keynote de GrafanaCON 2025 - Madrid
Imma Valls Bernaus
 
PDF
Alur Perkembangan Software dan Jaringan Komputer
ssuser754303
 
PDF
CodeCleaner: Mitigating Data Contamination for LLM Benchmarking
arabelatso
 
PPTX
Agentforce – TDX 2025 Hackathon Achievement
GetOnCRM Solutions
 
PPTX
For my supp to finally picking supp that work
necas19388
 
PDF
CodeCleaner: Mitigating Data Contamination for LLM Benchmarking
arabelatso
 
PDF
Best Practice for LLM Serving in the Cloud
Alluxio, Inc.
 
PDF
Mastering VPC Architecture Build for Scale from Day 1.pdf
Devseccops.ai
 
PDF
Why Edge Computing Matters in Mobile Application Tech.pdf
IMG Global Infotech
 
PPTX
Wondershare Filmora Crack 14.5.18 + Key Full Download [Latest 2025]
HyperPc soft
 
PDF
Designing Accessible Content Blocks (1).pdf
jaclynmennie1
 
Humans vs AI Call Agents - Qcall.ai's Special Report
Udit Goenka
 
The Rise of Sustainable Mobile App Solutions by New York Development Firms
ostechnologies16
 
What Is an Internal Quality Audit and Why It Matters for Your QMS
BizPortals365
 
Writing Maintainable Playwright Tests with Ease
Shubham Joshi
 
IObit Uninstaller Pro 14.3.1.8 Crack Free Download 2025
sdfger qwerty
 
Code Once; Run Everywhere - A Beginner’s Journey with React Native
Hasitha Walpola
 
AI Software Development Process, Strategies and Challenges
Net-Craft.com
 
Avast Premium Security crack 25.5.6162 + License Key 2025
HyperPc soft
 
Which Hiring Management Tools Offer the Best ROI?
HireME
 
capitulando la keynote de GrafanaCON 2025 - Madrid
Imma Valls Bernaus
 
Alur Perkembangan Software dan Jaringan Komputer
ssuser754303
 
CodeCleaner: Mitigating Data Contamination for LLM Benchmarking
arabelatso
 
Agentforce – TDX 2025 Hackathon Achievement
GetOnCRM Solutions
 
For my supp to finally picking supp that work
necas19388
 
CodeCleaner: Mitigating Data Contamination for LLM Benchmarking
arabelatso
 
Best Practice for LLM Serving in the Cloud
Alluxio, Inc.
 
Mastering VPC Architecture Build for Scale from Day 1.pdf
Devseccops.ai
 
Why Edge Computing Matters in Mobile Application Tech.pdf
IMG Global Infotech
 
Wondershare Filmora Crack 14.5.18 + Key Full Download [Latest 2025]
HyperPc soft
 
Designing Accessible Content Blocks (1).pdf
jaclynmennie1
 

Uncovering SQL Server query problems with execution plans - Tony Davis

  • 1. Uncovering SQL Server Query Problems with Execution Plans Tony Davis [email protected] @tonytheeditor #SQLintheCityUK
  • 2. About Me Books SQL Server transaction log SQL Server Source Control Speaker #SQLintheCityUK
  • 3. SELECT ProductID , ProductNumber , dbo.LineItemTotal(ProductID) AS SumTotal FROM Production.Product p GO SELECT bth.ProductID , bth.TransactionDate , bth.Quantity , bth.ActualCost FROM dbo.bigTransactionHistory bth WHERE bth.TransactionDate >= '20100701' AND bth.TransactionDate <= CURRENT_TIMESTAMP ORDER BY bth.TransactionDate DESC; GO Why Bother Learning Execution Plans? WITH Totals AS ( SELECT DATEADD(m, u.theMonth, 0) AS TheMonth , SUM(CASE WHEN u.theCol = 'DateJoined' THEN u.Registrations ELSE 0 END) AS PeopleJoined , SUM(CASE WHEN u.theCol = 'DateLeft' THEN u.Registrations ELSE 0 END) AS PeopleLeft FROM ( SELECT DATEDIFF(MONTH, 0, DateJoined) AS DateJoined , DATEDIFF(MONTH, 0, DateLeft) AS DateLeft , COUNT(*) AS Registrations FROM dbo.Registrations2 GROUP BY DATEDIFF(MONTH, 0, DateJoined) , DATEDIFF(MONTH, 0, DateLeft) ) AS d UNPIVOT ( theMonth FOR theCol IN ( d.DateJoined, d.DateLeft ) ) AS u GROUP BY u.theMonth HAVING SUM(CASE WHEN u.theCol = 'DateJoined' THEN u.Registrations ELSE 0 END) > 0 ) SELECT TheMonth , PeopleJoined , PeopleLeft , SUM(PeopleJoined - PeopleLeft) OVER ( ORDER BY TheMonth ROWS UNBOUNDED PRECEDING ) AS CurrentSubscribers FROM Totals;
  • 4. From Query to Execution plan SELECT bth.ProductID , bth.TransactionDate , bth.Quantity , bth.ActualCost FROM dbo.bigTransactionHistory bth WHERE bth.TransactionDate >= '20100701' AND bth.TransactionDate <= CURRENT_TIMESTAMP ORDER BY bth.TransactionDate DESC; GO #SQLintheCityUK
  • 5. Plan 1 Plan 2 Plan 3 Plan 4 Plan 57 Plan n? Algebrizer (Query Binding) Syntax Checking (Query Parsing) Query Optimization Plan Query Execution Engine Plan Cache Metadata Table/index structures SELECT bth.ProductID , bth.TransactionDate , bth.Quantity , bth.ActualCost FROM dbo.bigTransactionHistory bth WHERE bth.TransactionDate >= '20100701' AND bth.TransactionDate <= CURRENT_TIMESTAMP ORDER BY bth.TransactionDate DESC; GO Index/Column Statistics Volume/distribution of data #SQLintheCityUK
  • 6. Getting the Execution Plan .NET Code Profiler - e.g. ANTS Performance Profiler Get plans for previously executed queries from the plan cache - e.g. SQL Trace or Extended Events - Using sys.dm_exec_cached_plans Plans in cache contain no runtime information  #SQLintheCityUK
  • 8. Getting the Execution Plan .NET Code Profiler - e.g. ANTS Performance Profiler Get plans for previously executed queries from the plan cache - e.g. SQL Trace or Extended Events - Using sys.dm_exec_cached_plans Plans in cache contain no runtime information  SET During testing, request the plan for a query • “estimated” plan – no runtime • “actual” plan - with runtime There is only 1 plan!  #SQLintheCityUK
  • 10. Execution Plans for Developers • Don’t examine plan for every query • Gather stats, focus on critical, frequent, resource- intensive queries • Sometimes the code logic is just wrong – rip it up and start again! • Sometimes the basic logic is fine – But some subtler problem causes poor execution performance – This is where execution plans can help! #SQLintheCityUK
  • 11. • With SQL Server: • We submit SQL describing the data set we want • The Optimizer decides how to execute it • Some developers bring imperative approach line- by-line control to SQL Server… Problem: Row-by-row strategy #SQLintheCityUK
  • 32. Solution: set-based approach – Take the list to the supermarket ;) – Define single statement (if possible) to return required data set • Make as few ‘passes’ through the base tables as possible • Aggregate early • Reduce the working set to as few rows as possible #SQLintheCityUK
  • 33. WITH Totals AS ( SELECT DATEADD(m, u.theMonth, 0) AS TheMonth , SUM(CASE WHEN u.theCol = 'DateJoined' THEN u.Registrations ELSE 0 END) AS PeopleJoined , SUM(CASE WHEN u.theCol = 'DateLeft' THEN u.Registrations ELSE 0 END) AS PeopleLeft FROM ( SELECT DATEDIFF(MONTH, 0, DateJoined) AS DateJoined , DATEDIFF(MONTH, 0, DateLeft) AS DateLeft , COUNT(*) AS Registrations FROM dbo.Registrations2 GROUP BY DATEDIFF(MONTH, 0, DateJoined) , DATEDIFF(MONTH, 0, DateLeft) ) AS d UNPIVOT ( theMonth FOR theCol IN ( d.DateJoined, d.DateLeft ) ) AS u GROUP BY u.theMonth HAVING SUM(CASE WHEN u.theCol = 'DateJoined' THEN u.Registrations ELSE 0 END) > 0 ) SELECT TheMonth , PeopleJoined , PeopleLeft , SUM(PeopleJoined - PeopleLeft) OVER ( ORDER BY TheMonth ROWS UNBOUNDED PRECEDING ) AS CurrentSubscribers FROM Totals; Classic Running Total Problem A Registrations table containing a list of subscribers, with the dates that the subscribers joined and left #SQLintheCityUK
  • 34. WITH Totals AS ( SELECT DATEADD(m, u.theMonth, 0) AS TheMonth , SUM(CASE WHEN u.theCol = 'DateJoined' THEN u.Registrations ELSE 0 END) AS PeopleJoined , SUM(CASE WHEN u.theCol = 'DateLeft' THEN u.Registrations ELSE 0 END) AS PeopleLeft FROM ( SELECT DATEDIFF(MONTH, 0, DateJoined) AS DateJoined , DATEDIFF(MONTH, 0, DateLeft) AS DateLeft , COUNT(*) AS Registrations FROM dbo.Registrations2 GROUP BY DATEDIFF(MONTH, 0, DateJoined) , DATEDIFF(MONTH, 0, DateLeft) ) AS d UNPIVOT ( theMonth FOR theCol IN ( d.DateJoined, d.DateLeft ) ) AS u GROUP BY u.theMonth HAVING SUM(CASE WHEN u.theCol = 'DateJoined' THEN u.Registrations ELSE 0 END) > 0 ) SELECT TheMonth , PeopleJoined , PeopleLeft , SUM(PeopleJoined - PeopleLeft) OVER ( ORDER BY TheMonth ROWS UNBOUNDED PRECEDING ) AS CurrentSubscribers FROM Totals; A single ‘pass’ of the base table Early Aggregation 10000001690 Execution Plan #SQLintheCityUK
  • 35. Problem: Excessive logical reads • “Read 1000 pages to return 100 rows” – 1 page = 1 logical read – Goal: return data with few logical reads as possible – Sometimes we force SQL Server to read more pages than necessary (excessive IO) Inefficient data access paths (indexes) Inefficient SQL forcing SQL Server to do excessive work  #SQLintheCityUK
  • 36. Name (Index Key) A - Z + LocationID Name (Index Key) A - E + LocationID Name (Index Key) F - I + LocationID Name (Index Key) J - M + LocationID Name (Index Key) N - R + LocationID Name (Index Key) S - V + LocationID Name (Index Key) W - Z + LocationID Root Node Intermediate Level Leaf Level Name (Index Key) A - M + LocationID Name (Index Key) N - Z + LocationID WHERE l.Name ='Paint';Non-clustered Index #SQLintheCityUK
  • 37. Name (Index Key) A - Z + LocationID Name (Index Key) A - E + LocationID Name (Index Key) F - I + LocationID Name (Index Key) J - M + LocationID Name (Index Key) N - R + LocationID Name (Index Key) S - V + LocationID Name (Index Key) W - Z + LocationID Root Node Intermediate Level Leaf Level Name (Index Key) A - M + LocationID Name (Index Key) N - Z + LocationID WHERE l.Name ='Paint';Non-clustered Index #SQLintheCityUK
  • 38. LocationID = 6; (ProductID, LocationID) (Clustering Key) 1 to 12000 (ProductID, LocationID) 1-6000 (ProductID, LocationID) 6001-12000 Data rows for ProductID 1-2000 Data rows for ProductID 2001-4000 Data rows for ProductID 4001-6000 Data rows for ProductID 6001-8000 Data rows for ProductID 80001-10000 Data rows for ProductID 10001-12000 Root Node Intermediate Level Leaf Level(502,6) (4325,6) (10324,6)(7865,6) Clustered Index #SQLintheCityUK
  • 39. LocationID = 6; (ProductID, LocationID) (Clustering Key) 1 to 12000 (ProductID, LocationID) 1-6000 (ProductID, LocationID) 6001-12000 Data rows for ProductID 1-2000 Data rows for ProductID 2001-4000 Data rows for ProductID 4001-6000 Data rows for ProductID 6001-8000 Data rows for ProductID 80001-10000 Data rows for ProductID 10001-12000 Root Node Intermediate Level Leaf Level Clustered Index #SQLintheCityUK
  • 40. LocationID = 6; (ProductID, LocationID) (Clustering Key) 1 to 12000 (ProductID, LocationID) 1-6000 (ProductID, LocationID) 6001-12000 Data rows for ProductID 1-2000 Data rows for ProductID 2001-4000 Data rows for ProductID 4001-6000 Data rows for ProductID 6001-8000 Data rows for ProductID 80001-10000 Data rows for ProductID 10001-12000 Root Node Intermediate Level Leaf Level #SQLintheCityUK
  • 42. ProductID (Index Key) A - Z + SalesOrderID ProductID (Index Key) 1 - 150 + SalesOrderID ProductID (Index Key) 151 - 300 + SalesOrderID ProductID (Index Key) 301 - 450 + SalesOrderID ProductID (Index Key) 451 - 600 + SalesOrderID ProductID (Index Key) 601 - 750 + SalesOrderID ProductID (Index Key) 751 - 900 + SalesOrderID Root Node Intermediate Level Leaf Level ProductID (Index Key) A - M + SalesOrderID ProductID (Index Key) N - Z + SalesOrderID Lookup CarrierTrackingNumber in clustered index for each row Key Lookup SELECT ProductID , SalesOrderID , CarrierTrackingNumber FROM Sales.SalesOrderDetail WHERE ProductID = 709; #SQLintheCityUK
  • 44. #SQLintheCityUK • For small tables, scanning an index is very efficient operation • BUT…for critical and frequently-executed queries: – Provide covering non-clustered indexes that the queries can seek – Minimize logical reads (IO) required to gather the data Solution: optimize indexes for workloadSolution: optimize indexes for workload
  • 45. #SQLintheCityUK –Worst Strategy: No indexes. Query performance will be terrible –Second worst: Index on every column; same column participating in numerous indexes Data modification performance will be awful –Don’t do “SELECT *” Makes effective indexing VERY hard! Solution: optimize indexes for workloadSolution: optimize indexes for workload
  • 46. #SQLintheCityUK • Don’t index query by query • Goal: small set of indexes to help most important and frequent queries • Analyze the workload as a whole – Profiler and Database Engine Tuning Advisor • Good start point… • Not necessarily good end point – Refine recommendations using Missing Index (and other) DMVs • What is the write : read ratio (>1?) for the index? • How many plans currently associated with index? • How often are those plans used? • How many reads might the missing index have helped? Don’t create if this number is small… – See Performance Tuning with SQL Server Dynamic Management Views (free eBook) • http://bit.ly/1jVG3IW Solution: optimize indexes for workload
  • 47. “The explicit data type conversion has rendered the predicate non-SARGable!” Problem: Non-SARGable Predicates #SQLintheCityUK
  • 48. Beware of SARG-ability • Non-SARGable means that the optimizer can’t use the expression in a seek operation • Cause: use of function directly on column in WHERE or JOIN – Various incarnations – All lead to excessive IO (scans when seeks should be viable) #SQLintheCityUK
  • 49. SARG-able predicate …WHERE FirstName = 'bob';  SARGable FirstName LastName Aaron Bertrand Aaron Darrenovsky Amber Smith Apple Paltrow Bob Duffy Bob Carolgees Chris Christofferson …etc... #SQLintheCityUK
  • 50. Non SARGable predicate …WHERE REVERSE(FirstName) = 'bob'; FirstName LastName Aaron Bertrand Aaron Darrenovsky Amber Smith Apple Paltrow Bob Duffy Bob Carolgees Chris Christofferson …etc... REVERSE ('Aaron') = 'bob' ? #SQLintheCityUK
  • 51. Non SARG-able predicate …WHERE REVERSE(FirstName) = 'bob'; FirstName LastName Aaron Bertrand Aaron Darrenovsky Amber Smith Apple Paltrow Bob Duffy Bob Carolgees Chris Christofferson …etc... REVERSE ('Aaron') = 'bob' ? #SQLintheCityUK
  • 52. Non SARGable predicate …WHERE REVERSE(FirstName) = 'bob'; FirstName LastName Aaron Bertrand Aaron Darrenovsky Amber Smith Apple Paltrow Bob Duffy Bob Carolgees Chris Christofferson …etc... REVERSE ('Amber') = 'bob' ?  Non-SARGable #SQLintheCityUK
  • 54. Other common explicit conversions -- Using functions like LTRIM, RTRIM etc. -- often happens where people don't trust consistency of data inputs SELECT bp.ProductID , bp.Name , bp.ProductNumber FROM dbo.bigProduct bp WHERE LTRIM(bp.ProductNumber) LIKE 'AR%'; GO SELECT bp.ProductID , bp.Name , bp.ProductNumber FROM dbo.bigProduct bp WHERE bp.ProductNumber LIKE N'%1000'; GO #SQLintheCityUK
  • 55. Other causes of Non-SARG • Implicit Data type conversions – Variable data type doesn’t match column type – SQL Server uses CONVERT_IMPLICIT – Problem if column is lower precedence type • Data type precedence order: http://bit.ly/1ENOwjZ • Misuse of User Defined Functions (UDFs) #SQLintheCityUK
  • 56. • Rewrite the query to avoid direct use of function on column • Use helper functions • Avoid data type mismatches • Be careful when using UDFs! – Convert scalar UDFs to inline TVFs – http://bit.ly/1oN9ysK Solutions to Non-SARGability #SQLintheCityUK
  • 57. • One query size fits all • Leads to wildly inaccurate estimations and inappropriate execution plans Problem: Overly Generic SQL #SQLintheCityUK
  • 59. • Write SQL and stored procedures for a specific, defined purpose • Various ‘workarounds’ though none without drawbacks – Recompile on each execution – Dynamic SQL – See Gail Shaw’s “How to Confuse the Query Optimizer” (http://bit.ly/1Mb8Rnp) Solutions to Generic SQL #SQLintheCityUK
  • 60. Other issues that cause poor estimations • Stale statistics • Problems with parameter sniffing #SQLintheCityUK
  • 62. Conclusions • Plans tell us exactly how SQL Server executed your query – Focus on critical and frequently-executed queries – Use plans to: • spot common mistakes in the code • Uncover problems with inefficient indexing – Work with rather than against SQL Server #SQLintheCityUK
  • 63. Thank you to… • Grant Fritchey - @GFritchey • Hugo Kornelis - @Hugo_Kornelis • Gail Shaw - @SQLintheWild • Rodney Landrum - @SQLBeat • Phil Factor - @Phil_Factor #SQLintheCityUK