If this is the kind of SQL Server stuff you love learning about, you’ll love my training. I’m offering a 75% discount to my blog readers if you click from here. I’m also available for consulting if you just don’t have time for that, and need to solve database performance problems quickly. You can also get a quick, low cost health check with no phone time required.
If the Gods of post scheduling cooperate, this should publish in October. That means you’ve got a little more than a month to buy tickets and book travel to PASS.
Searching the internet for every problem isn’t cutting it.
You need to be more proactive and efficient when it comes to finding and solving database performance fires. I work with consulting customers around the world to put out SQL Server performance fires.
In this day of learning, I will teach you how to find and fix your worst SQL Server problems using the same modern tools and techniques which I use every week.
You’ll learn tons of new and effective approaches to common performance problems, how to figure out what’s going on in your query plans, and how indexes really work to make your queries faster.
Together, we’ll tackle query rewrites, batch mode, how to design indexes, and how to gather all the information you need to analyze performance.
This day of learning will teach you cutting edge techniques which you can’t find in training by folks who don’t spend time in the real world tuning performance.
Performance tuning mysteries can easily leave you stumbling through your work week, unsure if you’re focusing on the right things.
You’ll walk out of this class confident in your abilities to fix performance issues once and for all.
If you want to put out SQL Server performance fires, this is the precon you need to attend. Anyone can have a plan, it takes a professional to have a blueprint.
In-person attendees will also get a cool t-shirt. Arguably the coolest t-shirt ever made for a SQL Server conference.
Dates And Times
The PASS Data Community Summit is taking place in Seattle November 15-18, 2022 and online.
You can register here, to attend online or in-person. I’ll be there in all my fleshy goodness, and I hope to see you there too!
Thanks for reading!
Going Further
If this is the kind of SQL Server stuff you love learning about, you’ll love my training. I’m offering a 75% discount to my blog readers if you click from here. I’m also available for consulting if you just don’t have time for that, and need to solve database performance problems quickly. You can also get a quick, low cost health check with no phone time required.
If this is the kind of SQL Server stuff you love learning about, you’ll love my training. I’m offering a 75% discount to my blog readers if you click from here. I’m also available for consulting if you just don’t have time for that, and need to solve database performance problems quickly. You can also get a quick, low cost health check with no phone time required.
If this is the kind of SQL Server stuff you love learning about, you’ll love my training. I’m offering a 75% discount to my blog readers if you click from here. I’m also available for consulting if you just don’t have time for that, and need to solve database performance problems quickly. You can also get a quick, low cost health check with no phone time required.
If this is the kind of SQL Server stuff you love learning about, you’ll love my training. I’m offering a 75% discount to my blog readers if you click from here. I’m also available for consulting if you just don’t have time for that, and need to solve database performance problems quickly. You can also get a quick, low cost health check with no phone time required.
If this is the kind of SQL Server stuff you love learning about, you’ll love my training. I’m offering a 75% discount to my blog readers if you click from here. I’m also available for consulting if you just don’t have time for that, and need to solve database performance problems quickly. You can also get a quick, low cost health check with no phone time required.
If the Gods of post scheduling cooperate, this should publish in October. That means you’ve got a little more than a month to buy tickets and book travel to PASS.
Searching the internet for every problem isn’t cutting it.
You need to be more proactive and efficient when it comes to finding and solving database performance fires. I work with consulting customers around the world to put out SQL Server performance fires.
In this day of learning, I will teach you how to find and fix your worst SQL Server problems using the same modern tools and techniques which I use every week.
You’ll learn tons of new and effective approaches to common performance problems, how to figure out what’s going on in your query plans, and how indexes really work to make your queries faster.
Together, we’ll tackle query rewrites, batch mode, how to design indexes, and how to gather all the information you need to analyze performance.
This day of learning will teach you cutting edge techniques which you can’t find in training by folks who don’t spend time in the real world tuning performance.
Performance tuning mysteries can easily leave you stumbling through your work week, unsure if you’re focusing on the right things.
You’ll walk out of this class confident in your abilities to fix performance issues once and for all.
If you want to put out SQL Server performance fires, this is the precon you need to attend. Anyone can have a plan, it takes a professional to have a blueprint.
In-person attendees will also get a cool t-shirt. Arguably the coolest t-shirt ever made for a SQL Server conference.
Dates And Times
The PASS Data Community Summit is taking place in Seattle November 15-18, 2022 and online.
You can register here, to attend online or in-person. I’ll be there in all my fleshy goodness, and I hope to see you there too!
Thanks for reading!
Going Further
If this is the kind of SQL Server stuff you love learning about, you’ll love my training. I’m offering a 75% discount to my blog readers if you click from here. I’m also available for consulting if you just don’t have time for that, and need to solve database performance problems quickly. You can also get a quick, low cost health check with no phone time required.
I’m still looking into this a bit, but I ran into this issue when helping set up a process to migrate data to a table because the original table was running out of integer identity values.
The process looks something like:
Create a new table with the right definition
Write a loop to backfill existing table data
Create a trigger to keep updated data synchronized
Eventually swap the tables during a short maintenance window
All well and good! Except… You can end up with a really weird execution plan for the backfill process, and some pretty gnarly memory grants for wide tables.
Demonstrating with the Votes table:
CREATE TABLE
dbo.Votes_bigint
(
Id bigint IDENTITY NOT NULL,
PostId int NOT NULL,
UserId int NULL,
BountyAmount int NULL,
VoteTypeId int NOT NULL,
CreationDate datetime NOT NULL,
CONSTRAINT PK_Votes_bigint_Id
PRIMARY KEY CLUSTERED (Id ASC)
)
GO
INSERT
dbo.Votes_bigint
(
Id,
PostId,
UserId,
BountyAmount,
VoteTypeId,
CreationDate
)
SELECT TOP (100000)
v.Id,
v.PostId,
v.UserId,
v.BountyAmount,
v.VoteTypeId,
v.CreationDate
FROM dbo.Votes AS v
ORDER BY v.Id;
The query plan has a Sort in it, which is weird because… Both tables are primary key/clustered index on the same column. Why re-sort that data?
If this is the kind of SQL Server stuff you love learning about, you’ll love my training. I’m offering a 75% discount to my blog readers if you click from here. I’m also available for consulting if you just don’t have time for that, and need to solve database performance problems quickly. You can also get a quick, low cost health check with no phone time required.
CREATE EVENT SESSION keeper_HumanEvents_query
ON SERVER
ADD EVENT sqlserver.module_end
(SET collect_statement = 1
ACTION (sqlserver.database_name, sqlserver.sql_text, sqlserver.plan_handle, sqlserver.query_hash_signed, sqlserver.query_plan_hash_signed)
WHERE (
sqlserver.is_system = 0
AND duration >= 5000000
AND sqlserver.session_id = 60
)),
ADD EVENT sqlserver.rpc_completed
(SET collect_statement = 1
ACTION(sqlserver.database_name, sqlserver.sql_text, sqlserver.plan_handle, sqlserver.query_hash_signed, sqlserver.query_plan_hash_signed)
WHERE (
sqlserver.is_system = 0
AND duration >= 5000000
AND sqlserver.session_id = 60
)),
ADD EVENT sqlserver.sp_statement_completed
(SET collect_object_name = 1, collect_statement = 1
ACTION(sqlserver.database_name, sqlserver.sql_text, sqlserver.plan_handle, sqlserver.query_hash_signed, sqlserver.query_plan_hash_signed)
WHERE (
sqlserver.is_system = 0
AND duration >= 5000000
AND sqlserver.session_id = 60
)),
ADD EVENT sqlserver.sql_statement_completed
(SET collect_statement = 1
ACTION(sqlserver.database_name, sqlserver.sql_text, sqlserver.plan_handle, sqlserver.query_hash_signed, sqlserver.query_plan_hash_signed)
WHERE (
sqlserver.is_system = 0
AND duration >= 5000000
AND sqlserver.session_id = 60
)),
ADD EVENT sqlserver.query_post_execution_showplan
(
ACTION(sqlserver.database_name, sqlserver.sql_text, sqlserver.plan_handle, sqlserver.query_hash_signed, sqlserver.query_plan_hash_signed)
WHERE (
sqlserver.is_system = 0
AND duration >= 5000000
AND sqlserver.session_id = 60
))
I collect:
module_end
rpc_completed
sp_statement_completed
sql_statement_completed
query_post_execution_showplan
Not all of them are relevant to stored procedure calls, but in larger contexts where I have no idea where long running queries might be coming from, it’s useful to get all these.
Weirdness, Weirdness
Where I find things getting somewhat annoying is when things start showing up in there that meet the duration filter, but don’t really give me anything further to go on.
To simulate what I mean, I’m going to use this stored procedure:
CREATE OR ALTER PROCEDURE
dbo.Eventually
AS
BEGIN
SET NOCOUNT, XACT_ABORT ON;
WAITFOR DELAY '00:00:01.000';
SELECT TOP (1) * FROM dbo.Badges AS b;
WAITFOR DELAY '00:00:01.000';
SELECT TOP (1) * FROM dbo.Comments AS c;
WAITFOR DELAY '00:00:01.000';
SELECT TOP (1) * FROM dbo.Posts AS p;
WAITFOR DELAY '00:00:01.000';
SELECT TOP (1) * FROM dbo.Users AS u;
WAITFOR DELAY '00:00:01.000';
SELECT TOP (1) * FROM dbo.Votes AS v;
WAITFOR DELAY '00:00:01.000';
END;
GO
EXEC dbo.Eventually;
There are six waitfor commands that each pause for 1 second. In between them are queries that finish in milliseconds.
If I watch the event output, eventually, I’ll see this:
Okay, so the stored procedure took more than 5 seconds, but… no individual query took more than 5 seconds.
To troubleshoot further, I have to set the duration bar even lower, and then figure out what I can meaningfully tune.
Do I have one query that takes four seconds
Do I have ten queries that take 500 milliseconds
Do I have 500 queries that take ten milliseconds
I see this behavior quite a bit with queries that loop/cursor over tons of data. They might take a whole bunch of rows and do a lot of really fast queries over them, but the time adds up.
There’s not really a good solution for this, either. The closer you look, by reducing the duration filter to lower and lower values, the more you return, the more overhead you cause, and the longer you have to wait for things to finish.
Sure, you can wring someone’s neck about not “thinking in sets”, but that doesn’t help when the processing takes place by passing looped values to stored procedures, unless you have the freedom to make significant app changes by using table valued parameters or something instead.
Thanks for reading!
Going Further
If this is the kind of SQL Server stuff you love learning about, you’ll love my training. I’m offering a 75% discount to my blog readers if you click from here. I’m also available for consulting if you just don’t have time for that, and need to solve database performance problems quickly. You can also get a quick, low cost health check with no phone time required.
Far and away, some of the trickiest situations I run into when helping clients is rewriting scalar functions that have WHILE loops in them.
This sort of procedural code is often difficult, but not impossible, to replace with set-based logic.
Sure, lots of IF/THEN/ELSE stuff can be tough too, though that’s often easier to manage with CASE expressions in stacked CTEs or derived tables.
I ran across a really interesting function recently that I had to rewrite that had a couple WHILE loops in it, and I’ve simplified the example here to show my approach to fixing it.
Table Face
The original intent of the function was to do some string manipulation and return a cleaned version of it.
There were several loops that looked for “illegal” characters, add in formatting characters (like dashes), etc.
The bad way of doing this is like so. If you write functions like this, feel bad. Let it burn a little.
Ten years ago, I’d understand. These days, there’s a billion blog posts about why this is bad.
CREATE OR ALTER FUNCTION
dbo.CountLetters_Bad
(
@String varchar(20)
)
RETURNS bigint
AS
BEGIN
DECLARE
@CountLetters bigint = 0,
@Counter int = 0;
WHILE
LEN(@String) >= @Counter
BEGIN
IF PATINDEX
(
'%[^0-9]%',
SUBSTRING
(
@String,
LEN(@String) - @Counter,
1
)
) > 0
BEGIN
SET @CountLetters += 1;
SET @Counter += 1;
END;
ELSE
BEGIN
SET @Counter += 1;
END;
END;
RETURN @CountLetters;
END;
GO
SELECT
CountLetters =
dbo.CountLetters_Bad('1A1A1A1A1A');
Better Way
This is a better way to write this specific function. It doesn’t come with all the baggage that the other function has.
But the thing is, if you just test them with the example calls at the end, you wouldn’t nearly be able to tell the difference.
CREATE OR ALTER FUNCTION
dbo.CountLetters
(
@String AS varchar(20)
)
RETURNS table
AS
RETURN
WITH
t AS
(
SELECT TOP(LEN(@String))
*,
s =
SUBSTRING
(
@String,
n.Number +1,
1
)
FROM dbo.Numbers AS n
)
SELECT
NumLetters =
COUNT_BIG(*)
FROM t
WHERE PATINDEX('%[^0-9]%', t.s) > 0;
GO
SELECT
cl.*
FROM dbo.CountLetters('1A1A1A1A1A') AS cl;
Pop Quiz Tomorrow
This is a problem I run into a lot: developers don’t really test SQL code in ways that are realistic to how it’ll be used.
Look, this scalar UDF runs fine for a single value
Look, this view runs fine on its own
Look, this table variable is great when I pass a test value to it
But this is hardly the methodology you should be using, because:
You’re gonna stick UDFs all over huge queries
You’re gonna join that view to 75,000 other views
You’re gonna let users pass real values to table variables that match lots of data
In tomorrow’s post, I’m gonna show you an example of how to better test code that calls functions, and what to look for.
Thanks for reading!
Going Further
If this is the kind of SQL Server stuff you love learning about, you’ll love my training. I’m offering a 75% discount to my blog readers if you click from here. I’m also available for consulting if you just don’t have time for that, and need to solve database performance problems quickly. You can also get a quick, low cost health check with no phone time required.