Just How Live Are Live Query Plans In SQL Server?

Driven to Abstraction


Query plans are generally amusing things in SQL Server.

They aggregate huge amounts of information (in XML!), and then draw it up into a pretty picture that we make a living trying to understand.

Live query plans show you what your query is doing while it’s running.

Sort of.

They don’t show blocking.

Hidden Run


They also don’t show spills. Say I’ve got a query running, and it’s taking a long time.

I’ve got live query plans turned on, so I can see what it’s up to.

A SQL Server query plan
21 minute salute

Hm. Well, that’s not a very full picture. What if I go get the live query plan XML myself?

SELECT deqs.query_plan
FROM sys.dm_exec_sessions AS des
CROSS APPLY sys.dm_exec_query_statistics_xml(des.session_id) AS deqs;

There are three operators showing spills so far.

A SQL Server query plan
Leading me on

Frosted Tips


Even if I go look at the tool tips for operators registering spills, they don’t show anything.

A SQL Server query plan
Do not serve this operator

It’s fine if SSMS doesn’t decide to re-draw icons with little exclamation points, but if information about runtime and rows processed can be updated in real-ish time, information about spills should be, too.

Thanks for reading!

Going Further


If this is the kind of SQL Server stuff you love learning about, you’ll love my training. Blog readers get 25% off the Everything Bundle — over 100 hours of performance tuning content. Need hands-on help? I offer consulting engagements from targeted investigations to ongoing retainers. Want a quick sanity check before committing to a full engagement? Schedule a call — no commitment required.

SQL Server’s Not Very Mighty IF Branch

Falling For Rome


Okay, so like, an IF branch in a stored procedure can be helpful to control logic, but not to control performance.

That’s the most important line in the blog post, now lemme show you why.

All Possible Permutations Thereof


Let’s say for our stored procedure, we want to use a different plan for different TOPs, and our tipping point is 10,000.

That’s the tip of our TOP, if you will. And you will, because my name’s on the blog, pal.

CREATE OR ALTER PROCEDURE dbo.top_sniffer (@top INT, @vtid INT)
AS
BEGIN

    IF @top <= 10000
    BEGIN
    SELECT   TOP (@top) 
             v.Id, 
             v.PostId, 
             v.UserId, 
             v.BountyAmount, 
             v.VoteTypeId, 
             v.CreationDate
    FROM     dbo.Votes AS v
    WHERE    v.VoteTypeId = @vtid
    ORDER BY v.CreationDate DESC;
    END
    
    IF @top > 10000
    BEGIN
    SELECT   TOP (@top) 
             v.Id, 
             v.PostId, 
             v.UserId, 
             v.BountyAmount, 
             v.VoteTypeId, 
             v.CreationDate
    FROM     dbo.Votes AS v
    WHERE    v.VoteTypeId = @vtid
    ORDER BY v.CreationDate DESC;
    END

END;

Soup Sandwich


This goes quite poorly. If we just get estimated plans, here’s that they produce.

/*Run me*/
EXEC dbo.top_sniffer @top = 1, @vtid = 2;

/*Run me*/
EXEC dbo.top_sniffer @top = 5000000, @vtid = 2;

Contestant Number One

A SQL Server query plan
Why are there two with the same plan?

Contestant Number Two

The optimizer explores both paths, and the plan cache concurs.

2019 01 21 21 40 11
Dead giveaway

If you were to run it with the higher value first, you’d see the same thing for the parallel plans.

Logic, Not Performance


Making plan choices with IF branches like this plain doesn’t work.

The optimizer compiles a plan for both branches based on the initial compile value.

What you end up with is a stored procedure that doesn’t do what it’s supposed to do, and parameter sniffing times two.

For a lot more information and examples, check out this Stack Exchange Q&A.

Thanks for reading!

Going Further


If this is the kind of SQL Server stuff you love learning about, you’ll love my training. Blog readers get 25% off the Everything Bundle — over 100 hours of performance tuning content. Need hands-on help? I offer consulting engagements from targeted investigations to ongoing retainers. Want a quick sanity check before committing to a full engagement? Schedule a call — no commitment required.

Last Week’s Almost Definitely Not Office Hours: January 25

ICYMI


Last week’s thrilling, stunning, flawless episode of whatever-you-wanna-call-it.

Thanks for watching!

Video Summary

In this video, I explore some quirky aspects of SQL Server maintenance and query optimization. Starting off with a lighthearted joke about webcasts and attendance, I dive into more serious topics like index usage, DMVs (Dynamic Management Views), and the optimizer’s behavior. I share an amusing demo illustrating how certain columns in tables can influence parallel plans, leading to performance differences that are both interesting and somewhat perplexing. The discussion then shifts to backup encryption and restore processes, touching on best practices for securing backups when using cloud storage services like AWS. Throughout the video, I also share some personal insights—like my ongoing mustache-growing challenge and a few funny anecdotes about past projects and experiences with SQL Server. It’s been great engaging with you all, and I hope to see more of your questions in future sessions!

Full Transcript

I sure hope I’m alive. Wee! Alright, let’s get that worked out. And…

What if you threw a webcast and nobody came? Good question, right? I should share this thing on Twitter. Words that…

…the horrible coming out of my face. … …

