This document provides 25 tips for SQL Server performance tuning, SSIS, SSRS, SSAS and other Microsoft data tools. The tips cover topics like query shortcuts, code snippets, report formatting, finding hardware problems, windowing functions, TempDB configuration, dates tables, and more. The document is authored by Ike Ellis, an MVP, blogger and YouTube content creator focused on SQL Server and related Microsoft data tools.
Polkadot JAM Slides - Token2049 - By Dr. Gavin Wood
Tips & Tricks SQL in the City Seattle 2014
1. Tips & Tricks with SQL Server
Performance Tuning, SSAS, SSRS,
SSIS, and More!
By Ike Ellis, MVP
@ike_ellis
www.ikeellis.com
Blog.ikeellis.com
http://www.linkedin.com/in/ikeellis
2. So you want to be great at SQL
Server… SQL Server Integration Services
SQL Server Analysis Services
Tabular
MultiDimensional
SQL Server Reporting Services
Excel
Data Quality Services
Service Broker
Performance Tuning
Indexing
Query Plans
Plan Analysis
Memory Management
SANs
Network
Clustering
Availability Groups
PowerShell
Master Data Services
Architecture
Data Mart Design
Data Normalization
CDC
NoSQL/BigData (At least the MS Cloud Offerings)
Competitive Knowledge (Oracle, Tablaeu, QlickView,
Postgres)
ORMs(Entity Framework, Nhibernate, Micros)
Installation/Configuration/Upgrading/Service Packing
Power BI
PowerMap
PowerQuery
PowerView
PowerPivot
T-SQL
Querying
Stored Procedures
Functions
Windowing Functions
Aggregates
CLR
MDX
DAX
XMLA
BCP
SQL Azure
Tooling
Redgate
SSMS
SSDT
Past Versions
Central Management
DacPacs/BacPacs
Profiler/Extended Events
Auditing
Security/Encryption
Replication
SQLCMD
3. Tips From the SQL Consultant
• For the YouTube/Reddit/Chive/Cracked/Meme generation
• Lots of disjointed tips
• Popular mistakes I see or easy things I think you can take
advantage of
• Between 3 – 5 minutes each
• Let’s see if we can get through all 20
4. TIP #1: Query Shortcuts
• SELECT COUNT(*) FROM
• SELECT TOP 100 * FROM
6. TIP #3: Low hanging fruit
• CRTL-R
– F6 switches between result tabs
• Copy, Paste a line without selecting
• COMMENTS CTRL-K, C, CTRL-K U
• BOOKMARKS CTRL-K, K, CTRL-K N
• Refresh cache – CTRL-SHIFT R
– Unless RedGate then CTRL-SHIFT D
7. TIP #4: Block Commenting
• Easy column selection – bring up the query window
– CTRL-SHIFT Q
• Format the columns comma first, like you’re supposed to
– SQL PROMPT – CTRL-K, CTRL-Y
• Alias a table
• CTRL-ALT arrow
• Add alias
8. TIP #5: Code Snippets
• CTRL-K, CTRL-B – Bring up Code Snippet Manager
• Copy an existing one from a path and put it in the My Snippets
Path
• Edit the XML
• CTRL-K, CTRL-X to place the snippet
• Great for common queries
– WhoIsActive
– sp_Blitz
– DBCC OPENTRAN
10. Tip #7: Five minutes on report formatting = 10x more
impressive
• Spend 10 minutes on design (as opposed to the zero we
typically spend)
• Choose colors wisely
– 99/100 - developers use the default color palette
• HTML color picker websites
– http://www.lavishbootstrap.com
• MorgueFile
– http://www.morguefile.com/
11. Tip #8: The right way to find hardware
problems
• Merging PerfMon and Tracing
• Get the Batch and Completed Events Only
• Never trace from the computer you are monitoring
• Always trace to a file and then load in a table after.
11
*Thanks, Grant!
12. Tip #9: Lifehack: Readable Presentations
• Take the average age of the
people in your audience and
divide by 2: That’s your font
size
• USE THIS SIZE IF YOUR
AUDIENCE IS 110
13. Tip #10: Check for heaps/clustered
indexes
SELECT t.[Name] FROM sys.Indexes i
JOIN sys.Tables t
ON t.Object_ID = i.Object_id
WHERE i.type_desc = 'HEAP'
ORDER BY t.[Name]
15. Tip #12: No reason to use ISNULL
CONCAT!
• Messy vs clean code
• No + symbol needed
• No ISNULL needed
16. Tip #13: How to search schema
• F7
• SQLSearch
– Free
– Download it!
– http://www.red-gate.com/products/sql-development/sql-search/
– Did I mention it’s free?
• Dependency Tracker
– Not Free, but still cool
select object_name(object_id), definition as name
from sys.all_sql_modules
where definition like '%cust%'
17. Tip #14: Windowing Functions are pretty
cool
• They are worth learning, and have a neat evolution
18. Tip #15: SSDT Search for options
• No more digging around in options
• Just search for everything
19. Tip #16: Scripting: You have two
choices
• Two Choices
– Get good at boring repetitive tasks
– Get good at PowerShell & Scripting
• Who adds more value to their company or their customers?
• Who gets paid more?
20. Tip #17: TempDB Configuration
• Current thought is 4 logical processors to 1 file
• Just a good beginning, your mileage may very
• Start there, then go to 2:1 or 1:1 if necessary
• Trace Flag 1117 or autogrow off
21. Tip #18: Prettify!
21
http://extras.sqlservercentral.com/prettifier/prettifier.aspx
RedGate PlugIn for SQL Server Management Studio
23. Tip #20: Life is so easy with a dates
table
• Find the sales numbers for the first Monday of every month
of the year
• T-SQL with no dates table
• T-SQL with dates table
25. Tip #22: Never reinvent the wheel
• Take SQL# for example
• Good Documentation
• Easy Syntax
• Cheap (and much of it is free)
26. Tip #23: Save scripts for easy
access
• Lots of repetitive scripts with business logic
• No reason to write the same queries for the same tables
day after day
27. Tip #24: Enforce Business Rules in the
DB
• Foreign Keys
• Unique Constraints
• Check Constraints
27
28. Tip #25: Log, Log, Log (and beware of subscriptions)
select c.Name
, e.InstanceName
, e.UserName
, e.Parameters
, e.TimeStart
, e.TimeEnd
, e.TimeDataRetrieval
, e.TimeProcessing
, e.TimeRendering
from executionlog e
join catalog c
on e.reportid = c.ItemID
Send a Link, or a file on a shared folder that you can audit. Find someway to audit
who opened the link or the file in the folder. Try to avoid sending the PDF without
a way to audit it.
29. Ike Ellis
• http://blog.ikeellis.com
• http://www.ikeellis.com
• YouTube
– http://www.youtube.com/user/IkeEllisData
• SQL Pass Book Readers
– http://bookreaders.sqlpass.org/
• San Diego Tech Immersion Group
• Twitter: @ike_ellis
• 619.922.9801
• Email address is just my first name @ikeellis.com
Editor's Notes
use tsql2012
SELECT *
FROMSales.customers
go
select *
from sales.orders
go
select top 100000 *
from sales.orders, sales.customers
update sales.customers
set contacttitle = null
where custid in ('59', '66', '78', '15')
use tsql2012
select * from sales.customers
select ContactName
+ ' - ' + ContactTitle
+ ', ' + City as GreetingLine
from sales.customers
select isnull(ContactName, '')
+ ' - ' + isnull(ContactTitle, '')
+ ', ' + isnull(City, '') as GreetingLine
from sales.customers
select concat(ContactName, ' ', ContactTitle, ' ', City) as GreetingLine
from sales.customers
use tsql2012
select *
from sales.orders
select custid, freight
from sales.orders
select custid, freight, sum(freight) as totalFreight
from sales.orders
select custid, sum(freight) as totalFreight
from sales.orders
group by custid
select custid, freight, sum(freight) as totalFreight
from sales.orders
group by custid
select custid, freight, sum(freight) as totalFreight
from sales.orders
group by custid, freight
--we're skipping subqueries, because they look ugly and
--it insults me aesthetically
;with FreightTotal
as
(
select custid, sum(freight) as totalFreight
from sales.orders
group by custid
)
select o.custid
, o.freight
, ft.totalFreight as totalFreight
from sales.orders o
join FreightTotal ft
on o.custid = ft.custid
--BIG IMPROVEMENT
--query can be executed independantly
--Can be reused, like this
;with FreightTotal
as
(
select custid, sum(freight) as totalFreight
from sales.orders
group by custid
)
select o.custid
, o.freight
, ft.totalFreight as totalFreight
, o.freight /ft.totalfreight * 100 as FreightPercentage
from sales.orders o
join FreightTotal ft
on o.custid = ft.custid
--but remember our original thought process
select custid, freight, sum(freight) as totalFreight
from sales.orders
--here's a windowing function
select custid
, freight
, sum(freight) over (partition by custid) as totalFreight
from sales.orders
--and reusing it is not that big of a deal
select custid
, freight
, sum(freight) over (partition by custid) as totalFreight
, freight / sum(freight) over (partition by custid) * 100 as FreightPercentage
from sales.orders
# We can narrow down the list by filtering on the source
Get-EventLog Application |
Where-Object {$_.Source -like '*sql*' `
-and $_.EntryType -eq "Error" `
-and $_.TimeGenerated -ge ((Get-Date).AddHours(-96)) `
} |
Format-List
Write-Host "Press any key to continue ..."
$x = $host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")
Write-Host
Write-Host "A"
Write-Host "B"
Write-Host "C"
SELECT * FROM (SELECT urgency, name, phone, location, department, cc, status, case_manager, ip, case_manager_ei d, id_problem, id_problem_type, eid_author, title, body, date_created, date_modified FROM problems AS main INNER JOIN (SELECT id_problem as t_urgency_id_problem, node_value AS urgency FROM problem_nodes WHERE node_name = "urgency")t_urgency ON t_urgency.t_urgency_id_problem = main.id_problem INNER JOIN (SELECT id_problem as t_name_id_problem, node_value AS name FROM problem_nodes WHERE node_name = "name")t_name ON t_name.t_name_id_problem = main.id_problem INNER JOIN (SELECT id_problem as t_phone_id_problem, node_value AS phone FROM problem_nodes WHERE node_name = "phone")t_phone ON t_phone.t_phone_id_problem = main.id_problem INNER JOIN (SELECT id_problem as t_location_id_problem, node_value AS location FROM problem_nodes WHERE node_name = "location")t_location ON t_location.t_location_id_problem = main.id_problem INNER JOIN (SELECT id_problem as t_department_id_problem, node_value AS department FROM problem_nodes WHERE node_name = "department")t_department ON t_department.t_department_id_problem = main.id_problem INNER JOIN (SELECT id_problem as t_cc_id_problem, node_value AS cc FROM problem_nodes WHERE node_name = "cc")t_cc ON t_cc.t_cc_id_problem = main.id_problem INNER JOIN (SELECT id_problem as t_status_id_problem, node_value AS status FROM problem_nodes WHERE node_name = "status")t_status ON t_status.t_status_id_problem = main.id_problem INNER JOIN (SELECT id_problem as t_case_manager_id_problem, node_value AS case_manager FROM problem_nodes WHERE node_name = "case_manager")t_case_manager ON t_case_manager.t_case_manager_id_problem = main.id_problem INNER JOIN (SELECT id_problem as t_ip_id_problem, node_value AS ip FROM problem_nodes WHERE node_name = "ip")t_ip ON t_ip.t_ip_id_problem = main.id_problem INNER JOIN (SELECT id_problem as t_case_manager_eid_id_problem, node_value AS case_manager_eid FROM problem_nodes WHERE node_name = "case_manager_eid") t_case_manager_eid ON t_case_manager_eid.t_case_manager_eid_id_problem = main.id_problem)t
;with FirstMondays
as
(
Select DateAdd(day, (9-DatePart(weekday,
DateAdd(Month, 1+DateDiff(Month, 0, '12/1/2009'), 0)))%7,
DateAdd(Month, 1+DateDiff(Month, 0, '12/1/2009'), 0)) as FirstMonday
union all
select DateAdd(day, (9-DatePart(weekday,
DateAdd(Month, 1+DateDiff(Month, 0, FirstMonday), 0)))%7,
DateAdd(Month, 1+DateDiff(Month, 0, FirstMonday), 0))
from FirstMondays
where firstMonday <= '12/1/2010'
)
select * from FirstMondays
use adventureworksdw2012
select EnglishMonthName, min(datekey)
from dimdate
where englishdaynameofweek = 'monday'
and calendaryear = '2010'
group by EnglishMonthName, MonthNumberOfYear
order by MonthNumberOfYear
create table t1
(col1 varchar(100))
insert into t1
(col1)
values
('1')
,('2')
,('3')
,('ike')
,('1')
,('2')
,('3')
,('ike')
, ('1')
,('2')
,('3')
,('ike')
select cast(col1 as int) from t1
select try_cast(col1 as int) from t1
Watch the actual execution plan for these statements:
drop table dbo.t1
drop table dbo.t2
--create two test tables
create table dbo.t1
(c1 int
, c2 int check(c2 between 10 and 20));
insert into dbo.t1
values
(11,12);
create table dbo.t2
(c1 int
, c2 int);
go
insert into dbo.t2
values
(101, 102);
go
select t1.c1
, t2.c2
, t2.c2
from dbo.t1
join dbo.t2
on t1.c1 = t2.c2
and t1.c2 = 20;
select t1.c1
, t1.c2
, t2.c2
from dbo.t1
join dbo.t2
on t1.c1 = t2.c2
and t1.c2 = 30;
DBCC OPENTRAN
select s.plan_handle
, t.text
, sum(s.execution_count) as totalExecutionCount
, sum(s.total_elapsed_time) as totalElapsedTime
, sum(s.total_worker_time) as totalWorkerTime
, sum(s.total_logical_reads) as totalLogicalReads
, sum(s.total_logical_writes) as totalLogicalWrites
from sys.dm_exec_query_stats s
cross apply sys.dm_exec_sql_text(s.plan_handle) t
group by s.plan_handle, t.text
order by sum(s.execution_count) desc