… … … … all right and we’ll see oh we have a person hello a person there’s all well also a truck out front the truck is my new best friend i guess and i have a bunch of wine openers might ask you know it’s funny these uh these wine openers are functionally the same there is absolutely no difference between any of these wine openers these corkscrews they are identical in every single way right from the backs the fronts but they just have slightly different branding on them which i find very funny so i got two houdinis and a word i can’t read because it’s in too fancy of a script and uh everything about them is completely the same and it reminds me a bit of um like dmv queries in sql server where uh you know they can like they can be wrong you can totally write a wrong query but when you write the right query it’s going to look exactly the same as everyone else’s right query like you can find ways to mess with it like to do something a little bit differently but it’s all being the same dmvs and columns everything else and it’s very strange to think about that that everyone has the exact same set of tools to work with and everyone has the exact same set of tools uh to to look at but people get very territorial about their very special dmv or diagnostic queries and you you would you would act like they invented the thing but they didn’t it’s kind of funny anyway there are there are approximately three of you here one of you is going to say something one of you is going to ask a question like three people in a room one of you has a question about sql server maybe what’s up arthur daniels great post today about the memory grant thing enjoyed that uh something kind of funny that you’ll notice uh with stack overflow is when you’re writing queries against it uh stuff against like the users table that involves about me or the post table that involves uh a few of the columns or like the comments table that involves the text column the text of the comment uh the optimizer is quite biased against parallel plans when you involve when you involve those columns uh i noticed that like the even on 2010 like the the parallel version of the plan that you were looking at was quite quite a bit more expensive than the serial version but uh i’ll send you over a funny demo query specifically for the comments table where if you use like if you use a creation date column play goes parallel everything’s fine but if you use the the comment text column it goes to say serial and everything sucks it’s kind of amusing yes yes indeed indeed indeed we should all these things optimizer is quite quite a strange strange beast used used used to maxed up one oh yeah you use a sap right is it business one that you use mostly or something else there’s a funny delay on these things seven of you god bless does does one of seven people have a question i’m uh i’m busy growing out my various hairs uh you’ve since you’re here you you’ve probably all seen the uh the charity thing that i’m doing where if if i hit a certain dollar amount i’ll i’ll deliver my my regular session at bits not not the not the full pre-con i’m not going to dress like freddie mercury for eight hours i don’t think freddie mercury even dressed like freddie mercury for eight hours but uh if i’ll do the one hour session dressed just in freddie mercury’s get up from the live aid concert uh and so i’m trying to grow out the the mustache and the hair so i can i can look more freddie-ish when i do that going all for it i’m not gonna i’m not gonna get skinny though i don’t think i don’t think i have it i don’t think i have the capability to get to get that skinny but i tried once it didn’t go well i got very dizzy all the time no fake teeth no my my regular teeth are weird enough you put fake teeth in here i don’t know what would happen they’re all over the place you know what’s funny is i actually got uh my wit my wisdom teeth were perfectly fine like they weren’t impacted everything was normal with my wisdom teeth but it was decided that they should get pulled out and everyone who said that you know if you go take your wisdom teeth out it’ll help your like regular teeth straighten out and i did that when i was 19 and here i am now i think like i’m i’m 2 19s in age and my teeth haven’t moved an inch or not even like at all an inch would be huge but like my teeth did not change one bit so everyone lied to me my teeth did not like magically decompress in my gums uh arthur has an interesting question uh have you ever seen an execution plan in cache with just a select operator and zero estimated cost if it helps a query return zero rows and i run the query manually i get a clustered index seek uh so that can happen uh let’s see what’s the what’s the best way some um if and it’s it’s just a select query it’s like it’s not wrapped in like an if exists or like in any sort of if logic or branching logic anyway uh so when if you run the query manually um you might be getting a trivial plan which is which in which case you will get um uh what do you call it there an index seek but if you get a non-trivial plan um yeah so if the cash trivial plan you might be getting a constant scan in there uh which might be weird i don’t know though um um if you want to show me privately what you mean i’d be happy to take a better look at it but it is a little little rusty on that uh lee asks any gutches with backup encryption we’re copying backups to aws now and i’d like to ensure no one can restore it but us uh no no real gutches uh the only real gutches with backups right now are around um uh tde so if you’re if you’re just like if you’re using like like like like the like built-in backup encryption and not using tde and then taking the backups uh so like with tde it gets kind of funky because on versions of cc older than 2016 you would get this kind of weird thing where all of a sudden you couldn’t compress your backups when tde was enabled uh and then you know your backups were huge but after two cc was over 2016 introduced encryption for uh for or reintroduced backup compression for tde enabled databases but there’s been all sorts of crazy bugs uh can i still restore a backup of prod to dev without a bunch of hoops to jump through well initially you’ll have some hoops like you’ll have to make sure that they have the certificate and all that other stuff uh on on the on the dev server but after that it’s pretty easy to pretty easy to do uh and all that stuff is built into oldest scripts so or at least at least on the backup side of course ola doesn’t write a restore script poor ola yeah the trivial yeah the constant scan is a weird thing uh so like what my guess would be that sometimes in prod the query is non-trivial and it might bail out for some reason like there might be some contradiction detection uh so i don’t know lee says he’s using dba tools to generate restore scripts to restore a dev uh i’ve never actually used them for that um i still don’t have a real job where i would have to do backups and restores but um you know hopefully hopefully i’ll avoid that uh but you know if if they have if they have everything built in then i wouldn’t i wouldn’t distrust it the dba tools folks are uh pretty pretty good pretty smart folks pretty good at powershell much better at powershell than i’ll ever be i hope if anyone wants me to learn powershell i don’t know what i’ll do let’s go cry in a corner somewhere that’s what i what i do best anyway cry in a corner very goth all right any other questions any other people want to know about a thing there there are 10 of you here now this many no one asks me anything i’m gonna go back to doodling become quite quite a doodler the last few weeks maybe next week i’ll show you some of my doodles i recorded a video earlier too joe asks is this going to be a regular thing like office hours used to be yeah as long as i can keep it up uh you know it’s it’s just me and um you know so there’s like no coverage so if uh if if the day ever comes when i’m booked with a client friday at noon then i won’t be able to do it but i think as long as uh as long as i can i’ll do it i’ll make it regular well maybe i’ll do it on a different day maybe i’ll you know bring me i just tried fridays because you know when we did office hours on wednesdays people would always be like uh it’s wednesday at noon i can’t do anything fridays at noon people are a little bit more um a little bit more uh relaxed at work i think hopefully so i i started i just decided to do fridays because they seemed like a pretty reasonable day uh have you used the failover detection utility from the tiger team no um you know i it’s not that i don’t think it’s cool i think it’s really cool and i love that they built their they’re built the microsoft is building tools like this uh it’s just been so long since um i’ve even had an ag demo environment with my vms to play with that stuff it’s just it’s just not my main focus this is not what i’m particularly interested in with sql server um it looks great though uh i know a few people excuse me who have used it who do have uh production ag stuff and they are pretty pretty psyched on it um arthur asks on oltp systems are you a fan of compression um you know if if io is an issue sure um it’s if so yes if like so if you’re in a situation with oltp where like uh you have trouble or like even if you have a really big oltp system you know if you have you know if you have trouble either keeping all your data pretty uh you know pretty like a ton of data or if you know uh you if like when you read data from disk it’s you know uh it’s not as fast as you’d like it to be the compression can certainly help there uh as long as you’re not like terribly cpu bound it’s pretty neat also you know it’s one of those things where like you have to be really careful that a like you’re actually getting a compression benefit from things b that the compression isn’t messing with your inserts updates and deletes and see that um you know when it’s it’s not like causing any like not causing like harmful cpu overhead uh it totally has a good place with oltp i’m not against it by any means but you know there are some there are some boxes that you have to check before i think before you go and uh implement it the other thing that kind of stinks about compression is that there’s no way to set it at like the table level or database level or anything like that so like people can go and create a new index and with no compression and then you’re just kind of like stuck yeah if you want to change it you have to rebuild the index but then again that’s one of the reasons that i would support rebuilding an index is if you wanted to add compression lee says i recently compressed a database and the next day one of our nightly ssis packages to that database suddenly jumped five minutes in duration with a lot more logical reads interesting i wonder what did you did you happen to capture what was different about it i mean like i get that you compressed the database and that was different but i wonder what was different about the process or like what what what the process did differently in some way be interesting darren says he always has a cpu worry with compression well no of course nothing changed in the ssi but like the ssis package does stuff in sql server and sometimes those things have execution plans or they have you know things to go differently um you know like so for an example uh there are these funny things called dml request sorts when you modify data and some sometimes those dml request sorts can add a exactly what they sound like a gigantic sort to a query plan and sometimes i would guess that if you had to compress data loading data in a sorted pattern would would help so that if you like if you had an insert that was at one point not doing the the dml request sort and then after you did the compression it was then that might be the clue as far as like the cpu where he goes uh you know i very few times do i see a system that is cpu constrained in a way that like cpus are pegged so like i don’t see a lot of people whose systems are running oltp and they’re at like 99 which is where which is like where i’d be worried about compression where i most people like when they people have cpu which is on a sql server mostly what i see is like they’re running out of worker thread stuff where they’re hitting thread pool weights and compression won’t hit won’t compression compression won’t affect that compression will affect like if you have cpus that are running hot then they’re going to run a little bit hotter with compression not like a ton and says you should have seen your system a couple months ago could take a look now want to pay me the compile rate is super high in our internal apps like the how much when you say compile rate do you mean like queries compiling or do you mean like how long they have like how long it takes them to compile or uh how frequently they compile there’s a lot of questions they have so many questions so many big questions percent compile the batches oh and uh i wonder what changed because obviously a lot of compilations is never a good thing uh did you introduce like some weird dynamic sql or some temp tables or uh i don’t know i’ll tell you one of the funniest reasons uh that i ever saw for a high compile rate was um i forget what the application was i want to say it was like asp.net but all of the queries that went in uh they would they would they would like be this like initial query that would run that would like test to see if the query was valid so it would come in it would be like set fmt only on lower like set browse table on or whatever and then it would run a query that would hit like 70 million roll rows in a table and then it would like change the settings back and all of these queries are coming in and they were just compiling every single time because the the session would start it would run those set options and it would cause a compilation which is really funny to see i was like why why why would you do that silly can you fix parameter sniffing with option recompile uh so that doesn’t actually fix parameter sniffing what that does is it disables parameters sniffing um and while it is you technically a fix sometimes there are underlying plan quality issues that need to be addressed because what option recompile does is it it assumes that the query plan you get when sql server can make a good guess is a good plan and i don’t i don’t buy that a lot of the time i think a lot a lot of the time when people are like oh if i option recompile it fixed the problem but it does it there may still be these weird underlying things that you have to deal with oh well you know it’s it showed up in chat so i felt i figured i’d talk about it sorry i didn’t i didn’t mean that you weren’t funny i just i was i just figured i’d uh i’d talk about option recompile it’s not it’s not like i have a lot of other stuff to talk about i’m pretty boring i already knew a vacuum cleaner it was funny like uh my wife had knocked over this like pink princess unicorn snow globe that my daughter has and it just like it was like shattered glass and plaster and whatever the liquid inside of a snow globe is everywhere and she she used our nice vacuum monster dyson monster thing to to uh to vacuum it up and apparently you shouldn’t vacuum water leave it non-shop back because now like anytime you put one of the attachment heads in it just starts spinning even if you’re not vacuuming so there’s a short in there somewhere so i ordered a new vacuum this morning hopefully that shows up before the floors are covered in disgustingness uh that’s the rest of the way always turn on query store in sql server 2016 plus um no uh there is some observer overhead to it and well i think it’s a very valuable tool and especially if uh so i’ll put it like this if you’re going to use it then you should turn it on and see if there’s an there’s no unacceptable performance decrease with query store i think it’s a really cool thing to to be able to use to track query performance especially over time because the plan cache is such hot garbage at keeping things in it um so i would say that i would i would like to see it turned on more and i would like to see um the various weird bugs around it uh fixed and i would also like to see some more options about where the data is stored with query store i think that’d be nice but uh you know i’d say generally if you’re going to use it and there’s no you know um unacceptable performance overhead to turn it on uh one of my last great posts over on brent’s site was about uh the cruel defaults that uh query store is turned on with so like uh there are collection modes where they define the types of queries that will end up getting collected by query store by default it goes to all and all captures and since this is my channel i get to curse all capture some real bullshit all will capture every single like system query and all this dumb stuff that goes on in the background that who cares but you if you use the collection mode auto that like queries have to meet some certain thresholds they’re not documented so i don’t know what they are but uh they have to um they have to meet certain thresholds before they get collected in query story they’re in like like compile time or like how like you know if like the query took a while to run or something like that so they’re like that turning on auto makes the overhead a little bit less tough but you know it’s it can still be there and arthur was helpful enough to post the link for us thank you arthur you are my you’re my number one junior office hours dba i wish i had a better title for you uh darren says he has an odd question we have a data mart where they rebuild all the data databases are in simple recovery model it seems odd to have no maintenance at all though any thoughts uh no that’s like the perfect perfect time to not have any maintenance if you’re just blowing everything away and loading it back in every night who cares let’s say it’s like a dba’s dream to not have to care about that sort of thing i wouldn’t do any maintenance on that i mean like there’s like there’s like almost no point in backing it up there’s no point in doing index maintenance on it i mean okay i can’t even think of a good reason to run check run check db there i guess i guess check db would be would be okay like if you wanted to run that before you loaded data just to see if like any system tables were corrupt that might be a good idea but i can’t think of a good yeah it’s just isn’t really good if it’s just gonna get like you’re taking a backup of something that’s gonna be gone anyway all right out the door out the door with you see what other maintenance is there i wouldn’t update you know i wouldn’t update stats because i’m pretty sure that like if you like truncate or drop tables and create tables and reload everything i wouldn’t even like you like the stats would update there i mean i guess if you needed i guess if you needed better statistics than what the autumn like the whatever like automatic stats update threshold is then there might be there might be a use case for updating stats there at a higher sampling well they do all right we’re on the same page look at that good job you are whoever designed that is now a full-fledged sql server consultant they should i don’t know i don’t have any i don’t have any certifications i’m sorry if they did if i did i would i would say that they are honorary honorarily certified but i’ve got none none i’m too dumb to pass a test see lee says i have a two new two node ag on standard edition with five ags because i have to use basic availability groups any reasons not to migrate this one ag to enterprise if i do make so uh i’d say cost is the primary thing like how many so you know once you go from standard to enterprise your cost jumps up pretty significantly it’s a five thousand dollar per court difference if you do migrate any suggested method uh well if you’re migrating to like if you’re on uh windows server 2016 i think you can make it make it a bit more seamless but i think like i know with ags there’s some weird stuff from like the windows server the windows server level needing to match and then like the sql server level needing to match there’s weird stuff involved there oh if you have i mean if you if you already have it paid for then yeah i’d probably move uh if you need to move to like like a net new environment i would use uh like log shipping or something i wouldn’t use mirroring in this case because you’re going from standard to enterprise and uh you know you obviously you already have an ag setup so that would be rather awkward to try and implement so um i i would i would say to just if you wanted to use log shipping and just uh get your current databases moved over to whatever new vm hardware fake hardware whatever virtual hardware uh you’re going to set up then that’s probably how i’d move things just to just to make the transition a bit more seamless and that’s assuming that you couldn’t you know set up another vm to you know get the ag also synchronizing to and then fail over to that and then fail over to whatever’s next and then destroy the old environment yeah that’s tough so uh at least he’s trying to simplify things so i don’t have so many ags to manage and check like when we reboot for us yeah that’s a bummer um you know that’s one of the reasons why you know i i asked the question again ah man all my all my all my solid gold material is still still on brent’s site but uh you know i i had a poster over there recently about like have ags gotten any easier and i think you know when it comes to stuff like patching and um you know so and even something like you know just doing uh like uh checking out indexes in an ag right so you know you have the stuff on the primary and you check like index metrics there and you know you can see that like i have a bunch of unused indexes but they get used on a secondary if you’re offloading reads so yeah you know there’s still a lot of stuff that’s tough about ags and patching is certainly one of those very perilous things because with ags you know in particular like there are a ton of patches i mean there are a ton of fixes in each patch specifically for ag stuff and ag patches have been known to break stuff too so it’s like you’re very perilous you know it’s like whatever i’d say whatever you get um whatever you what do we have for your ags make sure that you know when you do those patches you’re testing them in a lower environment first because boy howdy if one of those patches breaks something you have to uninstall a patch uninstalling a patch is my one of my least favorite things in the world because i always forget it’s the best way to i know there’s like there’s like a few ways to do it and there’s like one of them that’s like the best way to do it and i always forget which one it is yeah daren mentions you might be able to do a rolling upgrade if you can do a rolling upgrade great if you’re on like windows server 2012 or something though i don’t think you can do the you can’t do a rolling upgrade if you’re moving to like windows server 16 or 19 down the line so it would have to be at least 2016 just fun nothing but a good time with those ags who’s emailing me no i’m not talking to you don’t worry it wasn’t brent uh let’s see all right anyone else weird questions the audience has dwindled down to six now i feel bad i bored someone well i must have must have must have made someone sleepy all right well if no one has any other questions i’m gonna i’m gonna unlive this thing or try to figure out how oh there there’s a big red button that says end stream all right uh i’m gonna i’m gonna d live myself and go um i don’t know what i’m gonna do i’m gonna practice singing i guess anyway uh thanks everyone for joining me uh i had a good time i’ll see you next week i hope you know god willing and uh yeah thanks lee yes i’ll see you next week and uh go have fun this weekend do do stuff i wouldn’t do yes you have a good weekend too joe all right everyone take care bye

Video Summary

In this video, I explore some quirky aspects of SQL Server maintenance and query optimization. Starting off with a lighthearted joke about webcasts and attendance, I dive into more serious topics like index usage, DMVs (Dynamic Management Views), and the optimizer’s behavior. I share an amusing demo illustrating how certain columns in tables can influence parallel plans, leading to performance differences that are both interesting and somewhat perplexing. The discussion then shifts to backup encryption and restore processes, touching on best practices for securing backups when using cloud storage services like AWS. Throughout the video, I also share some personal insights—like my ongoing mustache-growing challenge and a few funny anecdotes about past projects and experiences with SQL Server. It’s been great engaging with you all, and I hope to see more of your questions in future sessions!

Full Transcript

I sure hope I’m alive. Wee! Alright, let’s get that worked out. And…

What if you threw a webcast and nobody came? Good question, right? I should share this thing on Twitter. Words that…

…the horrible coming out of my face. … …

… … … … all right and we’ll see oh we have a person hello a person there’s all well also a truck out front the truck is my new best friend i guess and i have a bunch of wine openers might ask you know it’s funny these uh these wine openers are functionally the same there is absolutely no difference between any of these wine openers these corkscrews they are identical in every single way right from the backs the fronts but they just have slightly different branding on them which i find very funny so i got two houdinis and a word i can’t read because it’s in too fancy of a script and uh everything about them is completely the same and it reminds me a bit of um like dmv queries in sql server where uh you know they can like they can be wrong you can totally write a wrong query but when you write the right query it’s going to look exactly the same as everyone else’s right query like you can find ways to mess with it like to do something a little bit differently but it’s all being the same dmvs and columns everything else and it’s very strange to think about that that everyone has the exact same set of tools to work with and everyone has the exact same set of tools uh to to look at but people get very territorial about their very special dmv or diagnostic queries and you you would you would act like they invented the thing but they didn’t it’s kind of funny anyway there are there are approximately three of you here one of you is going to say something one of you is going to ask a question like three people in a room one of you has a question about sql server maybe what’s up arthur daniels great post today about the memory grant thing enjoyed that uh something kind of funny that you’ll notice uh with stack overflow is when you’re writing queries against it uh stuff against like the users table that involves about me or the post table that involves uh a few of the columns or like the comments table that involves the text column the text of the comment uh the optimizer is quite biased against parallel plans when you involve when you involve those columns uh i noticed that like the even on 2010 like the the parallel version of the plan that you were looking at was quite quite a bit more expensive than the serial version but uh i’ll send you over a funny demo query specifically for the comments table where if you use like if you use a creation date column play goes parallel everything’s fine but if you use the the comment text column it goes to say serial and everything sucks it’s kind of amusing yes yes indeed indeed indeed we should all these things optimizer is quite quite a strange strange beast used used used to maxed up one oh yeah you use a sap right is it business one that you use mostly or something else there’s a funny delay on these things seven of you god bless does does one of seven people have a question i’m uh i’m busy growing out my various hairs uh you’ve since you’re here you you’ve probably all seen the uh the charity thing that i’m doing where if if i hit a certain dollar amount i’ll i’ll deliver my my regular session at bits not not the not the full pre-con i’m not going to dress like freddie mercury for eight hours i don’t think freddie mercury even dressed like freddie mercury for eight hours but uh if i’ll do the one hour session dressed just in freddie mercury’s get up from the live aid concert uh and so i’m trying to grow out the the mustache and the hair so i can i can look more freddie-ish when i do that going all for it i’m not gonna i’m not gonna get skinny though i don’t think i don’t think i have it i don’t think i have the capability to get to get that skinny but i tried once it didn’t go well i got very dizzy all the time no fake teeth no my my regular teeth are weird enough you put fake teeth in here i don’t know what would happen they’re all over the place you know what’s funny is i actually got uh my wit my wisdom teeth were perfectly fine like they weren’t impacted everything was normal with my wisdom teeth but it was decided that they should get pulled out and everyone who said that you know if you go take your wisdom teeth out it’ll help your like regular teeth straighten out and i did that when i was 19 and here i am now i think like i’m i’m 2 19s in age and my teeth haven’t moved an inch or not even like at all an inch would be huge but like my teeth did not change one bit so everyone lied to me my teeth did not like magically decompress in my gums uh arthur has an interesting question uh have you ever seen an execution plan in cache with just a select operator and zero estimated cost if it helps a query return zero rows and i run the query manually i get a clustered index seek uh so that can happen uh let’s see what’s the what’s the best way some um if and it’s it’s just a select query it’s like it’s not wrapped in like an if exists or like in any sort of if logic or branching logic anyway uh so when if you run the query manually um you might be getting a trivial plan which is which in which case you will get um uh what do you call it there an index seek but if you get a non-trivial plan um yeah so if the cash trivial plan you might be getting a constant scan in there uh which might be weird i don’t know though um um if you want to show me privately what you mean i’d be happy to take a better look at it but it is a little little rusty on that uh lee asks any gutches with backup encryption we’re copying backups to aws now and i’d like to ensure no one can restore it but us uh no no real gutches uh the only real gutches with backups right now are around um uh tde so if you’re if you’re just like if you’re using like like like like the like built-in backup encryption and not using tde and then taking the backups uh so like with tde it gets kind of funky because on versions of cc older than 2016 you would get this kind of weird thing where all of a sudden you couldn’t compress your backups when tde was enabled uh and then you know your backups were huge but after two cc was over 2016 introduced encryption for uh for or reintroduced backup compression for tde enabled databases but there’s been all sorts of crazy bugs uh can i still restore a backup of prod to dev without a bunch of hoops to jump through well initially you’ll have some hoops like you’ll have to make sure that they have the certificate and all that other stuff uh on on the on the dev server but after that it’s pretty easy to pretty easy to do uh and all that stuff is built into oldest scripts so or at least at least on the backup side of course ola doesn’t write a restore script poor ola yeah the trivial yeah the constant scan is a weird thing uh so like what my guess would be that sometimes in prod the query is non-trivial and it might bail out for some reason like there might be some contradiction detection uh so i don’t know lee says he’s using dba tools to generate restore scripts to restore a dev uh i’ve never actually used them for that um i still don’t have a real job where i would have to do backups and restores but um you know hopefully hopefully i’ll avoid that uh but you know if if they have if they have everything built in then i wouldn’t i wouldn’t distrust it the dba tools folks are uh pretty pretty good pretty smart folks pretty good at powershell much better at powershell than i’ll ever be i hope if anyone wants me to learn powershell i don’t know what i’ll do let’s go cry in a corner somewhere that’s what i what i do best anyway cry in a corner very goth all right any other questions any other people want to know about a thing there there are 10 of you here now this many no one asks me anything i’m gonna go back to doodling become quite quite a doodler the last few weeks maybe next week i’ll show you some of my doodles i recorded a video earlier too joe asks is this going to be a regular thing like office hours used to be yeah as long as i can keep it up uh you know it’s it’s just me and um you know so there’s like no coverage so if uh if if the day ever comes when i’m booked with a client friday at noon then i won’t be able to do it but i think as long as uh as long as i can i’ll do it i’ll make it regular well maybe i’ll do it on a different day maybe i’ll you know bring me i just tried fridays because you know when we did office hours on wednesdays people would always be like uh it’s wednesday at noon i can’t do anything fridays at noon people are a little bit more um a little bit more uh relaxed at work i think hopefully so i i started i just decided to do fridays because they seemed like a pretty reasonable day uh have you used the failover detection utility from the tiger team no um you know i it’s not that i don’t think it’s cool i think it’s really cool and i love that they built their they’re built the microsoft is building tools like this uh it’s just been so long since um i’ve even had an ag demo environment with my vms to play with that stuff it’s just it’s just not my main focus this is not what i’m particularly interested in with sql server um it looks great though uh i know a few people excuse me who have used it who do have uh production ag stuff and they are pretty pretty psyched on it um arthur asks on oltp systems are you a fan of compression um you know if if io is an issue sure um it’s if so yes if like so if you’re in a situation with oltp where like uh you have trouble or like even if you have a really big oltp system you know if you have you know if you have trouble either keeping all your data pretty uh you know pretty like a ton of data or if you know uh you if like when you read data from disk it’s you know uh it’s not as fast as you’d like it to be the compression can certainly help there uh as long as you’re not like terribly cpu bound it’s pretty neat also you know it’s one of those things where like you have to be really careful that a like you’re actually getting a compression benefit from things b that the compression isn’t messing with your inserts updates and deletes and see that um you know when it’s it’s not like causing any like not causing like harmful cpu overhead uh it totally has a good place with oltp i’m not against it by any means but you know there are some there are some boxes that you have to check before i think before you go and uh implement it the other thing that kind of stinks about compression is that there’s no way to set it at like the table level or database level or anything like that so like people can go and create a new index and with no compression and then you’re just kind of like stuck yeah if you want to change it you have to rebuild the index but then again that’s one of the reasons that i would support rebuilding an index is if you wanted to add compression lee says i recently compressed a database and the next day one of our nightly ssis packages to that database suddenly jumped five minutes in duration with a lot more logical reads interesting i wonder what did you did you happen to capture what was different about it i mean like i get that you compressed the database and that was different but i wonder what was different about the process or like what what what the process did differently in some way be interesting darren says he always has a cpu worry with compression well no of course nothing changed in the ssi but like the ssis package does stuff in sql server and sometimes those things have execution plans or they have you know things to go differently um you know like so for an example uh there are these funny things called dml request sorts when you modify data and some sometimes those dml request sorts can add a exactly what they sound like a gigantic sort to a query plan and sometimes i would guess that if you had to compress data loading data in a sorted pattern would would help so that if you like if you had an insert that was at one point not doing the the dml request sort and then after you did the compression it was then that might be the clue as far as like the cpu where he goes uh you know i very few times do i see a system that is cpu constrained in a way that like cpus are pegged so like i don’t see a lot of people whose systems are running oltp and they’re at like 99 which is where which is like where i’d be worried about compression where i most people like when they people have cpu which is on a sql server mostly what i see is like they’re running out of worker thread stuff where they’re hitting thread pool weights and compression won’t hit won’t compression compression won’t affect that compression will affect like if you have cpus that are running hot then they’re going to run a little bit hotter with compression not like a ton and says you should have seen your system a couple months ago could take a look now want to pay me the compile rate is super high in our internal apps like the how much when you say compile rate do you mean like queries compiling or do you mean like how long they have like how long it takes them to compile or uh how frequently they compile there’s a lot of questions they have so many questions so many big questions percent compile the batches oh and uh i wonder what changed because obviously a lot of compilations is never a good thing uh did you introduce like some weird dynamic sql or some temp tables or uh i don’t know i’ll tell you one of the funniest reasons uh that i ever saw for a high compile rate was um i forget what the application was i want to say it was like asp.net but all of the queries that went in uh they would they would they would like be this like initial query that would run that would like test to see if the query was valid so it would come in it would be like set fmt only on lower like set browse table on or whatever and then it would run a query that would hit like 70 million roll rows in a table and then it would like change the settings back and all of these queries are coming in and they were just compiling every single time because the the session would start it would run those set options and it would cause a compilation which is really funny to see i was like why why why would you do that silly can you fix parameter sniffing with option recompile uh so that doesn’t actually fix parameter sniffing what that does is it disables parameters sniffing um and while it is you technically a fix sometimes there are underlying plan quality issues that need to be addressed because what option recompile does is it it assumes that the query plan you get when sql server can make a good guess is a good plan and i don’t i don’t buy that a lot of the time i think a lot a lot of the time when people are like oh if i option recompile it fixed the problem but it does it there may still be these weird underlying things that you have to deal with oh well you know it’s it showed up in chat so i felt i figured i’d talk about it sorry i didn’t i didn’t mean that you weren’t funny i just i was i just figured i’d uh i’d talk about option recompile it’s not it’s not like i have a lot of other stuff to talk about i’m pretty boring i already knew a vacuum cleaner it was funny like uh my wife had knocked over this like pink princess unicorn snow globe that my daughter has and it just like it was like shattered glass and plaster and whatever the liquid inside of a snow globe is everywhere and she she used our nice vacuum monster dyson monster thing to to uh to vacuum it up and apparently you shouldn’t vacuum water leave it non-shop back because now like anytime you put one of the attachment heads in it just starts spinning even if you’re not vacuuming so there’s a short in there somewhere so i ordered a new vacuum this morning hopefully that shows up before the floors are covered in disgustingness uh that’s the rest of the way always turn on query store in sql server 2016 plus um no uh there is some observer overhead to it and well i think it’s a very valuable tool and especially if uh so i’ll put it like this if you’re going to use it then you should turn it on and see if there’s an there’s no unacceptable performance decrease with query store i think it’s a really cool thing to to be able to use to track query performance especially over time because the plan cache is such hot garbage at keeping things in it um so i would say that i would i would like to see it turned on more and i would like to see um the various weird bugs around it uh fixed and i would also like to see some more options about where the data is stored with query store i think that’d be nice but uh you know i’d say generally if you’re going to use it and there’s no you know um unacceptable performance overhead to turn it on uh one of my last great posts over on brent’s site was about uh the cruel defaults that uh query store is turned on with so like uh there are collection modes where they define the types of queries that will end up getting collected by query store by default it goes to all and all captures and since this is my channel i get to curse all capture some real bullshit all will capture every single like system query and all this dumb stuff that goes on in the background that who cares but you if you use the collection mode auto that like queries have to meet some certain thresholds they’re not documented so i don’t know what they are but uh they have to um they have to meet certain thresholds before they get collected in query story they’re in like like compile time or like how like you know if like the query took a while to run or something like that so they’re like that turning on auto makes the overhead a little bit less tough but you know it’s it can still be there and arthur was helpful enough to post the link for us thank you arthur you are my you’re my number one junior office hours dba i wish i had a better title for you uh darren says he has an odd question we have a data mart where they rebuild all the data databases are in simple recovery model it seems odd to have no maintenance at all though any thoughts uh no that’s like the perfect perfect time to not have any maintenance if you’re just blowing everything away and loading it back in every night who cares let’s say it’s like a dba’s dream to not have to care about that sort of thing i wouldn’t do any maintenance on that i mean like there’s like there’s like almost no point in backing it up there’s no point in doing index maintenance on it i mean okay i can’t even think of a good reason to run check run check db there i guess i guess check db would be would be okay like if you wanted to run that before you loaded data just to see if like any system tables were corrupt that might be a good idea but i can’t think of a good yeah it’s just isn’t really good if it’s just gonna get like you’re taking a backup of something that’s gonna be gone anyway all right out the door out the door with you see what other maintenance is there i wouldn’t update you know i wouldn’t update stats because i’m pretty sure that like if you like truncate or drop tables and create tables and reload everything i wouldn’t even like you like the stats would update there i mean i guess if you needed i guess if you needed better statistics than what the autumn like the whatever like automatic stats update threshold is then there might be there might be a use case for updating stats there at a higher sampling well they do all right we’re on the same page look at that good job you are whoever designed that is now a full-fledged sql server consultant they should i don’t know i don’t have any i don’t have any certifications i’m sorry if they did if i did i would i would say that they are honorary honorarily certified but i’ve got none none i’m too dumb to pass a test see lee says i have a two new two node ag on standard edition with five ags because i have to use basic availability groups any reasons not to migrate this one ag to enterprise if i do make so uh i’d say cost is the primary thing like how many so you know once you go from standard to enterprise your cost jumps up pretty significantly it’s a five thousand dollar per court difference if you do migrate any suggested method uh well if you’re migrating to like if you’re on uh windows server 2016 i think you can make it make it a bit more seamless but i think like i know with ags there’s some weird stuff from like the windows server the windows server level needing to match and then like the sql server level needing to match there’s weird stuff involved there oh if you have i mean if you if you already have it paid for then yeah i’d probably move uh if you need to move to like like a net new environment i would use uh like log shipping or something i wouldn’t use mirroring in this case because you’re going from standard to enterprise and uh you know you obviously you already have an ag setup so that would be rather awkward to try and implement so um i i would i would say to just if you wanted to use log shipping and just uh get your current databases moved over to whatever new vm hardware fake hardware whatever virtual hardware uh you’re going to set up then that’s probably how i’d move things just to just to make the transition a bit more seamless and that’s assuming that you couldn’t you know set up another vm to you know get the ag also synchronizing to and then fail over to that and then fail over to whatever’s next and then destroy the old environment yeah that’s tough so uh at least he’s trying to simplify things so i don’t have so many ags to manage and check like when we reboot for us yeah that’s a bummer um you know that’s one of the reasons why you know i i asked the question again ah man all my all my all my solid gold material is still still on brent’s site but uh you know i i had a poster over there recently about like have ags gotten any easier and i think you know when it comes to stuff like patching and um you know so and even something like you know just doing uh like uh checking out indexes in an ag right so you know you have the stuff on the primary and you check like index metrics there and you know you can see that like i have a bunch of unused indexes but they get used on a secondary if you’re offloading reads so yeah you know there’s still a lot of stuff that’s tough about ags and patching is certainly one of those very perilous things because with ags you know in particular like there are a ton of patches i mean there are a ton of fixes in each patch specifically for ag stuff and ag patches have been known to break stuff too so it’s like you’re very perilous you know it’s like whatever i’d say whatever you get um whatever you what do we have for your ags make sure that you know when you do those patches you’re testing them in a lower environment first because boy howdy if one of those patches breaks something you have to uninstall a patch uninstalling a patch is my one of my least favorite things in the world because i always forget it’s the best way to i know there’s like there’s like a few ways to do it and there’s like one of them that’s like the best way to do it and i always forget which one it is yeah daren mentions you might be able to do a rolling upgrade if you can do a rolling upgrade great if you’re on like windows server 2012 or something though i don’t think you can do the you can’t do a rolling upgrade if you’re moving to like windows server 16 or 19 down the line so it would have to be at least 2016 just fun nothing but a good time with those ags who’s emailing me no i’m not talking to you don’t worry it wasn’t brent uh let’s see all right anyone else weird questions the audience has dwindled down to six now i feel bad i bored someone well i must have must have must have made someone sleepy all right well if no one has any other questions i’m gonna i’m gonna unlive this thing or try to figure out how oh there there’s a big red button that says end stream all right uh i’m gonna i’m gonna d live myself and go um i don’t know what i’m gonna do i’m gonna practice singing i guess anyway uh thanks everyone for joining me uh i had a good time i’ll see you next week i hope you know god willing and uh yeah thanks lee yes i’ll see you next week and uh go have fun this weekend do do stuff i wouldn’t do yes you have a good weekend too joe all right everyone take care bye

Going Further


If this is the kind of SQL Server stuff you love learning about, you’ll love my training. Blog readers get 25% off the Everything Bundle — over 100 hours of performance tuning content. Need hands-on help? I offer consulting engagements from targeted investigations to ongoing retainers. Want a quick sanity check before committing to a full engagement? Schedule a call — no commitment required.

What Happens When You Don’t Parameterize Dynamic SQL In SQL Server?

Unskinny Top


When I blogged about passing parameters to TOP, we ran into a problem that has many possible solutions.

Perhaps the least complex answer was just to fix the index. Nine times outta ten, that’s what I’d do.

Since I offered some other possible solutions, I do wanna talk about the pros and cons of them.

In this post, I’ll talk about using slightly less than fully parameterized dynamic SQL, which will, of course, terrify even people who live in Baltimore.

Disclaimer


While I’m not smart enough to figure out a SQL injection method without altering the stored procedure, that doesn’t mean it can’t happen.

It might be more difficult, but not impossible. Here’s our prize:

CREATE OR ALTER PROCEDURE dbo.top_sniffer (@top INT, @vtid INT)
AS
BEGIN

DECLARE @sql NVARCHAR(MAX) = N'';

SET @sql = @sql + N'
    SELECT   TOP (' + RTRIM(@top) + ')
             v.Id, 
             v.PostId, 
             v.UserId, 
             v.BountyAmount, 
             v.VoteTypeId, 
             v.CreationDate
    FROM     dbo.Votes AS v
    WHERE    v.VoteTypeId = @i_vtid
    ORDER BY v.CreationDate DESC;
    '

    PRINT @sql;
    EXEC sys.sp_executesql @sql, N'@i_vtid INT', @i_vtid = @vtid

END;

If we fully parameterize this, we’ll end up with the same problem we had before with plan reuse.

Since we don’t, we can can use a trick that works on filtered indexes.

But There’s Another Downside


Every different top will get a new plan. The upside is that plans with the same top may get reused, so it’s got a little something over recompile there.

So if I execute these:

/*Run me*/
EXEC dbo.top_sniffer @top = 1, @vtid = 4;

/*Run me*/
EXEC dbo.top_sniffer @top = 5000, @vtid = 4;

They each get their own plan:

SQL Server Query Plan
Shellac

And of course, their own plan cache entry.

SQL Server Query Results
I am your neighbor.

If lots of people look for lots of different TOPs (which you could cut down on by limiting the values your app will take, like via a dropdown), you can end up with a lot of plans kicking around.

Would I Do This?


Likely not, because of the potential risk, and the potential impact on the plan cache, but I thought it was interesting enough to follow up on.

Thanks for reading!

Going Further


If this is the kind of SQL Server stuff you love learning about, you’ll love my training. Blog readers get 25% off the Everything Bundle — over 100 hours of performance tuning content. Need hands-on help? I offer consulting engagements from targeted investigations to ongoing retainers. Want a quick sanity check before committing to a full engagement? Schedule a call — no commitment required.

SQL Server Indexes Sort Data For Us

Yelling and Screaming2019 01 21 13 03 05


If you saw my post about parameterized TOPs, one thing you may have immediately hated is the index I created.

And rightfully so — it was a terrible index for reasons we’ll discuss in this post.

If that index made you mad, congratulations, you’re a smart cookie.

CREATE INDEX whatever ON dbo.Votes(CreationDate DESC, VoteTypeId)
GO

Yes, my friends, this index is wrong.

It’s not just wrong because we’ve got the column we’re filtering on second, but because there’s no reason for it to be second.

Nothing in our query lends itself to this particular indexing scenrio.

CREATE OR ALTER PROCEDURE dbo.top_sniffer (@top INT, @vtid INT)
AS
BEGIN

    SELECT   TOP (@top) 
             v.Id, 
             v.PostId, 
             v.UserId, 
             v.BountyAmount, 
             v.VoteTypeId, 
             v.CreationDate
    FROM     dbo.Votes AS v
    WHERE    v.VoteTypeId = @vtid
    ORDER BY v.CreationDate DESC;

END;

We Index Pretty


The reason I sometimes see columns appear first in an index is to avoid having to physically sort data.

If I run the stored procedure without any nonclustered indexes, this is our query plan:

EXEC dbo.top_sniffer @top = 1, @vtid = 1;
A SQL Server query plan
spillo

A sort, a spill, kablooey. We’re not having any fun, here.

With the original index, our data is organized in the order that we’re asking for it to be returned in the ORDER BY.

This caused all sorts of issues when we were looking for VoteTypeIds that were spread throughout the index, where we couldn’t satisfy the TOP quickly.

There was no Sort in the plan when we had the “wrong” index added.

A SQL Server query plan
Primal

B-Tree Equality


We can also avoid having to sort data by having the ORDER BY column(s) second in the key of the index, because our filter is an equality.

CREATE INDEX whatever ON dbo.Votes(VoteTypeId, CreationDate DESC)
GO

Having the filter column first also helps us avoid the longer running query issue when we look for VoteTypeId 4.

EXEC dbo.top_sniffer @top = 5000, @vtid = 4;
A SQL Server query plan
I like you better.
Table 'Votes'. Scan count 1, logical reads 2262

 SQL Server Execution Times:
   CPU time = 0 ms,  elapsed time = 148 ms.

Solving for Sorts


If you’ve been following my blogging for a while, you’ve likely seen me say this stuff before, because Sorts have some issues.

  • They’re locally blocking, in that every row has to arrive before they can run
  • They require additional memory space to order data the way you want
  • They may spill to disk if they don’t get enough memory
  • They may ask for quite a bit of extra memory if estimations are incorrect
  • They may end up in a query plan even when you don’t explicitly ask for them

There are plenty of times when these things aren’t problems, but it’s good to know when they are, or when they might turn into a problem.

Thanks for reading!

Going Further


If this is the kind of SQL Server stuff you love learning about, you’ll love my training. Blog readers get 25% off the Everything Bundle — over 100 hours of performance tuning content. Need hands-on help? I offer consulting engagements from targeted investigations to ongoing retainers. Want a quick sanity check before committing to a full engagement? Schedule a call — no commitment required.

Predicate Selectivity And SQL Server Index Design

Loose Ends


A while back I promised I’d write about what allows SQL Server to perform two seeks rather than a seek with a residual predicate.

More recently, a post touched a bit on predicate selectivity in index design, and how missing index requests don’t factor that in when requesting indexes.

This post should tie the two together a bit. Maybe. Hopefully. We’ll see where it goes, eh?

If you want a TL;DR, it’s that neighboring index key columns support seeks quite easily, and that choosing the leading column should likely be a reflection of which is filtered on most frequently.

If you want more specific advice, I’d be happy to give it to you.

Index Management


Let’s get real wacky and create two indexes.

CREATE NONCLUSTERED INDEX whatever
    ON dbo.Posts ( PostTypeId, ClosedDate );

CREATE NONCLUSTERED INDEX apathy
    ON dbo.Posts ( ClosedDate, PostTypeId );

Now let’s run two identical queries, and have each one hit one of those indexes.

SELECT p.Id, p.PostTypeId, p.ClosedDate
FROM   dbo.Posts AS p WITH (INDEX = whatever)
WHERE  p.PostTypeId = 1
AND    p.ClosedDate >= '2018-06-01';

SELECT p.Id, p.PostTypeId, p.ClosedDate
FROM   dbo.Posts AS p WITH (INDEX = apathy)
WHERE  p.PostTypeId = 1
AND    p.ClosedDate >= '2018-06-01';

If you run them a bunch of times, the first query tends to end up around ~50ms ahead of the second, though they both sport nearly identical query plans.

The seek may look confusing, because PostTypeId seems to appear as both a seek and a residual predicate. That’s because it’s sort of both.

The seek tells us where we start reading, which means we’ll find rows starting with ClosedDate 2018-06-01, and with PostTypeId 1.

From there, we may find higher PostTypeIds, which is why we have a residual predicate; to filter those out.

More generally, a seek can find a single row, or a range of rows as long as they’re all together. When the leading column of an index is used to find a range, we can seek to a starting point, but we need a residual predicate to check for other predicates afterwards.

This is why the index rule of thumb for many people is to start indexes with equality predicates. Any rows located will be contiguous, and we can easily continue the seek while applying any other predicates.

Tool Tips from a SQL Server query plan
Seeky Scanny

There’s also differences in stats time and IO.

Table 'Posts'. Scan count 1, logical reads 8, 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 = 0 ms,  elapsed time = 156 ms.

Table 'Posts'. Scan count 1, logical reads 6, 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 = 0 ms,  elapsed time = 106 ms.

Remember that this is how things break down for each predicate:

Counts from SQL Server Management Studio
Lotsa and Nunna

But in neither case do we need to touch all ~6mm rows of PostTypeId 1 to locate the correct range of ClosedDates.

Downstairs Mixup


When does that change?

When we design indexes a little bit more differenter.

CREATE NONCLUSTERED INDEX ennui
    ON dbo.Posts ( PostTypeId ) INCLUDE (ClosedDate);

CREATE NONCLUSTERED INDEX morose
    ON dbo.Posts ( ClosedDate ) INCLUDE (PostTypeId);

Running the exact same queries, something changes quite drastically for the first one.

Table 'Posts'. Scan count 1, logical reads 16344, 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 = 297 ms,  elapsed time = 301 ms.

Table 'Posts'. Scan count 1, logical reads 6, 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 = 0 ms,  elapsed time = 187 ms.

This time, the residual predicate hurts us, when we look for a range of values.

Took Tips from a SQL Server Management Studio
Yaw yaw yaw

We do quite a lot of extra reads — in fact, this time we do need to touch all ~6mm rows of PostTypeId 1.

Off By One


Something similar happens if we only rearrange key columns, too.

CREATE NONCLUSTERED INDEX ennui
    ON dbo.Posts ( PostTypeId, OwnerUserId, ClosedDate ) WITH ( DROP_EXISTING = ON );

CREATE NONCLUSTERED INDEX morose
    ON dbo.Posts ( ClosedDate, OwnerUserId, PostTypeId ) WITH ( DROP_EXISTING = ON );

I have both columns I’m querying in the key of the index this time, but I’ve stuck a column I’m not querying at all between them — OwnerUserId.

This also throws off the range predicate. We read ~30k more pages here because the index is larger.

Table 'Posts'. Scan count 1, logical reads 19375, 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 = 312 ms,  elapsed time = 314 ms.

Table 'Posts'. Scan count 1, logical reads 7, 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 = 0 ms,  elapsed time = 178 ms.

The Seeks here look identical to the ones when I had columns in the include section of the index.

What’s It All Mean?


Index column placement, whether it’s in the key or in the includes, can have a non-subtle impact on reads, especially when we’re searching for ranges.

Even when we have a non-selective leading column like PostTypeId with an equality predicate on it, we don’t need to read every single row that meets the filter to apply a range predicate, as long as that predicate is seek-able.

When we move the range column to the includes, or we add a column before it in the key, we end up doing a lot more work to locate rows.

Thanks for reading!

Going Further


If this is the kind of SQL Server stuff you love learning about, you’ll love my training. Blog readers get 25% off the Everything Bundle — over 100 hours of performance tuning content. Need hands-on help? I offer consulting engagements from targeted investigations to ongoing retainers. Want a quick sanity check before committing to a full engagement? Schedule a call — no commitment required.

In SQL Server Stored Procedures, Parameters In TOP Are Sniffed Too

Thank You For Your Business


In yesterday’s post, we looked at how row goals affected performance when data matching our join and where clauses didn’t exist.

Today we’re going to look at something similar, and perhaps a workaround to avoid the same issues.

Here’s our index setup:

CREATE INDEX whatever ON dbo.Votes(CreationDate DESC, VoteTypeId)
GO

Now we’re gonna wrap our query in a stored procedure.

CREATE OR ALTER PROCEDURE dbo.top_sniffer (@top INT, @vtid INT)
AS
BEGIN

    SELECT   TOP (@top) v.*
    FROM     dbo.Votes AS v
    WHERE    v.VoteTypeId = @vtid
    ORDER BY v.CreationDate DESC;

END;

Video Killed MTV


You know what? I don’t wanna write all this stuff. Let’s do a video.

Thanks for reading!

Video Summary

In this video, I dive into an interesting scenario involving parameterized `TOP` in SQL Server stored procedures. I explore how SQL Server handles rare data searches and the impact on query performance when using parameters like `TOP`. By creating a specific index and running various queries, I demonstrate how SQL Server’s caching mechanism can lead to suboptimal plans for rare data lookups, showcasing both the challenges and potential solutions. I also touch on some lesser-known behaviors, such as the parameter sniffing issues that arise with `OPTIMIZE FOR UNKNOWN` and dynamic SQL alternatives, providing insights into optimizing your stored procedures for better performance in real-world scenarios.

Full Transcript

Howdy folks, Erik Darling here with, well, with Erik Darling Data. And I wanted to record this as a video because I am far too lazy to blog about this today. It’s Sunday and I don’t want to do all that work screen capping and highlighting stuff and all that other things that go along with a great post. So I’m just going to record this video because I think that’s going to be a little bit easier on everyone. So what I got is something kind of funny to show you about what happens when we parameterize top. In other words, when we write a query, a stored procedure, or whatever you want to call it, and we allow people to pass a parameter like this to top like this. Now, I found some interesting stuff in this. Hopefully you find it interesting too. Now I’ve got an index here. You can ignore that little red squiggle. It’s because I already created it ahead of time because I know how much everyone hates waiting for indexes to create. And it’s on creation date descending and vote type ID. Now that is there mostly to satisfy this part of the query, my where clause on vote type ID and my order by on creation date. I know that for a bunch of the columns I’m selecting here, I’m going to have to do a key lookup. That’s not really what this is about. What I want to show you is what happens when SQL Server has to go looking for rare data. Now in the votes table, the way things break down is there are kind of a lot of rows with a vote type ID of one. There are about 3.7 million. There are far fewer with a vote type ID of four. There’s only about 733. The way that they break down in the index is fairly even. So when we look at the year of the creation date, remember that’s the leading column in the index. Oops, jumped around a little bit there. We can see that things break down fairly evenly across all of the possible years here. There’s not like any great big gaps. We have one in just about every year in the index. Everything’s pretty even there. Now when we run the plan, I’m just going to recompile this thing real quick to make sure I’m getting something fresh out the bag. And I look for the top 5,000 rows for vote type ID one. This runs fairly quickly. This runs in around about 23 milliseconds total. And it does about 15,000 logical reads. So that’s good.

And when we look at the execution plan. What I want to show you first is that when we look at the parameters that got passed in, top just like any other parameter was cached. So we have a parameter compile value of 5,000. And we also have a parameter runtime value of 5,000. If I get rid of this, and we go look at an execution where we have a 1 in as top, exactly what you think is going to happen happens. We hit F4. We go look at the plan properties. Got to jump around a little bit to get to the right one. And when I look at the parameter list. Now I have a compile time value of 5,000. And a runtime value of 1. So just like any other parameter in a store procedure in SQL Server, it gets cached when we run the query the first time.

And then it gets reused when we run the query subsequent times. Now, obviously, this could backfire if we recompile here, and we run the plan looking for a top one first. And then the next execution, some ding dong comes along and looks for a million rows. This is the obvious one. We’re going to reuse that tiny little plan that SQL Server came up with to find one row, and we’re going to use it to find a million rows. That’s the obvious bad part of parameter sniffing. The kind of less bad part, or the kind of more interesting bad part, is what happens when we look for data that is a little bit harder to find, that rare data. So now I’m going to look for a top one for the vote type ID of 4. Remember, this is the one that only has about 773 rows in the table. But when I look for top one, it’s fast.

Because it finds a row really early in the index that it can give back. Again, this is going to be early in the index, it’s order descending. So it finds this row from 2013 and says, good, we’re done. If I say I want the top 5,000 where vote type ID equals 4, this is going to be a little bit less snappy. Now we’re going to spend about two seconds, or actually close to three seconds, scanning the entire index and looking for data.

You can see that we spent an elapsed 2,700 milliseconds, close enough to three seconds for me. And we did, let’s see, that’s 146,534 reads. So we had to scan that entire index looking for that kind of spread out, kind of rare vote type ID for.

We had to start at the beginning, which is going to be the year 2013. And we had to go all the way to the end, which is 2008. So that’s one example.

Now what’s kind of interesting that I found is if I recompile things, if I clear out the plan for that store procedure, and I look for the top 5,000 first. Now even though I know that 5,000 rows don’t exist, that’s not really an issue here because the top will exit when the nested loops join says, I don’t have any more rows to give you. The top says, okay, well I wanted 5,000, thanks a lot.

So if we run this first, the plan changes a little. And it changes in that it goes parallel now. SQL Server says, I think I might have to spend a little bit more time reading this. I’m actually going to go parallel.

I’m going to use multiple threads to find these rows because they’re spread out everywhere. So that’s kind of a cool thing that SQL Server knew to do. And then when I rerun, of course, looking for a top 1, it’s going to reuse that parallel plan. And things are going to be fairly snappy still, right? It’s still okay. We’re still running in under a sec.

We’re still pretty well under a second there. Now, something else interesting that I came across when I was messing with stuff is that if you use optimize for unknown and you use a variable for top, you get kind of a funky guess just like you do with most other unknown guesses. Now, what I’ve done for this run of the store procedure is I’ve quoted out the vote type ID part of the where clause because I don’t want you thinking that the row estimate is coming from the vote type ID that I’ve chosen to pass in.

That’s not where it’s coming from. If I recompile the query without that where clause in it, and just for good luck, I’m going to make sure it’s extra double recompiled. I’m going to run this now looking for the top 5,000 rows.

And when I go look at the execution plan, rather than guessing 5,000, my top has a guess of 100. So when you use optimize for unknown with a parameterized top, you always get this guess of 100. I tried lots of different numbers, and the only time that it was never not 100 is if I recompile it through a recompile hint on that.

So that was a little funny. Anyway, if you use top in a parameterized sort of way, there are some things you might want to explore if you’re running into problems. If you’re recompiling the plan, if unparameterized or even non-parameterized, dynamic SQL might help.

Now, I say that, and I know people are going to crawl out of their skin because unparameterized dynamic SQL can lead to SQL injection attacks. I know that it’s a lesson to a degree because you would be using an integer with top, and likely just using a function like rtrim or something to produce a stringified version of the number to concatenate into the query.

But the risk is still there, so be careful. You might want to say, okay, if I know that under certain conditions I want a different plan for things, do I need to separate code into different store procedures?

Or do I have an optimized for a value? Like if I, rather than doing optimized for unknown, can I optimize for a value like 5,000, 10,000, 2.1 billion, whatever Adam Mechanics phone number is, and get a better plan that way? Do I need to change indexes?

Like maybe the order of the key of the index has been appropriate for the query? Do I need to have some included columns to get rid of that key lookup? Do I need to make an adjustment there? So there’s a lot of stuff that you could look at as ways to solve it, but this is just to let you know that that issue exists, and maybe if you keep reading my blog posts and stuff like that, maybe we’ll talk about all the ways that you can fix it coming up.

Anyway, I hope you enjoyed yourselves. I hope you learned some stuff. I mean, who knows? Maybe you didn’t. If you’re smart, you didn’t learn anything. I hope you were at least entertained if you didn’t learn anything. Anyway, I am Erik Darling with Erik Darling Data, and thanks for watching.

Bye.

Going Further


If this is the kind of SQL Server stuff you love learning about, you’ll love my training. Blog readers get 25% off the Everything Bundle — over 100 hours of performance tuning content. Need hands-on help? I offer consulting engagements from targeted investigations to ongoing retainers. Want a quick sanity check before committing to a full engagement? Schedule a call — no commitment required.

Last Week’s Almost Definitely Not Office Hours: January 18


 

Video Summary

In this video, I dive into some complex topics related to SQL Server performance tuning and parameter sniffing. Starting off, I address the nuances of parameter sniffing by discussing scenarios where a stored procedure might generate either a big plan or a small one, and how keeping the big plan can serve as a temporary fix. However, I emphasize that this approach needs careful testing to ensure it doesn’t cause issues in highly concurrent environments. Moving on, we explore resource governor and its limitations, suggesting it should be renamed “query thought throttler” due to its tendency to negatively impact performance. The conversation then shifts towards the challenges of intelligent query processing, particularly parameter sniffing, and how improvements could lead to more adaptive plans based on varying parameters passed into stored procedures.

Full Transcript

A

All right, so, uh, look at this. Ready? Yeah, yeah.
Do, do, do, do, do, do, do. Mm-hmm. Mm-hmm.
Mm-hmm. Mm-hmm. Mm-hmm.
Mm-hmm. Mm-hmm. Hello, hello, hello.
Hello, hello, hello. Hello, maybe. Weed.
Weed. It’s working, I think, kind of. It turns out scheduling these things is a lot harder. Or, like, starting up these things when they’re scheduled is a lot harder than just starting one up.
I have to do weird, odd, uncomfortable things to get this started up when I schedule them. So I think from now on, I’m just going to last minute go live. I’m just going to give whatever.
There are four of you in here, huh? We got a link to this one because the other one’s dead. That’s fun.
Happy Friday. Friday. Dancing on Twitter.
Did I promise dancing on Twitter? I forget. Now I’m too hungover to dance. I might yak on you if I start dancing. Sorry, Peter.
I apologize. So I have spent my morning messing with, oh, I had to set up an AWS account and stuff. And I had to upload the, oh, private dancing.
Yes. Well, there are six people in here, so it wouldn’t be very private. So I had to spin up an AWS account and set up an S3 bucket with permissions and all sorts of other goofy stuff this morning on top of trying to rattle off some blog posts and whatever before the weekend.
It’s going to be fun. It’s going to be a fun time. Of course I set up I am.
I followed Amazon’s best practices in that regard because leaving the root user is the only user seemed like a terrible idea. I did have a, actually it was kind of funny because I had to create a public bucket. And for a while it just kept saying like access denied every time I tried to make my public bucket public.
And the more I say public bucket, the more I regret saying public bucket. But yeah, it kept saying access denied. And it turned out that I had to like allow the making of, I had to allow making public buckets public, which is a weird thing to have to do first.
Like it should say that like this, like this bucket does not allow, is not allowed to go public. It’s goofy. Yeah, I have, I have a bunch of I am users now to confuse hackers.
So like they’ll get in, they’ll think they can do something, but they really can’t. It’s like dozens of, dozens of I am users that will lead them nowhere. Yeah, it was weird.
Like, cause I want to be able to share slides and stuff at the bits pre con and other training stuff that I’m doing. So I needed to have like a bucket that everyone could just freely download from, but not do anything else from. So, uh, there it is.
I don’t want to use Dropbox for that because, oh, I don’t know. I suppose there’s a reason. I don’t really know what it is.
Now that I got this fancy business Dropbox account, I don’t know. I don’t know. I just don’t know anymore. Man, I don’t know what other fun things have happened since last week.
I got business insurance. I got contracts from lawyers. I made, I had, I sent out my first invoice.
I don’t know. It’s been an okay week. It wasn’t a very big invoice. I probably haven’t made that little money in a week since. I don’t know.
Uh, I would say, I’d say when I, when I should have been in college. And I don’t know. It’s been an okay week. Uh, I feel like you got a lot of the, a lot of stuff figured out in here that was, that was kind of up in the air and unsure last, last Friday.
All right. There are five of you, but no one’s asking questions. So this might be quick. Like, I’ll give it another couple minutes, but then, then I have to go back to doing, well, I guess being unemployed.
Doing, doing my work. This woman’s work. It’s a good Kate Bush song. I wish that this thing would show me the list of users so that I could, like, why is there no list of users?
There we go. Oh, it’s just me and one of the, who are the other, who are the other people in here then? That’s ridiculous.
Yes. Josh is here. Thank you, Josh.
I guess, I guess, I guess by participant, it means people who have actively said something and not people who, um, I don’t know. I actually don’t even know. I don’t know.
I don’t understand this thing anymore. I seem to have, seem to have lost all touch with reality. If no dancing, what about whiskey? Uh, I have to go to the gym later.
Stupid. So, uh, I don’t think I’m going to start drinking whiskey this early. Uh, Peter asks, your post on fragment, or I guess says, your post on fragmentation was timely for frustrating reasons.
Trying to make the best of it, but would you say it’s worth my time to dig into fragmentation intervals? Uh, what do you mean fragmentation intervals? Like how often you run defrags or how often things become fragmented?
Or what exactly is a fragmentation interval? Not sure. Yeah. Not sure what that means. Internals.
Oh, no. Uh, I wouldn’t, I wouldn’t touch that. I don’t think it’s, I don’t think it’s really worth the effort. Um, you know, I, I remember, uh, every time I opened an internals book, I think the first, like, three or four chapters were just, like, like, uh, like a filler waste of time on, like, how things are stored.
And it’s good stuff to, like, know once or, like, you know, know, like, a little bit and then just kind of tuck away. But I don’t think it’s ever helped me solve much of a, um, much of a performance problem on a server.
It’s always one of those things where it’s just like, yes, it’s very neat to know that there are 8K pages and they get stored on extents. They get stored in data files and data files have these games and S games and PFS pages and all this other stuff.
But it’s not, I never, like, looked at that and went, oh, well, that’s why this query is slow. Because, um, this just never happened. It’s always been some other, like, you know, uh, well, sometimes basic reason.
Other times it’s more fun. I think, um, Adam Mechanic has one of my absolute favorite talks and it’s called, like, like, it’s like the five things that cause 95% of your query performance problems.
So it’s on YouTube too. You can find it if you look for Adam Mechanic videos on here. But, um, it, I think the, the real wisdom in there is that it usually is, uh, just one of, like, you know, one of a few really basic things that, uh, often causes the majority of performance issues and once you get outside of those things, that’s when, like, the really interesting stuff comes in.
Like, I’m gonna, I’m gonna write a blog post today, I think, about how when you, uh, it’s like when you write a query that will find data. Sometimes it’s a lot faster than a query that won’t find data.
Farah says, what monitoring tool do you wish existed but doesn’t? I don’t know. I guess mine. Uh, so the kind of interesting thing about, uh, not working for Brent anymore is, uh, you know, a lot of the work that I put into the first responder kit is now kind of, uh, I mean, I mean, it’s not off limits, but it’s, uh, you know, a bit awkward to go back to at this point.
So, uh, I’m, you know, gonna have to do some of my own stuff around that. Uh, as far as stuff that I wish, like, stuff that I, you know, stuff that I wish monitoring tools did that I don’t think exists very well is, uh, correlating, uh, or, like, capturing relevant details of, you know, um, catastrophic events.
And I don’t mean catastrophic, like, the server crashed or the server, like, failed over or something. I mean, like, you hit thread pool or, like, resource semaphore or, like, you know, just CPU pegged up to 100%.
I wish that when that stuff happened, there would be, like, this emergency mode for monitoring tools where they were just, like, like, like, like, okay, like, this crazy thing is happening. We need to focus on, like, these few things instead of just running the same dumb queries and be like, oh, 10TBs might be funny.
You know, like, stop looking at, you know, disk activity and just be like, no, we need to focus on this. Like, we need to, like, really zoom in on, uh, what’s happening there instead of, like, running the, like, like, like goofball template queries that kind of keep doing.
I’m flipping around, looking at stuff. Chris is on Twitter saying he’s watching. Why don’t you ask a question, Chris?
That would be really helpful. I don’t have anything to talk about. Oh, boy. Josh says, I have a parameter sniffing question from the Master Inquiry Tuning class that you maybe worked on.
Wow. Wow. Okay. How, you can ask me a very general parameter sniffing question, and maybe I’ll be able to answer that. Because if I have a stored proc that either gets a big plan, meaning parallel big memory grant, or a small plan, should I try to keep the big plan to avoid the small plan spilling?
Um, I think that that’s a pretty good band-aid. Like, that’s a pretty good temporary fix. Because a lot of the time when the big plan runs, unless the big plan runs very concurrently, and those additional resources become a point of contention, either parallelism, either via parallelism you hit thread pool weights, or via the memory grants you wanted to restore a semaphore, then generally, yes, that’s a pretty good band-aid until you can dig deeper into maybe getting a good plan for all variations of the query.
But that can be really tough to do, especially when, uh, you know, stored procedures, you know, especially, like, you know, when you’re learning about parameters sniffing, a lot of the examples are just like a single parameter that gets passed in.
And, like, that single parameter can cause enough variation in a plan. But then you get to the real world, you get back to your job, and there’s, like, you know, 5, 10, 15, 20 different parameters that can get passed in.
And some of them can be null, and some of them can cause real problems. So, uh, you know, if you need to, like, quick fix parameter sniffing stuff, then sometimes, yeah, like, getting and keeping the big plan can, you know, be worthwhile.
But sometimes, um, you know, you really, you know, it’s, it’s, it can cause problems downstream if, if it’s a highly concurrent thing. So, like, what I would do is I would test, if I get that big plan, how many of it can I run at once, right?
So, like, you know, use, like, SQL query stress or O stress or something and just run the big plan and, like, like, add threads to it until you either, like, run out of worker threads and hit thread pool or you run out of memory available and hit restore semaphore.
So, I’d probably like to do one of those two things and then be like, all right, well, like, this is, like, the limit for how many of this query can run. And then think about, like, as far as your workload goes, like, do you have a bunch of other queries that might be running alongside it?
How many copies of this query can run? How many, like, other, these other queries might come along? You know, there’s a, there’s a, everyone, when they think about concurrency, they just think about kind of standard, like, locking and blocking on the server, right?
Where, uh, you know. They’re like, well, I can’t run, you know, like, three of these updates at once because they’ll block each other.
Or I can’t run this update and these other select queries because they’ll block each other, right? And that’s when people fall into no lock or, you know, uh, maybe the slightly smarter decision to use an optimistic isolation level.
But, uh, you know, like, I think the, the next step in learning about concurrency is really, like, um, you know, understanding that queries interact with each other and the hardware in very particular ways.
And that it’s very easy to, um, uh, you know, become very, like, I would say, non-concurrent, uh, when enough, uh, when enough queries run. Peter says, could you get fancy with resource governor?
So resource governor has some really interesting stuff, especially, I think, for memory grants. But it, it shouldn’t be called resource governor. It should be called query thought throttler because all it really does is, uh, you know, like limit the resources that a query can use.
And often that has some rather bad side effects on performance. Yeah. Parameter sifting is hard. Um, and it’s one of those things where, uh, out of all of, I think the intelligent query processing stuff that Microsoft has been working on, I really wish that parameter sniffing were, you know, coming to the forefront.
Uh, because I think that is like, as far as like, you know, if you want to think about intelligent query processing, uh, you know, it’s nice that you can switch join types and it’s nice that you get memory grant feedback between executions.
And it’s nice that, you know, some stuff has happened with functions and table variables coming up in 2019, but man, parameter sniffing is still, I think like the biggest problem. And if you wanted to really have intelligent query processing, being able to, um, you know, store, I think branch iterations of the plan, like make some different plan choices along the way for things.
If, uh, you know, depending on the parameters that get passed in, like, I don’t think you should have multiple copies of a plan, but I think you should have multiple iterations of a plan and that, uh, you know, you could, you could do more to say, well, you know, instead of just reusing one plan, we’ll reuse like one of three or five plans depending on what parameters get passed in.
Uh, so I think, I think that might be like a good next step for the intelligent query processing crew would be to do something like that. You know, uh, it is just, you know, something that like every time, well, back, back when, back when I had clients, back when I had clients, that was always a big problem for them.
Uh, what day is your SQL bits pre-con? Uh, boy, that’s a good question. Um, let me check.
My SQL bits pre-con is, oh, it’s, you know what, it’s on the SQL bits website. I’m an idiot. I don’t know why I didn’t just look there.
I think it’s, it’s, it’s the second day. Sessions, training days. Uh, yeah, mine is on Thursday. Total server performance tuning. So Thursday, February 29th.
The training days are Wednesday and Thursday. So, uh, mine, mine’ll be Thursday. And if you’re going on Wednesday, you should definitely go check out Aaron’s session because, uh, Aaron, I think has a really cool one.
And, uh, if, if, if you, if it turns out that mine is sold out, uh, I think Penal’s would be a good, good choice to go to as well. Okay.
So that’s fun, fun, fun, fun, right? Good stuff. Uh, let’s see here. Uh, Farrah says, what were the most common query performance problems you ran across as a consultant? Were they mostly sniffing?
Uh, so I, I would say a lot of them would, a lot of them were parameter sniffing. Uh, other ones that were quite common were, were around indexing where it was either a general lack of indexing or a general, like abundance of indexing.
Like, none of the indexes, like someone added a lot of indexes a long time ago and just never revisited them. And it’s like code changed, queries changed, you know, uh, the way the app worked changed. No one ever revisited the indexes.
And we’re like, Oh, cool. Like we could, we could get rid of these five indexes that are somehow on the same single column over and over again. And we could like, you know, maybe add some other indexes. It’ll help the queries that we’re running today.
All right. So that, those are, those are very common. Um, stuff with functions was always very common. Uh, people selecting a lot of, or a lot more data. Actually, there were a number of really funny incidents where, uh, you would see, be like, people would be like, Oh, you know, we have this, you know, this query, these queries that run and they populate like, like an Excel file or a dashboard or something.
And they would just be like select star with nowhere clause. And you’d just be like, well, you know, you can limit the data that goes there because they would get it all into the application. And then people would do it like with the data that landed in the application.
That was very fun. Uh, Mr. Sick asks, did you get to keep that home, home server? I built. Yes, I did get to keep that. Uh, Brent was very nice about the things that I was allowed to keep, uh, in the layoffs, including, um, you know, a lot of the, the training material that I had worked on there.
I get to, um, you know, do, do my own thing with, because I mean, obviously I don’t, I mean, I don’t think Brent, I would imagine Brent wants to write his own material. Most of it, or he could, you know, he could, he could write, uh, things in a different way than I do. So I got to take the stuff that I worked on.
Um, and I will be using that for various stuff. Uh, I don’t know if it’s, I don’t know if it’s totally NDA. I don’t think it’s NDA anymore, but, uh, my, I put in an application to, uh, do videos with Pluralsight. And it was accepted and I’m just working on getting, you know, hooked up with someone over there.
And I’m going to be producing a couple courses for them, uh, at least, uh, fairly immediately. And then, you know, talk about some additional courses that I might do for them. Because it’s, you know, uh, I think, I think Pluralsight’s a pretty cool, uh, platform for that.
But, and, you know, as much as I would love to say, hey, like, I could, you know, like set up a, you know, whole video training thing the way, the way Brent has, um, I, that’s like a little bit beyond the scope of what I can do, uh, do, do quickly. So, you know, I’m going to say I’m going to do the Pluralsight thing for, uh, the foreseeable future. And then if, um, you know, that goes well, or if, you know, the, the consulting and training thing, uh, takes off in an appreciable way, then I’ll try to put in some plumbing to do, uh, videos through my own site.
Uh, oh, thank you, Mr. Sick. I’m excited to, uh, you know, I mean, I’m not excited to be unemployed, but I am excited to be able to do, be free to kind of, you know, uh, you know, shape my own stuff the way, the way I want to, uh, you know, that is kind of, that is a nice freedom to have. But we’ll, we’ll see how it goes.
I mean, I’m not, I’m not, I don’t think I’m going to become a suit and tie man anytime soon, but, uh, you know. So, uh, stuff like that is stuff like that is hard to forecast, uh, especially in January when there is not a whole heck of a lot of work. I’ve, I’ve been like, sort of like, just like, you know, talking to some other people who do independent consulting and I’m like, so how’s your January going?
And they’re like, we thumb twiddling. It’s like nothing doing. I don’t feel too bad.
I don’t feel like I’ve messed anything up yet, but, uh, I’m just waiting for, waiting for, uh, waiting for January to thaw out a little bit. So I, you know, maybe get some more action over on my end. It would be, it would be nice to, uh, have a positive income stream by February.
That would be a little, maybe a lofty goal, but it’s one that I’m going to go with. I don’t know. Who else?
Anyone else have anything? Anyone? Anyone? Anyone? Anyone? We could, you could have a, I have a brief discussion on what I should name the, the, the scripts that I work on. Obviously Blitz is taken.
So, uh, we’ll just call it SP Eric and maybe it’ll crash your server and maybe it won’t. Maybe it’ll run DBCC write page. Maybe, uh, I don’t know.
Maybe it’ll just change weird settings. I don’t know yet. We’ll see. I have, I’ve, uh, I remember at the DBA days that we wrote a chaos sloth or I wrote a chaos sloth script and it was pretty fun. And then it would go through and change weird settings.
It would change like max stop and cost threshold and max memory and some other stuff. And, uh, I always thought, man, it’d be fun to have one that did more stuff like, like, like change, like, like bigger settings or like restart SQL or something like that.
Or like, you know, just do crazy stuff. Like, you know, use like XP command shell or, uh, something else to like, you know, turn off the network or whatever. Yeah.
Random trace flags, but you’ll, you’ll have to send me your list of random trace flags. If you want me to do that for us, because you’ve, you’ve got a better list than I do. I have like, I have like a, uh, like a markdown file with a few found helpful over the years, but I don’t, I’m not. I’m not good at keeping track of all of the trace flags.
So you’ll have to send me your list of ones that would be especially nefarious to turn on and off. Tech news says, hello from India. Hello, tech news.
Uh, I am not in India, though. I wish I was because the food is wonderful. I think that’s about my favorite food in the world. So you’re thinking, thinking about other stuff that that script could do.
I don’t know. It’s like drop indexes. Where am I from? I am from America. I think sometimes. I’ve been to Canada a few times.
It’s pretty nice there. Kevin says, when query tuning, it’s easy to force a query to use a particular index, uh, using hints. But sometimes I want to see what the index optimizer would choose if the index it has chosen wasn’t there.
But I don’t want to disable the index because it could be large and take too long to rebuild. I wish there was a hint that allowed it to specify an index not to use. Oh, interesting.
So, uh, good news and bad news. When you specify indexes, when you specify indexes, you can actually give a list of indexes that you want it to use. Uh, I wrote the post over on Brent’s site a while back.
Um, let me see if I can find it quickly. With one of the bad idea, bad idea jeans posts. And as long as Brent’s sites up, you can do this.
There we go. Look at that. First try. It’s amazing the way that works. I’m going to stick that into chat. Um, so you can actually, you can specify multiple indexes for a query to use. So if you know, like, the names or the IDs of all the indexes that, you know, you’re okay with the optimizer having a choice of, and, you know, which ones you don’t want it to look at, is that you can specify all the ones that you say, like, are interested in seeing more from, seeing it choose from, and then not, like, not specify the ones that you don’t want to see.
So that’s one way to do it. I know it’s not the greatest thing in the world. Like, it would be much easier to say, no, just don’t use this one, rather than maybe you can use all of these.
But it could be worse. You could have absolutely no options there. That might be. I mean, the other thing is, well, I guess on a dev server, it still might be just as annoying. I don’t have a better idea on that, aside from the fact that you can specify all of the possible indexes that might be a yes, rather than specifying one that might be a no.
You know, I try to know a thing or two. I think that’s the whole point of consulting, is to know a thing or two. So sometimes, you know, like, actually, there are times when people, like, say something that’s so wrong, it makes me question whether I know what I’m talking about or not.
And that’s always a frightening moment, because in my head, I’m like, no, no, that’s wrong. And I know it’s wrong. And I know the right answer.
But, man, you are so convinced about this wrong thing that I’m trying to, like, doubt my own sanity. I’m like, I don’t know what to do right now. I’m, like, trying to, like, you know, like, low-key Google stuff and be like, this is right.
There’s no way this can be right. There’s no way. And, yeah, it’s always weird. It’s like brief losses of confidence when you’re like, wow, this person is so spectacularly wrong. They are making me question, like, things that I’ve known for a decade or things that, you know, I’ve, like, tested myself and looked at.
I’m like, what on earth? How could you do that? All right.
Any other questions? You funny, peanutty people? There are significantly fewer people here this week. There were 22 last week. But, you know, maybe that was, like, beginner’s luck. Now I’m down to eight people.
You will only do SQL types? Yeah, that’s all I really know about. I don’t know about C Sharp or anything else. So, unfortunately, I wish I knew more stuff.
I’m terrible at PowerShell. I don’t know. What else? What else is there? I mean, I can talk about hardware a little bit. I like hardware.
Things like that. Windabug. Yeah. Except you’re better at windbag than I am, Forrest. So, I don’t know why you’d want me to talk about it.
You have cracked some nuts in there that I haven’t been able to. What am I doing? Good question.
What am I doing? I think I’m actually going to go get lunch. Because there are very sparse questions. So, thank you for joining me. It was a fun-filled half hour of trying to figure out how to get this thing to work. I will most likely be back next week with another one.
We’ll see. Maybe. Maybe not. Anyway. Thank you. And see you next time. Bye. Bye.
Thank you.

SQL Server Query Performance When You Search For Rare Data Points

I Got No Rows


Over in the Votes table in the Stack Overflow database, a couple of the more popular vote types are 1 and 2.

A vote type of 1 means that an answer was accepted as being the solution by a user, and a vote type of 2 means someone upvoted a question or answer.

SQL Server Query Results
I Voted!

What this means is that it’s impossible for a question to ever have an accepted answer vote cast for it. A question can’t be an answer, here.

Unfortunately, SQL Server doesn’t have a way of inferring that.

SQL Server Query Results
ANSWER ME!

Anything with a post type id of 1 is a question.

The way the tables are structured, VoteTypeId and PostTypeId don’t exist together, so we can’t use a constraint to validate any conditions that exist between them.

SQL Server Management Studio Table
Votan
SQL Server Management Studio Table
Postan

Lost And Found


When we run a query that looks for posts with a type of 2 (that’s an answer) that have a vote type of 1, we can find 2500 of them relatively quickly.

    SELECT   TOP (2500) 
             p.OwnerUserId, 
             p.Score, 
             p.Title, 
             v.CreationDate,
             ISNULL(v.BountyAmount, 0) AS BountyAmount
    FROM     dbo.Posts AS p
    JOIN     dbo.Votes AS v
        ON  p.Id = v.PostId
    WHERE v.VoteTypeId = 2 --WHERE VoteTypeId = 2
    AND   p.PostTypeId = 1
    ORDER BY v.CreationDate DESC;

Here’s the stats:

Table 'Posts'. Scan count 0, logical reads 29044
Table 'Votes'. Scan count 1, logical reads 29131

 SQL Server Execution Times:
   CPU time = 63 ms,  elapsed time = 272 ms.

And here’s the plan:

SQL Server Query Plan
Nelly.

Colossus of Woes


Now let’s ask SQL Server for some data that doesn’t exist.

    SELECT   TOP (2500) 
             p.OwnerUserId, 
             p.Score, 
             p.Title, 
             v.CreationDate,
             ISNULL(v.BountyAmount, 0) AS BountyAmount
    FROM     dbo.Posts AS p
    JOIN     dbo.Votes AS v
        ON  p.Id = v.PostId
    WHERE v.VoteTypeId = 1 --Where VoteTypeId = 1
    AND   p.PostTypeId = 1
    ORDER BY v.CreationDate DESC;

Here’s the stats:

Table 'Posts'. Scan count 0, logical reads 11504587
Table 'Votes'. Scan count 1, logical reads 11675392

 SQL Server Execution Times:
   CPU time = 14813 ms,  elapsed time = 14906 ms.

You could say things got “worse”.

Not only that, but they got worse for the exact same plan.

SQL Server Query Plan
Argh.

So What Happened?


In the original plan, the TOP asked for rows, and quickly got them.

In the second plan, the TOP kept asking for rows, getting them from the Votes table, and then losing them on the join to Posts.

There was no parameter sniffing, there were no out of date stats, no blocking, or any other oddities. It’s just plain bad luck because of the data’s relationship.

If we apply hints to this query to:

  • Scan the clustered index on Votes
  • Choose Merge or Hash joins instead of Nested Loops
  • Force the join order as written

We get much better performing queries. The plan we have is chosen because the TOP sets a row goal that makes a Nested Loops plan using narrow (though not covering) indexes attractive to the optimizer. When it’s right, like in the original query, you probably don’t even think about it.

When it’s wrong, like in the second query, it can be quite mystifying why such a tiny query can run forever to return nothing.

If you want to try it out for yourself, use these indexes:

    CREATE INDEX whatever 
        ON dbo.Votes( CreationDate, VoteTypeId, PostId );

    CREATE NONCLUSTERED INDEX apathy
        ON dbo.Posts ( PostTypeId )
            INCLUDE ( OwnerUserId, Score, Title );

Thanks for reading!

Going Further


If this is the kind of SQL Server stuff you love learning about, you’ll love my training. Blog readers get 25% off the Everything Bundle — over 100 hours of performance tuning content. Need hands-on help? I offer consulting engagements from targeted investigations to ongoing retainers. Want a quick sanity check before committing to a full engagement? Schedule a call — no commitment required.

SQLBits Precon Logistics

Sell Out


If you’re coming to my SOLD OUT precon, and really, I appreciate that you all chose to come to my session, here’s some stuff you should do:

Join the Slack channel! Forgive their recent logo sins, and hang out in there to ask questions, yell at me, or ask when lunch is.

You can do that by going to http://sqlslack.com/ and entering your email address to get an invite. Once you’re in, you’ll wanna join #sqlbits-tuning.

If you wanna play along with any parts of the demos, you’ll wanna download this copy of the StackOverflow database.

Fair warning: if you’re gonna do this, do it well in advance. Downloading over public W-i-Fi is quite a gamble.

Lastly But Not Leastly


Check out all the other great sessions available that have seats remaining in them.

Thanks for reading!

Going Further


If this is the kind of SQL Server stuff you love learning about, you’ll love my training. Blog readers get 25% off the Everything Bundle — over 100 hours of performance tuning content. Need hands-on help? I offer consulting engagements from targeted investigations to ongoing retainers. Want a quick sanity check before committing to a full engagement? Schedule a call — no commitment required.