<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>SQL Server Planet</title>
	<atom:link href="http://sqlserverplanet.com/feed" rel="self" type="application/rss+xml" />
	<link>https://sqlserverplanet.com</link>
	<description>SQL Articles &#38; Tips from the field</description>
	<lastBuildDate>Fri, 20 Mar 2020 18:44:09 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.7</generator>
	<item>
		<title>Coronavirus (COVID19) Asymptomatic Carriers &#8211; The bad and a possible resolution?</title>
		<link>https://sqlserverplanet.com/uncategorized/coronavirus-covid19-asymptomatic-carriers-the-bad-and-a-possible-resolution</link>
		
		<dc:creator><![CDATA[Derek Dieter]]></dc:creator>
		<pubDate>Fri, 20 Mar 2020 18:42:15 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://sqlserverplanet.com/?p=3885</guid>

					<description><![CDATA[While Italy continues to be brutally hit by this unrelenting disease, they have stepped up their efforts in testing in a the northern city of Vò .  They are taking on the task of testing everyone in a desperate effort to stop the virus and what they are finding is bad news and a possible resolution that escapes our current expectation. The article states: By some reports, between a half and three-quarters of carriers in Vò, were asymptomatic. https://www.newsweek.com/coronavirus-mass-testing-experiment-italian-town-covid-19-outbreak-1493183 So what does this mean?  While I am by no means an Epidemiologist, it most likely means the virus is way more contagious that we think it is, and that many more people &#8220;have it&#8221; that previously believed, however these people exhibit no symptoms whatsoever, marking them as &#8220;asymptomatic carriers&#8221;.  How long the virus stays in an asymptomatic carrier is anyone&#8217;s guess, but I would venture to say no more than the 21 days as reported being the largest outlier. We can correlate these asymptomatic findings with another article written by Israeli nobel laureate Michael Levitt.  The most convincing he cites is that of the Diamond princess.  He equates the diamond princess where he states: The Diamond Princess cruise ship represented the worst-case scenario in terms of disease spread, as the close confines of the ship offered optimal conditions for the virus to be passed among those aboard. The population density aboard the ship was the equivalent of trying to cram the whole Israeli population into an area 30 kilometers square. In addition, the ship had a central air conditioning [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>While Italy continues to be brutally hit by this unrelenting disease, they have stepped up their efforts in testing in a the northern city of Vò .  They are taking on the task of testing everyone in a desperate effort to stop the virus and what they are finding is bad news and a possible resolution that escapes our current expectation.</p>
<p>The article states:</p>
<blockquote><p>By some reports, between a half and three-quarters of carriers in Vò, were asymptomatic.</p></blockquote>
<p><a href="https://www.newsweek.com/coronavirus-mass-testing-experiment-italian-town-covid-19-outbreak-1493183">https://www.newsweek.com/coronavirus-mass-testing-experiment-italian-town-covid-19-outbreak-1493183</a></p>
<p>So what does this mean?  While I am by no means an Epidemiologist, it most likely means the virus is way more contagious that we think it is, and that many more people &#8220;have it&#8221; that previously believed, however these people exhibit no symptoms whatsoever, marking them as &#8220;asymptomatic carriers&#8221;.  How long the virus stays in an asymptomatic carrier is anyone&#8217;s guess, but I would venture to say no more than the 21 days as reported being the largest outlier.</p>
<p>We can correlate these asymptomatic findings with another article written by Israeli nobel laureate Michael Levitt.  The most convincing he cites is that of the Diamond princess.  He equates the diamond princess where he states:</p>
<blockquote><p>The <em>Diamond Princess</em> cruise ship represented the worst-case scenario in terms of disease spread, as the close confines of the ship offered optimal conditions for the virus to be passed among those aboard. The population density aboard the ship was the equivalent of trying to cram the whole Israeli population into an area 30 kilometers square. In addition, the ship had a central air conditioning and heating system, and communal dining rooms.</p>
<p>“Those are extremely comfortable conditions for the virus and still, only 20% were infected. It is a lot, but pretty similar to the infection rate of the common flu,” Levitt said. Based on those figures, his conclusion was that most people are simply naturally immune.</p></blockquote>
<p>He&#8217;s basically saying the same thing that the Northern Italy town of Vò may be finding.  That many people may have natural immunity to the disease and it passes quickly and unnoticed due to their being asymptomatic.  Of the entire 3,711 people on board, 712 tested positive for the virus.  The question is, does that mean the other 2,999 were exposed however have naturally immunity, or they had symptoms that passed very quickly?  If so, it may mean the virus is much more contagious that previously thought, and that everyone on the Diamond Cruise ship was exposed, but only 19.2% showed symptoms.  Of those 19.2% (712) 7 of them passed away representing just under 1%.  Keep in mind however they had exceptional medical care due to Japan&#8217;s medical system not being overrun like that of Italy&#8217;s.</p>
<p>source: <a href="https://www.jpost.com/HEALTH-SCIENCE/Israeli-nobel-laureate-Coronavirus-spread-is-slowing-621145">https://www.jpost.com/HEALTH-SCIENCE/Israeli-nobel-laureate-Coronavirus-spread-is-slowing-621145</a></p>
<p>&nbsp;</p>
<p>What does all this mean?  Like I said, I am no Epidemiologist, so take it with a grain of salt.  But if this is more contagious that previously thought, and 20% of those who do contract it show symptoms, that means potentially 80% are asymptomatic.  Now of the 20% that do show symptoms, approximately 20% of them need medical care in terms ventilators or respirators, resulting in about a 1% death rate of symptomatic cases.  This should not be taken lightly, as this is heartbreaking and will severely affect us, our families, and our society as a whole.  That is the bad, the very bad.  But the potential bright light of that, is if it is so contagious, the hope is this thing will run it&#8217;s course quickly.  Let&#8217;s just pray we can stave it off long enough to ensure we do not overwhelm the heroes that work in hospitals, putting their lives on the line to save us all.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Coronavirus (Covid19) Victims Explain First Symptoms</title>
		<link>https://sqlserverplanet.com/uncategorized/coronavirus-covid19-victims-explain-first-symptoms</link>
		
		<dc:creator><![CDATA[Derek Dieter]]></dc:creator>
		<pubDate>Wed, 18 Mar 2020 17:42:38 +0000</pubDate>
				<category><![CDATA[Uncategorized]]></category>
		<guid isPermaLink="false">https://sqlserverplanet.com/?p=3883</guid>

					<description><![CDATA[In a deviation from what this site usually reports on, &#8220;SQL Server&#8221; I&#8217;ve decided to create a post about Coronavirus.  Doing a bunch of research to figure out the first symptoms people exhibit, I&#8217;ve recorded some of the research here.  Sources are included below as well.  I&#8217;ll continue to update the list as I find more. Coronavirus First Symptoms: &#62;&#62;What did your first signs feel like? Did you cough first and get fever later? &#8220;First signs were being supppper achy and having a fever, cough came probably a couple days in but the chest pain/hard time breathing was there from the jump&#8221; &#62;&#62; What was your temperature in the beginning? 102-103 the first time I got it checked [deleted by user] by inAMA SECOND PERSON: &#62;&#62; I keep getting messages about symptoms, so here’s a day by day. Friday 6 day 1 : contact with the person for 45 mins in a small non ventilated room, along with my husband and son. Mon 9 D4 : chills, low fever for me and toddler, feeling strange and an itchy throat some cough Tues 10 D5 : diarrhea, strong throat ache and headaches. Low fever, Dry cough. This is a day we got the email Saying we were in contact Wed 11 D6: dry cough, headaches, weight in my chest- like pressure. I’m pretty exhausted. No fever but I thought I was a lot better. Thurs 12 D7 : bouts of dry cough, just like being so tired I can’t catch my [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>In a deviation from what this site usually reports on, &#8220;SQL Server&#8221; I&#8217;ve decided to create a post about Coronavirus.  Doing a bunch of research to figure out the first symptoms people exhibit, I&#8217;ve recorded some of the research here.  Sources are included below as well.  I&#8217;ll continue to update the list as I find more.<br />
Coronavirus First Symptoms:</p>
<p>&gt;&gt;What did your first signs feel like? Did you cough first and get fever later?</p>
<p>&#8220;First signs were being supppper achy and having a fever, cough came probably a couple days in but the chest pain/hard time breathing was there from the jump&#8221;</p>
<p>&gt;&gt; What was your temperature in the beginning?<br />
102-103 the first time I got it checked</p>
<blockquote class="reddit-embed-bq" style="height:316px" ><p><a href="https://www.reddit.com/r/AMA/comments/fi812s/deleted_by_user/">[deleted by user]</a><br /> by<a href=""></a> in<a href="https://www.reddit.com/r/AMA/">AMA</a></p></blockquote>
<p><script async src="https://embed.reddit.com/widgets.js" charset="UTF-8"></script></p>
<p>SECOND PERSON:</p>
<p>&gt;&gt; I keep getting messages about symptoms, so here’s a day by day.</p>
<p>Friday 6 day 1 : contact with the person for 45 mins in a small non ventilated room, along with my husband and son.</p>
<p>Mon 9 D4 : chills, low fever for me and toddler, feeling strange and an itchy throat some cough</p>
<p>Tues 10 D5 : diarrhea, strong throat ache and headaches. Low fever, Dry cough. This is a day we got the email Saying we were in contact</p>
<p>Wed 11 D6: dry cough, headaches, weight in my chest- like pressure. I’m pretty exhausted. No fever but I thought I was a lot better.</p>
<p>Thurs 12 D7 : bouts of dry cough, just like being so tired I can’t catch my breath. No pain but my chest feels tight.</p>
<p>Fri 13 D8: I coughed up a ‘spray’ of red blood, got freaked out. No phone lines to answer but I got through to a doctor who’s a friend and they said it looked like my throat was irritated. I was told to take my leftover meds from when I had bronchitis. Still hard to breathe, but the meds make it manageable. Headaches</p>
<p>Sat 14 D9 headache, not much cough, still tight chest&#8230; but a lot better</p>
<blockquote class="reddit-embed-bq" style="height:316px" ><p><a href="https://www.reddit.com/r/AMA/comments/fhhaps/deleted_by_user/">[deleted by user]</a><br /> by<a href=""></a> in<a href="https://www.reddit.com/r/AMA/">AMA</a></p></blockquote>
<p><script async src="https://embed.reddit.com/widgets.js" charset="UTF-8"></script></p>
<p>THREE:</p>
<p>When did I realise I maybe have covid-19? Short of breath</p>
<p>&gt;&gt;Whats the normal symptoms? (for you)</p>
<p>&#8220;Started with a cold like symptom minus the runny nose, short of breath and then I developed a cough.&#8221;</p>
<blockquote class="reddit-embed-bq" style="height:316px" ><p><a href="https://www.reddit.com/r/AMA/comments/fiz6q9/yesterday_i_tested_positive_for_covid_19_ama/">Yesterday I tested positive for Covid 19 . AMA</a><br /> by<a href="https://www.reddit.com/user/fukDiarmo/">u/fukDiarmo</a> in<a href="https://www.reddit.com/r/AMA/">AMA</a></p></blockquote>
<p><script async src="https://embed.reddit.com/widgets.js" charset="UTF-8"></script></p>
<p>FOUR:</p>
<p>&gt;&gt; Is it okay if you can list your symptoms? I&#8217;m just curious</p>
<p>&#8220;First 3 to 5 days: Fever Headache Caughing Weakness cardiovascula proplems</p>
<p>Day 5-13 Same as before Stomach pain Fainting when standing up Pain in the lungs but no breathing proplems</p>
<p>Day 13- 18 Headache Caughing Stomach Pain Really low fever</p>
<p>I think thats it&#8221;</p>
<blockquote class="reddit-embed-bq" style="height:316px" ><p><a href="https://www.reddit.com/r/AMA/comments/fkedv7/i_had_the_corona_virus_and_spent_18_days_in/">I had the Corona Virus and spent 18 days in quarantine. I&#8217;ve now beaten it. AMA</a><br /> by<a href="https://www.reddit.com/user/zwante-/">u/zwante-</a> in<a href="https://www.reddit.com/r/AMA/">AMA</a></p></blockquote>
<p><script async src="https://embed.reddit.com/widgets.js" charset="UTF-8"></script></p>
<p>FIVE:<br />
Day 1: No symptoms, just anxiety if I got enough stuff. Later that night I ventured into cvs to pick up wat was left of cough medicine and painkillers. Thank god I did.</p>
<p>Day 2: Felt tired&#8230; slept a lot. Managed to maybe walk outside for some air. It was hard to take deep breaths as I usually do when I go for walks. Took some painkillers that night.</p>
<p>Day 3: Shit hits fan, as I realize this is no ordinary weekend hibernation. I felt my body drifting in and out of sleep. I knew it was coronavirus cuz I haven’t gotten sick in years.</p>
<p>Day 4: No other symptoms other than weakness and body giving out (can’t even stand in warm shower for more than 5 mins). General feeling of malaise overall. After the shower I went to doctor, they refused to test me cuz I didn’t exhibit enough symptoms. Went home hoping to just wait it out, but things didn’t get better.</p>
<p>Day 5: Walked into hospital telling nurses white lie that ppl at my work had coronavirus and I strongly believe I caught it from them. They took it seriously and wheeled me in for testing.. test came back and I was positive.</p>
<blockquote class="reddit-embed-bq" style="height:316px" ><p><a href="https://www.reddit.com/r/AMA/comments/fj0lj6/i_have_coronavirus_young_and_healthy_ama/">I have coronavirus&#8230; young and healthy, AMA</a><br /> by<a href=""></a> in<a href="https://www.reddit.com/r/AMA/">AMA</a></p></blockquote>
<p><script async src="https://embed.reddit.com/widgets.js" charset="UTF-8"></script><br />
SIX:</p>
<p>https://www.foxnews.com/entertainment/frozen-2-rachel-matthews-positive-coronavirus</p>
<p>The actress said the first day she exhibited symptoms began with a &#8220;sore throat, fatigue and headache,&#8221; which progressed to a &#8220;mild fever&#8221; of 100.3 on day two. Her fever was accompanied by &#8220;horrible body aches, shortness of breath, major fatigue&#8221; and &#8220;no appetite&#8221; as well as a &#8220;deep, dry cough.&#8221;</p>
<p>SEVEN:</p>
<p>Game of thrones star:</p>
<p>I only have mild symptoms of a cold.</p>
<p>https://www.instagram.com/p/B9zvkEHBRjf/?utm_source=ig_embed<br />
EIGHT:</p>
<p>&gt;&gt;What were some of your symptoms?</p>
<p>&#8220;It was like a headache, brain fog, feeling like dizzy and weird. Sore throat, cough. I was really cold. No matter how hot it was anywhere I was really cold. My nose was going between stuffy and runny.&#8221;<br />
https://www.wmfe.org/orlando-tsa-worker-who-tested-positive-for-coronavirus-talks-symptoms-testing-and-whether-the-agency-is-doing-enough/148604</p>
<p>&nbsp;</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>SQL Server Hints Explained</title>
		<link>https://sqlserverplanet.com/optimization/sql-server-hints-explained</link>
					<comments>https://sqlserverplanet.com/optimization/sql-server-hints-explained#comments</comments>
		
		<dc:creator><![CDATA[Derek Dieter]]></dc:creator>
		<pubDate>Thu, 06 Aug 2015 05:14:42 +0000</pubDate>
				<category><![CDATA[Optimization]]></category>
		<guid isPermaLink="false">https://sqlserverplanet.com/?p=3779</guid>

					<description><![CDATA[The news is out. The consensus on using hints is &#8220;Do not use them&#8221;. They will ruin you. But much like the 1950&#8217;s people keep their dirty laundry to themselves and outwardly show their best &#8220;non hint&#8221; selves, this applies as well. With that said, I will go ahead and use the disclaimer that you SHOULD NOT use hints. But, if you ARE like me, and touch a lot of SQL code where there is a huge difference in site performance between a 1 second execution and 20ms execution, then sometimes you don&#8217;t have much of an option. With that said, I will talk about the optimizer. It&#8217;s getting smarter and smarter at dealing with the majority of queries and keeping them within an even keel baseline. But that comes at a cost. It can&#8217;t take as many chances so it plays it safe. And this is where you come in. But before you go around adding hints everywhere in your system (which is a horrible idea). You need to know how to write code so that it will not NEED hints. To do this is very simple. The answer is KEEP IT SIMPLE. In other words, keep the queries simple. Do not create huge SQL Statements. It&#8217;s much better to break the queries up so that you do not give the optimizer many different routes to take. Breaking them up typically entails materializing the data into temp tables (write as little data as possible) so you can break up [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>The news is out.  The consensus on using hints is &#8220;Do not use them&#8221;.  They will ruin you.  But much like the 1950&#8217;s people keep their dirty laundry to themselves and outwardly show their best &#8220;non hint&#8221; selves, this applies as well.  With that said, I will go ahead and use the disclaimer that you SHOULD NOT use hints.  But, if you ARE like me, and touch a lot of SQL code where there is a huge difference in site performance between a 1 second execution and 20ms execution, then <em>sometimes</em> you don&#8217;t have much of an option.</p>
<p>With that said, I will talk about the optimizer.  It&#8217;s getting smarter and smarter at dealing with the majority of queries and keeping them within an even keel baseline.  But that comes at a cost.  It can&#8217;t take as many chances so it plays it safe.  And this is where you come in.  But before you go around adding hints everywhere in your system (which is a horrible idea).  You need to know how to write code so that it will not NEED hints.  To do this is very simple.  The answer is KEEP IT SIMPLE.  In other words, keep the queries simple.  Do not create huge SQL Statements.  It&#8217;s much better to break the queries up so that you do not give the optimizer many different routes to take.  Breaking them up typically entails materializing the data into temp tables (write as little data as possible) so you can break up large queries into multiple small queries.</p>
<p>With that said, let&#8217;s get onto the hints.  I&#8217;m not going to go over all of them, just the ones I use the most.</p>
<h4>LOOP JOIN</h4>
<p>This hint works in two ways.  Often times when you are joining two tables, one table will be much smaller and IF the optimizer chose to join using this table first, you would have a very fast query.  The inner loop join, changes the join order forcing these two tables to FIRST limit the result set by joining on each other first, AND it does so using a SEEK.  The important thing to note about this technique, is that both of the join columns need to be indexed.  If you try a loop join on two non indexed columns you will be in trouble.</p>
<p>To do an inner loop join, do the following:</p>
<p>[cc lang=&#8221;sql&#8221;]<br />
SELECT *<br />
FROM table_a a<br />
INNER LOOP JOIN table_b b ON b.id = a.id;<br />
[/cc]</p>
<p>Test the outcome before you do it.  I usually only do this as an emergency measure, or a way to squeeze another couple hundred ms out of a highly called proc.  I have never seen INNER LOOP JOIN error out.  The optimizer has always been able to create an execution plan.</p>
<h4>INDEX HINT</h4>
<p>This actually is probably the most common.  For whatever reason, the optimizer decides to use the wrong index.  Most of the time it&#8217;s because stats changed or possibly a parameter sniffing issue on the last compile.  This may be fine a lot of the time, but if it&#8217;s a critical process, it won&#8217;t fly, and you don&#8217;t want to be woken up at 2am.</p>
<p>[cc lang=&#8221;sql&#8221;]<br />
SELECT *<br />
FROM table_a a WITH (INDEX(ix_someindex))<br />
WHERE a.id > 50;<br />
[/cc]</p>
<h4>INNER HASH JOIN</h4>
<p>This join is the most efficient if you do not have indexes on the join columns.  It saves time because not only does the optimizer not have to consider which type of join to use, but since you know the join columns have no indexes (or they are small tables) there is no reason to do a loop join.  A hash join operates by create buckets of each side of the join columns, matching the buckets, then sorting each bucket to join the columns. That&#8217;s at least how I understand it.  I have not seen the optimizer fail by using a hash join hint.</p>
<p>[cc lang=&#8221;sql&#8221;]<br />
SELECT *<br />
FROM table_a a<br />
INNER HASH JOIN table_b b ON b.id = a.id;<br />
[/cc]</p>
<h4>INNER MERGE JOIN</h4>
<p>This is a hint I never touch.  The reason is because the optimizer CAN error out when trying to create a plan when this hint is applied.  This is because each column must be sorted before the join.  However, a merge join is the most efficient in both CPU and IO.  A really cool trick I learned from an Adam Machanic post is below (he advocated not to do this as well).  In theory this probably would not error out, but I would still not suggest doing it in production.  However this would join things VERY quickly in a lot of cases.  Again don&#8217;t do this</p>
<p>[cc lang=&#8221;sql&#8221;]<br />
SELECT *<br />
FROM table_a a<br />
INNER MERGE JOIN<br />
(<br />
  SELECT TOP (1000000000) &#8212; really large unreasonable number<br />
  FROM table_b b<br />
  ORDER BY b.id<br />
) b ON b.id = a.id;<br />
[/cc]</p>
<h4>MAXDOP</h4>
<p>This is one of the most common ones I use.  The reason is because sometimes there will be a process that is invoked from the website, that really does not benefit from parallelism, or the parallelism ends up eating a bunch of CPU.  Or on the flip side, a process that consumes a lot of data, needs a bit of a boost.  A setting of 1, turns parallelism off.  A setting higher than that, basically assigns the same number of spids to the number you assign.</p>
<p>[cc lang=&#8221;sql&#8221;]<br />
&#8212; turn parallelism off<br />
SELECT *<br />
FROM table_a a<br />
INNER JOIN table_b b ON b.id = a.id<br />
OPTION (MAXDOP 1);</p>
<p>&#8212; turn it on to use 4 processors<br />
SELECT *<br />
FROM table_a a<br />
INNER JOIN table_b b ON b.id = a.id<br />
OPTION (MAXDOP 4);<br />
[/cc]</p>
<h4>FORCEORDER</h4>
<p>I do not advocate using this one.  What it&#8217;s supposed to do, never really works in my opinion.  Forceorder should process the order of events from top to bottom.  Utilizing the driver table (first table you specify) as being the lead table to limit the result set.  For me, this simply does not work.  The last time it worked for me was in SQL 2000.  A better way to force the order of your query, is to force the order of events using virtual tables.</p>
<p>[cc lang=&#8221;sql&#8221;]<br />
&#8212; this will in most cases, force the processing of table_a first<br />
SELECT *<br />
FROM (<br />
  SELECT a.id<br />
  FROM<br />
  (<br />
    SELECT a.id<br />
    FROM table_a<br />
    WHERE a.id = (1,4)<br />
  ) t1<br />
) t2<br />
INNER JOIN table_b b ON b.id = t2.id;<br />
[/cc]</p>
<h4>OPTION (RECOMPILE)</h4>
<p>This is another very common one.  It usually gets placed on the big fat query you can&#8217;t touch (or don&#8217;t want to break up).  It gets a bad hop on an execution plan one too many times and you say fock it, I&#8217;m going to throw this on so the bad hops don&#8217;t happen.  Hey.. I&#8217;m just being real.  But having to use this one too many times typically means your queries are overly complicated.  You can probably benefit from breaking them up.</p>
<p>[cc lang=&#8221;sql&#8221;]<br />
SELECT *<br />
FROM table_a a<br />
INNER JOIN table_b b ON b.id = a.id<br />
INNER JOIN fat_table_c ON c.id = b.id<br />
INNER JOIN fatter_table_d ON d.id = c.id<br />
INNER JOIN fat_who_created_this_e e ON e.id = d.id<br />
OPTION (RECOMPILE);<br />
[/cc]<br />
That&#8217;s all folks.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlserverplanet.com/optimization/sql-server-hints-explained/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>What to do when your SQL CPU is at 100%</title>
		<link>https://sqlserverplanet.com/dba/what-to-do-when-your-sql-cpu-is-at-100</link>
					<comments>https://sqlserverplanet.com/dba/what-to-do-when-your-sql-cpu-is-at-100#respond</comments>
		
		<dc:creator><![CDATA[Derek Dieter]]></dc:creator>
		<pubDate>Thu, 06 Aug 2015 04:20:37 +0000</pubDate>
				<category><![CDATA[DBA]]></category>
		<guid isPermaLink="false">https://sqlserverplanet.com/?p=3775</guid>

					<description><![CDATA[We&#8217;ve all had it happen, alerts start going off that the CPU on the SQL Server is now pegged. It&#8217;s an all hands on deck situation that you need to figure out quick. It helps to know the history of your SQL Server performance, but if you don&#8217;t there&#8217;s still hope using some troubleshooting techniques. First Make sure it&#8217;s SQL Server Sometimes there can be other processes (even backups) that are causing the CPU to push over that final edge. If you don&#8217;t have access to log into the box, the quickest way to determine how much CPU SQL Server is using is to run the following DMV: [cc lang=&#8221;sql&#8221;] DECLARE @ts_now bigint SELECT @ts_now = cpu_ticks / (cpu_ticks/ms_ticks) FROM sys.dm_os_sys_info; SELECT record_id, dateadd(ms, -1 * (@ts_now &#8211; [timestamp]), GetDate()) as EventTime, SQLProcessUtilization, SystemIdle, 100 &#8211; SystemIdle &#8211; SQLProcessUtilization as OtherProcessUtilization FROM ( SELECT record.value(&#8216;(./Record/@id)[1]&#8217;, &#8216;int&#8217;) as record_id, record.value(&#8216;(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]&#8217;, &#8216;int&#8217;) as SystemIdle, record.value(&#8216;(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]&#8217;, &#8216;int&#8217;) as SQLProcessUtilization, TIMESTAMP FROM ( SELECT timestamp, CONVERT(xml, record) as record FROM sys.dm_os_ring_buffers WHERE ring_buffer_type = N&#8217;RING_BUFFER_SCHEDULER_MONITOR&#8217; AND record LIKE &#8216;%%&#8217;) as x ) as y ORDER BY record_id DESC; [/cc] If the OtherProcessUtilization column has a high value then you know you need to log into the box, open task manager and figure out what is hosing your system. It should be noted that you really should only run SQL Server on SQL Boxes, and if this is your issue, you&#8217;ll learn that today. If it is SQL Server, then it typically falls into one [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>We&#8217;ve all had it happen, alerts start going off that the CPU on the SQL Server is now pegged.  It&#8217;s an all hands on deck situation that you need to figure out quick.  It helps to know the history of your SQL Server performance, but if you don&#8217;t there&#8217;s still hope using some troubleshooting techniques.</p>
<h4>First Make sure it&#8217;s SQL Server</h4>
<p>Sometimes there can be other processes (even backups) that are causing the CPU to push over that final edge.  If you don&#8217;t have access to log into the box, the quickest way to determine how much CPU SQL Server is using is to run the following DMV:</p>
<p>[cc lang=&#8221;sql&#8221;]<br />
DECLARE @ts_now bigint</p>
<p>SELECT @ts_now = cpu_ticks / (cpu_ticks/ms_ticks) FROM sys.dm_os_sys_info;</p>
<p>SELECT<br />
	record_id,<br />
	dateadd(ms, -1 * (@ts_now &#8211; [timestamp]), GetDate()) as EventTime,<br />
	SQLProcessUtilization,<br />
	SystemIdle,<br />
	100 &#8211; SystemIdle &#8211; SQLProcessUtilization as OtherProcessUtilization<br />
FROM (<br />
	SELECT<br />
	record.value(&#8216;(./Record/@id)[1]&#8217;, &#8216;int&#8217;) as record_id,<br />
	record.value(&#8216;(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]&#8217;, &#8216;int&#8217;) as SystemIdle,<br />
	record.value(&#8216;(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]&#8217;, &#8216;int&#8217;) as SQLProcessUtilization,<br />
	TIMESTAMP<br />
	FROM (<br />
		SELECT timestamp, CONVERT(xml, record) as record<br />
		FROM sys.dm_os_ring_buffers<br />
		WHERE ring_buffer_type = N&#8217;RING_BUFFER_SCHEDULER_MONITOR&#8217;<br />
		AND record LIKE &#8216;%<SystemHealth>%&#8217;) as x<br />
) as y<br />
ORDER BY record_id DESC;<br />
[/cc]</p>
<p>If the OtherProcessUtilization column has a high value then you know you need to log into the box, open task manager and figure out what is hosing your system.  It should be noted that you really should only run SQL Server on SQL Boxes, and if this is your issue, you&#8217;ll learn that today.</p>
<h4>If it is SQL Server, then it typically falls into one of three categories.</h4>
<h3>First Category</h3>
<p>You have a procedure that has caught a bad execution plan hop and it either needs to be recompiled, or have a hint applied.  To figure this out, you need to run <a href="https://sqlserverplanet.com/dba/a-better-sp_who2-using-dmvs-sp_who3" target="_blank">sp_who3</a>.  Compile this procedure into your master database and run it.  If after running it you see multiple spids (rows) with the same ObjectName or SQLStatement, then you have a bad plan.  Consider recompiling the procedure using sp_recompile:</p>
<p>[cc lang=&#8221;sql&#8221;]<br />
exec sp_recompile &#8216;pr_procedure_name&#8217;;<br />
[/cc]</p>
<p>If that does not work, you may want to consider flushing the entire procedure cache.  Take note of the procedure name before you do this, as it may not show up in subsequent sp_who3 executions in case you need to find it again.  You will need ALTER SERVER STATE permissions to perform this:</p>
<p>[cc lang=&#8221;sql&#8221;]<br />
DBCC FREEPROCCACHE<br />
[/cc]</p>
<p>If that does not work, you may need to open the procedure and make a modification.  Unfortunately that a big fish we won&#8217;t be able to fry in this article.  Your options could range from creating a new index for the bad query, forcing an index for the bad query, or applying a force order hint (like loop join).  </p>
<h3>Second Category</h3>
<p>If the above is not the issue, you may have a single process that has gone rogue.  This is more of an issue if you don&#8217;t have many CPU cores.  The more CPU cores you have, the less this will likely impact you UNLESS you have your Server instance&#8217;s max degree of parallelism setting set too high, along with Query threshold for parallelism.  See <a href="https://sqlserverplanet.com/dba/configuring-parallelism" target="_blank">this article</a> to learn how to configure that.  However, it you do have a single process that has gone rogue, use the same process above to recompile it, or kill it if necessary.  To kill it you need to run <a href="https://sqlserverplanet.com/dba/a-better-sp_who2-using-dmvs-sp_who3">sp_who3</a> to find out what the <a href="https://sqlserverplanet.com/dba/spid-what-is-it">SPID</a> is, and use the kill command to kill it.  Do not kill a spid if it is 50 or lower.  These are internal SPIDs used by SQL Server.</p>
<p>[cc lang=&#8221;sql&#8221;]<br />
KILL 555<br />
[/cc]</p>
<h3>Third Category</h3>
<p>You have a lot of missing indexes you have not taken care of, but viola, you now need to add them.  This is usually the case in most SQL Server instances that do not receive a lot of love.  Developers keep pushing code and all is well until TODAY.  Now you need to add indexes.  If this is the case, you will simply see a lot of SPIDs (rows) show up when you run <a href="https://sqlserverplanet.com/dba/a-better-sp_who2-using-dmvs-sp_who3" target="_blank">sp_who3</a>.  Your best bet (if you should choose) is to deploy the <a href="https://sqlserverplanet.com/dmvs/missing-indexes-dmv" target="_blank">top 3 missing indexes</a> into your system to see if that has an effect.  And if your system does not get a lot of love, it very likely will.  As one DBA asked me after doing this for a client, &#8220;Did we just unplug all the webservers?&#8221;  (Because CPU went down by like 60%).  I linked to it already, but you can find the DMV script which will display <a href="https://sqlserverplanet.com/dmvs/missing-indexes-dmv" target="_blank">the missing indexes here.</a>  Just copy the index, name it properly, and deploy it.  You should add the WITH (ONLINE=ON) if you are running SQL Server Enterprise Edition in production.<br />
[cc lang=&#8221;sql&#8221;]<br />
CREATE INDEX ix_someindex ON tablename (column1,column2) INCLUDE (momo1, momo2) WITH (ONLINE=ON);<br />
[/cc]<br />
That&#8217;s it, hopefully that helped you with your issue.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlserverplanet.com/dba/what-to-do-when-your-sql-cpu-is-at-100/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Convert Int to String</title>
		<link>https://sqlserverplanet.com/tsql/convert-int-to-string</link>
					<comments>https://sqlserverplanet.com/tsql/convert-int-to-string#comments</comments>
		
		<dc:creator><![CDATA[Derek Dieter]]></dc:creator>
		<pubDate>Wed, 05 Aug 2015 05:37:51 +0000</pubDate>
				<category><![CDATA[TSQL]]></category>
		<guid isPermaLink="false">https://sqlserverplanet.com/?p=3771</guid>

					<description><![CDATA[There are two different functions that can be used when converting an integer to a string. One is CAST and the other is CONVERT. Either of these functions can be used with the exact same result when converting to a string. The only difference with the CONVERT function is that it takes an extra optional parameter for style which can affect the way certain data types are displayed after they are converted (an example is with date/time formats). The common need to convert an INT to a string is to then concatenate it with either another int or an existing string. Here is a simple example: [cc lang=&#8221;sql&#8221;] SELECT CAST(12345 AS VARCHAR(11)) [/cc] And the output: Here we are casting the int to a varchar(11). This is a safe value for us to convert to because the maximum integer value is -2147483648. We see if we try to convert an integer to a smaller string, it returns back an asterisks (*) meaning an error has occurred. [cc lang=&#8221;sql&#8221;] SELECT CAST(-2147483648 AS VARCHAR(10)) [/cc] So it is important to choose a data type that is large enough. With that said, I have seen the following work as well, however I personally would not run this in production in case it is deprecated in a future version of SQL. [cc lang=&#8221;sql&#8221;] SELECT CAST(-2147483648 AS VARCHAR) [/cc]]]></description>
										<content:encoded><![CDATA[<p>There are two different functions that can be used when converting an integer to a string.  One is CAST and the other is CONVERT.  Either of these functions can be used with the exact same result when converting to a string.  The only difference with the CONVERT function is that it takes an extra optional parameter for <em>style</em> which can affect the way certain data types are displayed after they are converted (an example is with date/time formats).</p>
<p>The common need to convert an INT to a string is to then concatenate it with either another int or an existing string.</p>
<p>Here is a simple example:<br />
[cc lang=&#8221;sql&#8221;]<br />
SELECT CAST(12345 AS VARCHAR(11))<br />
[/cc]<br />
And the output:<br />
<a href="https://sqlserverplanet.com/wp-content/uploads/2015/08/cast_int_to_varchar.png"><img fetchpriority="high" decoding="async" src="https://sqlserverplanet.com/wp-content/uploads/2015/08/cast_int_to_varchar.png" alt="cast_int_to_varchar" width="397" height="148" class="aligncenter size-full wp-image-3772" srcset="https://sqlserverplanet.com/wp-content/uploads/2015/08/cast_int_to_varchar.png 397w, https://sqlserverplanet.com/wp-content/uploads/2015/08/cast_int_to_varchar-300x112.png 300w" sizes="(max-width: 397px) 100vw, 397px" /></a></p>
<p>Here we are casting the int to a varchar(11).  This is a safe value for us to convert to because the maximum integer value is -2147483648.</p>
<p>We see if we try to convert an integer to a smaller string, it returns back an asterisks (*) meaning an error has occurred.<br />
[cc lang=&#8221;sql&#8221;]<br />
SELECT CAST(-2147483648 AS VARCHAR(10))<br />
[/cc]<br />
<a href="https://sqlserverplanet.com/wp-content/uploads/2015/08/convert_int_to_varchar_error.png"><img decoding="async" src="https://sqlserverplanet.com/wp-content/uploads/2015/08/convert_int_to_varchar_error.png" alt="convert_int_to_varchar_error" width="441" height="158" class="aligncenter size-full wp-image-3773" srcset="https://sqlserverplanet.com/wp-content/uploads/2015/08/convert_int_to_varchar_error.png 441w, https://sqlserverplanet.com/wp-content/uploads/2015/08/convert_int_to_varchar_error-300x107.png 300w" sizes="(max-width: 441px) 100vw, 441px" /></a></p>
<p>So it is important to choose a data type that is large enough.</p>
<p>With that said, I have seen the following work as well, however I personally would not run this in production in case it is deprecated in a future version of SQL.</p>
<p>[cc lang=&#8221;sql&#8221;]<br />
SELECT CAST(-2147483648 AS VARCHAR)<br />
[/cc]</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlserverplanet.com/tsql/convert-int-to-string/feed</wfw:commentRss>
			<slash:comments>3</slash:comments>
		
		
			</item>
		<item>
		<title>Export Table to CSV File &#8211; step by step</title>
		<link>https://sqlserverplanet.com/dba/export-table-to-csv</link>
					<comments>https://sqlserverplanet.com/dba/export-table-to-csv#respond</comments>
		
		<dc:creator><![CDATA[Derek Dieter]]></dc:creator>
		<pubDate>Wed, 05 Aug 2015 05:00:29 +0000</pubDate>
				<category><![CDATA[DBA]]></category>
		<guid isPermaLink="false">https://sqlserverplanet.com/?p=3755</guid>

					<description><![CDATA[A very common requirement with SQL Server is to export a table to a csv file. It&#8217;s actually an easy task when using SQL Server Management Studio (SSMS). The export method actually behind the scenes uses SSIS wizard which also gives you an option to save the package that performs the export, however saving the package is often not necessary. The permissions you need to perform this, are merely read permissions on the table. Here are the steps broken out in detail. Right-click on the database that contains the table and choose tasks -> Export Data&#8230; Click next past the intro screen and on the &#8220;Choose a Data Source Screen&#8221; make sure the database that contains your table is listed in the drop down. Then Select Next. On the &#8220;Choose a Destination&#8221; Window, select &#8220;Flat File Destination&#8221; in the top drop down box labeled &#8220;Destination&#8221;. On the same &#8220;Choose a Destination&#8221; Window, click the &#8220;Browse&#8221; button and the Open file dialog box appears prompting you to find a location for your file. Find the location, then type in the file name. THEN to the right of the file name text box, change the drop down from &#8220;Text Files (*.txt)&#8221; to &#8220;CSV Files (*.csv)&#8221; As shown below. While still on the same &#8220;Choose a Destination&#8221; window, select whether you want the column names to be included in the CSV by selecting the &#8220;Column names in first data row&#8221; check box. Then Select Next. On the &#8220;Specify Table Copy or Query&#8221; window [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>A very common requirement with SQL Server is to export a table to a csv file.  It&#8217;s actually an easy task when using SQL Server Management Studio (SSMS).  The export method actually behind the scenes uses SSIS wizard which also gives you an option to save the package that performs the export, however saving the package is often not necessary.  The permissions you need to perform this, are merely read permissions on the table.</p>
<p>Here are the steps broken out in detail.</p>
<h4>Right-click on the database that contains the table and choose <strong>tasks -> Export Data&#8230;</strong><br />
<a href="https://sqlserverplanet.com/wp-content/uploads/2015/08/export_as_csv_menu_context.png"><img decoding="async" src="https://sqlserverplanet.com/wp-content/uploads/2015/08/export_as_csv_menu_context.png" alt="export_as_csv_menu_context" width="661" height="503" class="aligncenter size-full wp-image-3756" srcset="https://sqlserverplanet.com/wp-content/uploads/2015/08/export_as_csv_menu_context.png 661w, https://sqlserverplanet.com/wp-content/uploads/2015/08/export_as_csv_menu_context-300x228.png 300w" sizes="(max-width: 661px) 100vw, 661px" /></a></p>
<h4>Click next past the intro screen and on the &#8220;Choose a Data Source Screen&#8221; make sure the database that contains your table is listed in the drop down.  Then Select Next.</h4>
<p><a href="https://sqlserverplanet.com/wp-content/uploads/2015/08/export_as_csv_menu_data_source.png"><img loading="lazy" decoding="async" src="https://sqlserverplanet.com/wp-content/uploads/2015/08/export_as_csv_menu_data_source.png" alt="export_as_csv_menu_data_source" width="566" height="579" class="aligncenter size-full wp-image-3757" srcset="https://sqlserverplanet.com/wp-content/uploads/2015/08/export_as_csv_menu_data_source.png 566w, https://sqlserverplanet.com/wp-content/uploads/2015/08/export_as_csv_menu_data_source-293x300.png 293w, https://sqlserverplanet.com/wp-content/uploads/2015/08/export_as_csv_menu_data_source-50x50.png 50w" sizes="auto, (max-width: 566px) 100vw, 566px" /></a></p>
<h4>On the &#8220;Choose a Destination&#8221; Window, select &#8220;Flat File Destination&#8221; in the top drop down box labeled &#8220;Destination&#8221;.</h4>
<p><a href="https://sqlserverplanet.com/wp-content/uploads/2015/08/export_as_csv_flat_file_destination.png"><img loading="lazy" decoding="async" src="https://sqlserverplanet.com/wp-content/uploads/2015/08/export_as_csv_flat_file_destination.png" alt="export_as_csv_flat_file_destination" width="566" height="579" class="aligncenter size-full wp-image-3758" srcset="https://sqlserverplanet.com/wp-content/uploads/2015/08/export_as_csv_flat_file_destination.png 566w, https://sqlserverplanet.com/wp-content/uploads/2015/08/export_as_csv_flat_file_destination-293x300.png 293w, https://sqlserverplanet.com/wp-content/uploads/2015/08/export_as_csv_flat_file_destination-50x50.png 50w" sizes="auto, (max-width: 566px) 100vw, 566px" /></a></p>
<h4>On the same &#8220;Choose a Destination&#8221; Window, click the &#8220;Browse&#8221; button and the Open file dialog box appears prompting you to find a location for your file.  Find the location, then type in the file name.  THEN to the right of the file name text box, change the drop down from &#8220;Text Files (*.txt)&#8221; to &#8220;CSV Files (*.csv)&#8221;  As shown below.</h4>
<p><a href="https://sqlserverplanet.com/wp-content/uploads/2015/08/csv_drop_down.png"><img loading="lazy" decoding="async" src="https://sqlserverplanet.com/wp-content/uploads/2015/08/csv_drop_down.png" alt="csv_drop_down" width="884" height="475" class="aligncenter size-full wp-image-3759" srcset="https://sqlserverplanet.com/wp-content/uploads/2015/08/csv_drop_down.png 884w, https://sqlserverplanet.com/wp-content/uploads/2015/08/csv_drop_down-300x161.png 300w" sizes="auto, (max-width: 884px) 100vw, 884px" /></a></p>
<h4>While still on the same &#8220;Choose a Destination&#8221; window, select whether you want the column names to be included in the CSV by selecting the &#8220;Column names in first data row&#8221; check box. Then Select Next.</h4>
<p><a href="https://sqlserverplanet.com/wp-content/uploads/2015/08/csv_column_names_in_first_data_row.png"><img loading="lazy" decoding="async" src="https://sqlserverplanet.com/wp-content/uploads/2015/08/csv_column_names_in_first_data_row.png" alt="csv_column_names_in_first_data_row" width="564" height="576" class="aligncenter size-full wp-image-3760" srcset="https://sqlserverplanet.com/wp-content/uploads/2015/08/csv_column_names_in_first_data_row.png 564w, https://sqlserverplanet.com/wp-content/uploads/2015/08/csv_column_names_in_first_data_row-294x300.png 294w, https://sqlserverplanet.com/wp-content/uploads/2015/08/csv_column_names_in_first_data_row-50x50.png 50w" sizes="auto, (max-width: 564px) 100vw, 564px" /></a></p>
<h4>On the &#8220;Specify Table Copy or Query&#8221; window make sure &#8220;Copy data from one or more tables or views&#8221; is selected then click Next.</h4>
<p><a href="https://sqlserverplanet.com/wp-content/uploads/2015/08/copy_data_from_table.png"><img loading="lazy" decoding="async" src="https://sqlserverplanet.com/wp-content/uploads/2015/08/copy_data_from_table.png" alt="copy_data_from_table" width="563" height="574" class="aligncenter size-full wp-image-3761" srcset="https://sqlserverplanet.com/wp-content/uploads/2015/08/copy_data_from_table.png 563w, https://sqlserverplanet.com/wp-content/uploads/2015/08/copy_data_from_table-294x300.png 294w, https://sqlserverplanet.com/wp-content/uploads/2015/08/copy_data_from_table-50x50.png 50w" sizes="auto, (max-width: 563px) 100vw, 563px" /></a></p>
<h4>On the &#8220;Configure Flat File Destination&#8221; window, select your source table or view.  Leave the Row delimiter and column delimiter as the default then click Next.</h4>
<p><a href="https://sqlserverplanet.com/wp-content/uploads/2015/08/configure_flat_file_destination.png"><img loading="lazy" decoding="async" src="https://sqlserverplanet.com/wp-content/uploads/2015/08/configure_flat_file_destination.png" alt="configure_flat_file_destination" width="566" height="579" class="aligncenter size-full wp-image-3762" srcset="https://sqlserverplanet.com/wp-content/uploads/2015/08/configure_flat_file_destination.png 566w, https://sqlserverplanet.com/wp-content/uploads/2015/08/configure_flat_file_destination-293x300.png 293w, https://sqlserverplanet.com/wp-content/uploads/2015/08/configure_flat_file_destination-50x50.png 50w" sizes="auto, (max-width: 566px) 100vw, 566px" /></a></p>
<h4>The &#8220;Save and run package&#8221; window now appears.  Make sure &#8220;Run immediately&#8221; is selected then click Next.</h4>
<p><a href="https://sqlserverplanet.com/wp-content/uploads/2015/08/save-and-run-package.png"><img loading="lazy" decoding="async" src="https://sqlserverplanet.com/wp-content/uploads/2015/08/save-and-run-package.png" alt="save and run package" width="566" height="579" class="aligncenter size-full wp-image-3763" srcset="https://sqlserverplanet.com/wp-content/uploads/2015/08/save-and-run-package.png 566w, https://sqlserverplanet.com/wp-content/uploads/2015/08/save-and-run-package-293x300.png 293w, https://sqlserverplanet.com/wp-content/uploads/2015/08/save-and-run-package-50x50.png 50w" sizes="auto, (max-width: 566px) 100vw, 566px" /></a></p>
<h4>Finally the &#8220;Complete the wizard&#8221; window appears.  The details with show &#8220;Copy rows from [your table] to [your csv location].  If this is correct, click &#8220;Finish&#8221;.<br />
<a href="https://sqlserverplanet.com/wp-content/uploads/2015/08/complete_the_wizard.png"><img loading="lazy" decoding="async" src="https://sqlserverplanet.com/wp-content/uploads/2015/08/complete_the_wizard.png" alt="complete_the_wizard" width="566" height="579" class="aligncenter size-full wp-image-3764" srcset="https://sqlserverplanet.com/wp-content/uploads/2015/08/complete_the_wizard.png 566w, https://sqlserverplanet.com/wp-content/uploads/2015/08/complete_the_wizard-293x300.png 293w, https://sqlserverplanet.com/wp-content/uploads/2015/08/complete_the_wizard-50x50.png 50w" sizes="auto, (max-width: 566px) 100vw, 566px" /></a></p>
<h4>The execution actions will now be displayed along with the status of the export.  If it finishes successfully, you will see the following screen.</h4>
<p><a href="https://sqlserverplanet.com/wp-content/uploads/2015/08/execution_was_successful.png"><img loading="lazy" decoding="async" src="https://sqlserverplanet.com/wp-content/uploads/2015/08/execution_was_successful.png" alt="execution_was_successful" width="566" height="579" class="aligncenter size-full wp-image-3765" srcset="https://sqlserverplanet.com/wp-content/uploads/2015/08/execution_was_successful.png 566w, https://sqlserverplanet.com/wp-content/uploads/2015/08/execution_was_successful-293x300.png 293w, https://sqlserverplanet.com/wp-content/uploads/2015/08/execution_was_successful-50x50.png 50w" sizes="auto, (max-width: 566px) 100vw, 566px" /></a></p>
<p>That&#8217;s it!  If your execution was not successful, check to see what the error messages say.  Make sure you have access to the file location where you want to write the CSV file to.  Good luck!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlserverplanet.com/dba/export-table-to-csv/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Search Procedure Text</title>
		<link>https://sqlserverplanet.com/dba/search-procedure-text-2</link>
					<comments>https://sqlserverplanet.com/dba/search-procedure-text-2#respond</comments>
		
		<dc:creator><![CDATA[Derek Dieter]]></dc:creator>
		<pubDate>Sun, 31 May 2015 07:16:14 +0000</pubDate>
				<category><![CDATA[DBA]]></category>
		<guid isPermaLink="false">https://sqlserverplanet.com/?p=3664</guid>

					<description><![CDATA[This has to be one of the most common repetitive functions needed for development. Updating a certain column in a specific table and want to find all the procedures that are affected by that? You&#8217;ll need to use an object search. There are a few different methods for doing this. The ANSI standard method using the INFORMATION_SCHEMA.ROUTINES system views have not in the past contained all of the stored procedure text. Only the first 8000 characters or so. (If I&#8217;m wrong on that, don&#8217;t challenge me because I don&#8217;t care). Simple snippet [cc lang=&#8221;sql&#8221;] SELECT sm.*, so.* FROM sys.sql_modules sm WITH (NOLOCK) INNER JOIN sys.objects so WITH (NOLOCK) ON sm.[object_id] = so.[object_id] WHERE sm.definition LIKE &#8216;%table_name%&#8217; [/cc] However if you&#8217;re going to be using this search a lot, and I can&#8217;t imagine you&#8217;re not, then why not encapsulate it in a stored procedure so you can include multiple terms, exclude terms, and sort. You will have to place this procedure in each database you want to search however. Stored Procedure: sp_search [cc lang=&#8221;sql&#8221;] USE master GO CREATE PROCEDURE spsearch @include varchar(max) = NULL, @sort varchar(5) = 1, &#8212; 1 =&#8217;name_asc&#8217; 2 = &#8216;name_desc&#8217; 3 = &#8216;date_desc&#8217;, 4 = &#8216;date_asc&#8217; @exclude varchar(max) = NULL AS &#8212; EXEC sp_search &#8216;UPDATE,products,status&#8217;, 3 BEGIN SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED; DECLARE @Delimiter varchar(max) = &#8216;,&#8217; IF OBJECT_ID(N&#8217;tempdb..#split&#8217;) IS NOT NULL DROP TABLE #split ;WITH split(stpos,endpos) AS( SELECT 0 AS stpos, CHARINDEX(@delimiter,@include) AS endpos UNION ALL SELECT CAST(endpos+1 as int), CHARINDEX(@delimiter,@include,endpos+1) FROM split WHERE endpos [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>This has to be one of the most common repetitive functions needed for development.  Updating a certain column in a specific table and want to find all the procedures that are affected by that?  You&#8217;ll need to use an object search.  There are a few different methods for doing this.  The ANSI standard method using the INFORMATION_SCHEMA.ROUTINES system views have not in the past contained all of the stored procedure text.  Only the first 8000 characters or so.  (If I&#8217;m wrong on that, don&#8217;t challenge me because I don&#8217;t care).</p>
<h4>Simple snippet</h4>
<p>[cc lang=&#8221;sql&#8221;]<br />
SELECT sm.*, so.*<br />
FROM sys.sql_modules sm WITH (NOLOCK)<br />
INNER JOIN sys.objects so WITH (NOLOCK)<br />
ON sm.[object_id] = so.[object_id]<br />
WHERE sm.definition LIKE &#8216;%table_name%&#8217;<br />
[/cc]</p>
<p>However if you&#8217;re going to be using this search a lot, and I can&#8217;t imagine you&#8217;re not, then why not encapsulate it in a stored procedure so you can include multiple terms, exclude terms, and sort.  You will have to place this procedure in each database you want to search however.</p>
<h4>Stored Procedure: sp_search</h4>
<p>[cc lang=&#8221;sql&#8221;]<br />
USE master<br />
GO</p>
<p>CREATE PROCEDURE spsearch<br />
	@include varchar(max) = NULL,<br />
	@sort varchar(5) = 1, &#8212; 1 =&#8217;name_asc&#8217; 2 = &#8216;name_desc&#8217; 3 = &#8216;date_desc&#8217;, 4 = &#8216;date_asc&#8217;<br />
	@exclude varchar(max) = NULL</p>
<p>AS<br />
&#8212; EXEC sp_search &#8216;UPDATE,products,status&#8217;, 3<br />
BEGIN</p>
<p>	SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;</p>
<p>	DECLARE @Delimiter varchar(max) = &#8216;,&#8217;</p>
<p>	IF OBJECT_ID(N&#8217;tempdb..#split&#8217;) IS NOT NULL DROP TABLE #split</p>
<p>	;WITH split(stpos,endpos)<br />
	AS(<br />
		SELECT 0 AS stpos, CHARINDEX(@delimiter,@include) AS endpos<br />
		UNION ALL<br />
		SELECT CAST(endpos+1 as int), CHARINDEX(@delimiter,@include,endpos+1)<br />
			FROM split<br />
			WHERE endpos > 0<br />
	)<br />
	SELECT &#8216;Id&#8217; = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),<br />
		&#8216;Data&#8217; = &#8216;%&#8217; + SUBSTRING(@include,stpos,COALESCE(NULLIF(endpos,0),LEN(@include)+1)-stpos) + &#8216;%&#8217;<br />
	INTO #split<br />
	FROM split;</p>
<p>	IF ((SELECT MAX(id) FROM #split) > 3)<br />
	BEGIN<br />
		SELECT &#8216;cannot specify more than 3 include criteria&#8217;<br />
		RETURN<br />
	END</p>
<p>	;WITH split(stpos,endpos)<br />
	AS(<br />
		SELECT 0 AS stpos, CHARINDEX(@delimiter,@exclude) AS endpos<br />
		UNION ALL<br />
		SELECT CAST(endpos+1 as int), CHARINDEX(@delimiter,@exclude,endpos+1)<br />
			FROM split<br />
			WHERE endpos > 0<br />
	)<br />
	SELECT &#8216;Id&#8217; = ROW_NUMBER() OVER (ORDER BY (SELECT 1)),<br />
		&#8216;Data&#8217; = &#8216;%&#8217; + SUBSTRING(@exclude,stpos,COALESCE(NULLIF(endpos,0),LEN(@exclude)+1)-stpos) + &#8216;%&#8217;<br />
	INTO #split_exclude<br />
	FROM split<br />
	WHERE @exclude IS NOT NULL;</p>
<p>	IF ((SELECT MAX(id) FROM #split_exclude) > 3)<br />
	BEGIN<br />
		SELECT &#8216;cannot specify more than 3 exclude criteria&#8217;<br />
		RETURN<br />
	END</p>
<p>	SELECT<br />
		so.name,<br />
		so.type_desc,<br />
		so.create_date,<br />
		so.modify_date,<br />
		sm.[definition]<br />
	FROM sys.sql_modules sm WITH (NOLOCK)<br />
	INNER JOIN sys.objects so WITH (NOLOCK)<br />
	ON so.[object_id] = sm.[object_id]<br />
	WHERE<br />
	(<br />
		(<br />
			NOT EXISTS (SELECT data FROM #split WHERE id = 1)<br />
			OR<br />
			sm.definition LIKE (SELECT data FROM #split WHERE id = 1)<br />
		)<br />
		AND<br />
		(<br />
			NOT EXISTS (SELECT data FROM #split WHERE id = 2)<br />
			OR<br />
			sm.definition LIKE (SELECT data FROM #split WHERE id = 2)<br />
		)<br />
		AND<br />
		(<br />
			NOT EXISTS (SELECT data FROM #split WHERE id = 3)<br />
			OR<br />
			sm.definition LIKE (SELECT data FROM #split WHERE id = 3)<br />
		)<br />
	)<br />
	AND<br />
	(<br />
		(<br />
			NOT EXISTS (SELECT data FROM #split_exclude WHERE id = 1)<br />
			OR<br />
			sm.definition NOT LIKE (SELECT data FROM #split_exclude WHERE id = 1)<br />
		)<br />
		AND<br />
		(<br />
			NOT EXISTS (SELECT data FROM #split_exclude WHERE id = 2)<br />
			OR<br />
			sm.definition NOT LIKE (SELECT data FROM #split_exclude WHERE id = 2)<br />
		)<br />
		AND<br />
		(<br />
			NOT EXISTS (SELECT data FROM #split_exclude WHERE id = 3)<br />
			OR<br />
			sm.definition NOT LIKE (SELECT data FROM #split_exclude WHERE id = 3)<br />
		)<br />
	)<br />
	ORDER BY<br />
		CASE WHEN @sort = &#8216;1&#8217; THEN so.name END ASC,<br />
		CASE WHEN @sort = &#8216;2&#8217; THEN so.name END DESC,<br />
		CASE WHEN @sort = &#8216;3&#8217; THEN so.modify_date END DESC,<br />
		CASE WHEN @sort = &#8216;3&#8217; THEN so.create_date END ASC</p>
<p>END<br />
[/cc]</p>
<h4>Example Usage</h4>
<ul>
<li>first parameter is a comma delimited list of phrases you want to match.  You do not need to add the wildcard % I did it for you in the code.</li>
<li>second parameter is a sort option.  &#8212; 1 =&#8217;name_asc&#8217; 2 = &#8216;name_desc&#8217; 3 = &#8216;date_desc&#8217;, 4 = &#8216;date_asc&#8217;</li>
<li>third parameter is a comma delimited list of phrases you want to exclude</li>
</ul>
<p>[cc lang=&#8221;sql&#8221;]<br />
&#8212; look for the three words listed and sort base modify date desc:<br />
EXEC sp_search &#8216;UPDATE,products,status&#8217;, 3<br />
&#8212; look for the three words listed and sort base name asc, and exclude a single word:<br />
EXEC sp_search &#8216;UPDATE,products,status&#8217;, 2, &#8216;customerid&#8217;<br />
[/cc]</p>
<p>That&#8217;s it!</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlserverplanet.com/dba/search-procedure-text-2/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>How to do basic Performance Tuning on Microsoft SQL Server</title>
		<link>https://sqlserverplanet.com/optimization/how-to-do-basic-performance-tuning-on-microsoft-sql-server</link>
					<comments>https://sqlserverplanet.com/optimization/how-to-do-basic-performance-tuning-on-microsoft-sql-server#comments</comments>
		
		<dc:creator><![CDATA[Sripal]]></dc:creator>
		<pubDate>Wed, 20 May 2015 07:23:28 +0000</pubDate>
				<category><![CDATA[Optimization]]></category>
		<guid isPermaLink="false">https://sqlserverplanet.com/?p=3649</guid>

					<description><![CDATA[1)  FIND THE CULPRITS Like other software’s, we need to understand that MS SQL server is also a computer program but a complex program. Here Microsoft written this complex program. So if there is any problem with the SQL server, we need to understand why this program is not running as we expected. From SQL Server we need to pull and push data as fast &#38; accurate as possible. If we face any issues, reasons may be SQL Server (complex program) needs certain hardware and installation settings which we are not providing properly. The way SQL Server implemented and the way it understands T-SQL code, we are not providing proper T-SQL code to it Even though MS SQL Server is a proprietary software, they provided us a lot of ways to understand the Server and what’s going on inside so that we can use it efficiently. If the SQL server is running without errors, first we need to calculate wait statistics of different threads. SQL server uses threads for every user request. Again a thread is nothing but another program inside complex program which is called SQL server (This thread is not Operating system thread on which SQL server installed. This is related to SQLOS thread which is a pseudo operating system for the SQL Server). We can find wait statistics using “sys.dm_os_wait_stats” DMV. There are many scripts online to query this view as per your needs. I like Paul Randal script(http://www.sqlskills.com/blogs/paul/wait-statistics-or-please-tell-me-where-it-hurts) [cc lang=&#8221;sql] WITH [Waits] AS (SELECT [wait_type], [wait_time_ms] [&#8230;]]]></description>
										<content:encoded><![CDATA[<h3><strong>1)  FIND THE CULPRITS</strong></h3>
<p>Like other software’s, we need to understand that MS SQL server is also a computer program but a complex program. Here Microsoft written this complex program. So if there is any problem with the SQL server, we need to understand why this program is not running as we expected.</p>
<p>From SQL Server we need to pull and push data as fast &amp; accurate as possible. If we face any issues, reasons may be</p>
<ul>
<li>SQL Server (complex program) needs certain hardware and installation settings which we are not providing properly.</li>
<li>The way SQL Server implemented and the way it understands T-SQL code, we are not providing proper T-SQL code to it</li>
</ul>
<p>Even though MS SQL Server is a proprietary software, they provided us a lot of ways to understand the Server and what’s going on inside so that we can use it efficiently.</p>
<p>If the SQL server is running without errors, first we need to calculate wait statistics of different threads. SQL server uses threads for every user request. Again a thread is nothing but another program inside complex program which is called SQL server (This thread is not Operating system thread on which SQL server installed. This is related to SQLOS thread which is a pseudo operating system for the SQL Server).</p>
<p>We can find wait statistics using “sys.dm_os_wait_stats” DMV. There are many scripts online to query this view as per your needs. I like Paul Randal script<a target="_blank" rel="nofollow">(http://www.sqlskills.com/blogs/paul/wait-statistics-or-please-tell-me-where-it-hurts)</a></p>
<p>[cc lang=&#8221;sql]</p>
<p>WITH [Waits] AS<br />
(SELECT<br />
[wait_type],<br />
[wait_time_ms] / 1000.0 AS [WaitS],<br />
([wait_time_ms] &#8211; [signal_wait_time_ms]) / 1000.0 AS [ResourceS],<br />
[signal_wait_time_ms] / 1000.0 AS [SignalS],<br />
[waiting_tasks_count] AS [WaitCount],<br />
100.0 * [wait_time_ms] / SUM ([wait_time_ms]) OVER() AS [Percentage],<br />
ROW_NUMBER() OVER(ORDER BY [wait_time_ms] DESC) AS [RowNum]<br />
FROM sys.dm_os_wait_stats<br />
WHERE [wait_type] NOT IN (<br />
N&#8217;BROKER_EVENTHANDLER&#8217;, N&#8217;BROKER_RECEIVE_WAITFOR&#8217;,<br />
N&#8217;BROKER_TASK_STOP&#8217;, N&#8217;BROKER_TO_FLUSH&#8217;,<br />
N&#8217;BROKER_TRANSMITTER&#8217;, N&#8217;CHECKPOINT_QUEUE&#8217;,<br />
N&#8217;CHKPT&#8217;, N&#8217;CLR_AUTO_EVENT&#8217;,<br />
N&#8217;CLR_MANUAL_EVENT&#8217;, N&#8217;CLR_SEMAPHORE&#8217;,<br />
N&#8217;DBMIRROR_DBM_EVENT&#8217;, N&#8217;DBMIRROR_EVENTS_QUEUE&#8217;,<br />
N&#8217;DBMIRROR_WORKER_QUEUE&#8217;, N&#8217;DBMIRRORING_CMD&#8217;,<br />
N&#8217;DIRTY_PAGE_POLL&#8217;, N&#8217;DISPATCHER_QUEUE_SEMAPHORE&#8217;,<br />
N&#8217;EXECSYNC&#8217;, N&#8217;FSAGENT&#8217;,<br />
N&#8217;FT_IFTS_SCHEDULER_IDLE_WAIT&#8217;, N&#8217;FT_IFTSHC_MUTEX&#8217;,<br />
N&#8217;HADR_CLUSAPI_CALL&#8217;, N&#8217;HADR_FILESTREAM_IOMGR_IOCOMPLETION&#8217;,<br />
N&#8217;HADR_LOGCAPTURE_WAIT&#8217;, N&#8217;HADR_NOTIFICATION_DEQUEUE&#8217;,<br />
N&#8217;HADR_TIMER_TASK&#8217;, N&#8217;HADR_WORK_QUEUE&#8217;,<br />
N&#8217;KSOURCE_WAKEUP&#8217;, N&#8217;LAZYWRITER_SLEEP&#8217;,<br />
N&#8217;LOGMGR_QUEUE&#8217;, N&#8217;ONDEMAND_TASK_QUEUE&#8217;,<br />
N&#8217;PWAIT_ALL_COMPONENTS_INITIALIZED&#8217;,<br />
N&#8217;QDS_PERSIST_TASK_MAIN_LOOP_SLEEP&#8217;,<br />
N&#8217;QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP&#8217;,<br />
N&#8217;REQUEST_FOR_DEADLOCK_SEARCH&#8217;, N&#8217;RESOURCE_QUEUE&#8217;,<br />
N&#8217;SERVER_IDLE_CHECK&#8217;, N&#8217;SLEEP_BPOOL_FLUSH&#8217;,<br />
N&#8217;SLEEP_DBSTARTUP&#8217;, N&#8217;SLEEP_DCOMSTARTUP&#8217;,<br />
N&#8217;SLEEP_MASTERDBREADY&#8217;, N&#8217;SLEEP_MASTERMDREADY&#8217;,<br />
N&#8217;SLEEP_MASTERUPGRADED&#8217;, N&#8217;SLEEP_MSDBSTARTUP&#8217;,<br />
N&#8217;SLEEP_SYSTEMTASK&#8217;, N&#8217;SLEEP_TASK&#8217;,<br />
N&#8217;SLEEP_TEMPDBSTARTUP&#8217;, N&#8217;SNI_HTTP_ACCEPT&#8217;,<br />
N&#8217;SP_SERVER_DIAGNOSTICS_SLEEP&#8217;, N&#8217;SQLTRACE_BUFFER_FLUSH&#8217;,<br />
N&#8217;SQLTRACE_INCREMENTAL_FLUSH_SLEEP&#8217;,<br />
N&#8217;SQLTRACE_WAIT_ENTRIES&#8217;, N&#8217;WAIT_FOR_RESULTS&#8217;,<br />
N&#8217;WAITFOR&#8217;, N&#8217;WAITFOR_TASKSHUTDOWN&#8217;,<br />
N&#8217;WAIT_XTP_HOST_WAIT&#8217;, N&#8217;WAIT_XTP_OFFLINE_CKPT_NEW_LOG&#8217;,<br />
N&#8217;WAIT_XTP_CKPT_CLOSE&#8217;, N&#8217;XE_DISPATCHER_JOIN&#8217;,<br />
N&#8217;XE_DISPATCHER_WAIT&#8217;, N&#8217;XE_TIMER_EVENT&#8217;)<br />
AND [waiting_tasks_count] &gt; 0<br />
)<br />
SELECT<br />
MAX ([W1].[wait_type]) AS [WaitType],<br />
CAST (MAX ([W1].[WaitS]) AS DECIMAL (16,2)) AS [Wait_S],<br />
CAST (MAX ([W1].[ResourceS]) AS DECIMAL (16,2)) AS [Resource_S],<br />
CAST (MAX ([W1].[SignalS]) AS DECIMAL (16,2)) AS [Signal_S],<br />
MAX ([W1].[WaitCount]) AS [WaitCount],<br />
CAST (MAX ([W1].[Percentage]) AS DECIMAL (5,2)) AS [Percentage],<br />
CAST ((MAX ([W1].[WaitS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgWait_S],<br />
CAST ((MAX ([W1].[ResourceS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgRes_S],<br />
CAST ((MAX ([W1].[SignalS]) / MAX ([W1].[WaitCount])) AS DECIMAL (16,4)) AS [AvgSig_S]<br />
FROM [Waits] AS [W1]<br />
INNER JOIN [Waits] AS [W2]<br />
ON [W2].[RowNum] &lt;= [W1].[RowNum]<br />
GROUP BY [W1].[RowNum]<br />
HAVING SUM ([W2].[Percentage]) &#8211; MAX ([W1].[Percentage]) &lt; 95; &#8212; percentage threshold<br />
GO</p>
<p><span style="line-height: 1.5">[/cc]</span></p>
<p><span style="line-height: 1.5">Please concentrate on top most rows first. They represent the maximum wait type. Also please read about different wait types on your top 10 rows online. You need to understand them perfectly so that you will take a good decision.</span></p>
<p><em><strong>For Example:</strong></em></p>
<p>If you have too much “PAGEIOLATCH_XX” that means a thread is waiting for data page reads from the disk into buffer (which is nothing but memory). We need to be very careful in understanding this scenario. It does not mean we have poor IO subsystem or less memory. You can solve this problem by increasing IO subsystem &amp; memory temporarily. But to find out permanent solution we need see why we are reading so much data from the disk? What type of SQL commands are behind this? Are we unnecessarily reading too much data instead of reading less data using filters like where clause? Is too much data read happening because of table scans or index scans? Can we convert them to index seeks by implementing or modifying existing indexes? Are we writing SQL queries which are misunderstood by SQL optimizer (another program inside complex SQL server program)?</p>
<p>We need to think in different angles and use different test cases to come up with a final solution. Each of the above wait type needs a different solution. Any DBA need to research them thoroughly before taking any action. But most of the time 60 to 70% of the problems will be solved by finding problematic T-SQL queries and tuning them.</p>
<h3><strong>2) FINDING PROBLEMATIC QUERIES</strong></h3>
<p>You can use following query to find out top 20 worst performing queries:</p>
<p>[cc lang=&#8221;sql]</p>
<p>SELECT TOP 20<br />
total_worker_time/execution_count AS Avg_CPU_Time<br />
,Execution_count<br />
,total_elapsed_time/execution_count as AVG_Run_Time<br />
,total_elapsed_time<br />
,(SELECT<br />
SUBSTRING(text,statement_start_offset/2+1,statement_end_offset<br />
) FROM sys.dm_exec_sql_text(sql_handle)<br />
) AS Query_Text<br />
FROM sys.dm_exec_query_stats<br />
ORDER BY Avg_CPU_Time DESC</p>
<p>[/cc]</p>
<p>What we need to observe here is even though a query can have maximum avg run time, but if it runs only once then the total effect on the server is low comparing to a query which has medium avg run time &amp; runs lots of times in a day.</p>
<h3><strong>3) FINE TUNING QUERIES</strong></h3>
<p>Fine tuning T-SQL queries is a very big concept. Fundamental thing we need to understand is how well we can write T-SQL queries and implement indexes etc&#8230; So that SQL optimizer (a program inside MSSQL program) will find an optimized plan to do what we wanted it to do. Every new release of a SQL Server, we will get more sophisticated optimizer so that it will cover our mistakes in writing optimized SQL queries and also any bugs related to previous optimizer program. Like any human being how intelligent he may be, if we can’t communicate properly to him then he will misunderstand us. Same goes with computer programs.</p>
<p>MSSQL Server uses advanced search &amp; sorting algorithms. If you are good at search and sorting algorithms then most of the time you can easily guess why SQL Server is taking particular action. The best book for understanding such algorithms is <strong>The Art of Computer Programming (Donald Knuth).</strong></p>
<p>When we examine queries which are need to be fine-tuned, we need to use Execution plan of those queries so that we can find out how SQL server is interpreting them.</p>
<p>I can’t cover all the aspects of execution plan here but on a basic level I can explain the things we need to consider in execution plan.</p>
<ul>
<li>First we need to find out which are the operators taking most of the query cost</li>
<li>If the operator is taking more cost then we need to see the reason for it. Most of the time scans will represent more cost than seeks. We need to examine why particular scan is happening (table scan or index scan) instead of index seek. We can solve this problem by implementing proper indexes on table columns but as with any complex computer program like SQL Server there is no fixed solution. For example if the table is small then scans are faster than seeks.</li>
<li>There are approximately 78 operators which represent various actions and decisions of the SQL Server execution plan. We need to study them in-depth so that we can understand them better to take proper action.</li>
</ul>
<p>Even if we implement proper indexes on tables &amp; write good T-SQL code, if the execution plan is not reused then it creates performance problems.</p>
<h3><strong>4) EXECUTION PLAN RE-USE</strong></h3>
<p>After fine tuning the queries, we need to make sure that execution plan will be reused when necessary. Most of the CPU time will be spent on calculating execution plan which can be eliminated if we re-use the plan.</p>
<p>You can use below query to find out execution plan re-use:</p>
<p>[cc lang=&#8221;sql]</p>
<p>SELECT [ecp].[refcounts]<br />
, [ecp].[usecounts]<br />
, [ecp].[objtype]<br />
, DB_NAME([est].[dbid]) AS [db_name]<br />
, [est].[objectid]<br />
, [est].[text] as [query_ext]<br />
, [eqp].[query_plan]<br />
FROM sys.dm_exec_cached_plans ecp<br />
CROSS APPLY sys.dm_exec_sql_text ( ecp.plan_handle ) est<br />
CROSS APPLY sys.dm_exec_query_plan ( ecp.plan_handle ) eqp</p>
<p>[/cc]</p>
<p>Here “usecounts” represents how many times plan reused.</p>
<p>The best way to re- use execution plan is by implementing parameterized stored procedures. When we are not in a position to implement stored procedures we can use “sp_executesql”.</p>
<p>But as with any complex computer program there is no fixed solution. Sometimes it is better to compile the plan again.</p>
<p><em><strong>For Example:</strong></em></p>
<ul>
<li>select name from table where name = &#8216;sri&#8217;;</li>
<li>select name from table where name = &#8216;pal&#8217;;</li>
</ul>
<p>Let us assume we have non-clustered index on “name&#8221; column and half of the table has value &#8216;sri&#8217; and few rows have &#8220;pal” in the “name” column. So for the 1st query SQL Server uses Table Scan because half of the table has same values. But for the 2nd query it is better to use Index Scan because only few rows have &#8216;pal&#8217; value.</p>
<p>So even though queries are similar, same execution plan may not be good. Most of the time it may not be the case. So we need to carefully analyse everything before we decide whether it is better to use same execution plan or not.</p>
<p>If we don&#8217;t want to re-use execution plan we can always use “recompile” option in stored procedures.</p>
<p>Even after using stored procedures or “sp_executesql” there are times when execution plan won&#8217;t be re-used. They are</p>
<ul>
<li>When indexes used by the query changes or dropped</li>
<li>When statistics, structure or schema of a table used by the query changes</li>
<li>When we use “recompile” option</li>
<li>When large number of insertions, updates or deletes happen</li>
<li>When we mix DDL &amp; DML within single query etc&#8230;</li>
</ul>
<p>&nbsp;</p>
<h3><strong>5) REMOVING UNNECESSARY INDEXES</strong></h3>
<p>After fine tuning the queries, we need to check how we are using indexes over all in SQL Server. Index maintenance requires lots of CPU &amp; I/O. It is better to remove indexes if they are not used at all because every time we insert data into SQL Server it needs to update indexes also.</p>
<p>SQL server provides us “dm_db_index_usage_stats” DMV to find index statistics. You can use below query to find out index usage statistics:</p>
<p>[cc lang=&#8221;sql]</p>
<p>SELECT<br />
OBJECT_NAME(IUS.[OBJECT_ID]) AS [OBJECT NAME],<br />
DB_NAME(IUS.database_id) AS [DATABASE NAME],<br />
I.[NAME] AS [INDEX NAME],<br />
USER_SEEKS,<br />
USER_SCANS,<br />
USER_LOOKUPS,<br />
USER_UPDATES<br />
FROM SYS.DM_DB_INDEX_USAGE_STATS AS IUS<br />
INNER JOIN SYS.INDEXES AS I<br />
ON I.[OBJECT_ID] = IUS.[OBJECT_ID]<br />
AND I.INDEX_ID = IUS.INDEX_ID</p>
<p>[/cc]</p>
<p>&nbsp;</p>
<h3><strong>6) MS SQL SERVER INSTALLATION &amp; DATABASE SETUP</strong></h3>
<p>Please keep data and log files separately. Main reason for this is writing &amp; accessing data files are not sequential, where as to log files it is sequential. So if we put them on the same drive then we can’t use them in an optimized way.</p>
<p>Please note that when you purchase SAN from vendor they may give you some recommendations on how to setup but it is not always useful. Please have a detailed discussion with your hardware &amp; networking guys so that you can keep data and log files separately in an optimized way.</p>
<h3><strong>7) DON’T OVERLOAD SQL SERVER</strong></h3>
<p>The primary duty of any DBA is to make sure Production Server runs smoothly and serves the customers as best as possible. To make this happen we need to maintain separate databases (if possible separate machines) for the following environments:</p>
<ul>
<li>Production</li>
<li>Development</li>
<li>Testing</li>
<li>Analytical</li>
</ul>
<p>For Production we need a database with Full recovery mode &amp; for other works Simple recovery mode is enough.</p>
<p>If we do testing on Production database then it will put lots of load on transaction log, indexes, CPU &amp; I/O. That’s why we need to use separate databases for production, development, testing &amp; analytical.</p>
<p>If possible please use separate machines for these activities because it will decrease load on CPU &amp; I/O.</p>
<p>&nbsp;</p>
<h3><strong>8) TRANSACTION LOG, TEMPDB &amp; MEMORY</strong></h3>
<p>Please make sure your Log file has enough free space for normal operations because auto-grow operation on log file is time-consuming and it can force other operations to wait until it is completed.</p>
<p>You can use “DBCC SQLPERF(logspace)” to find out Log file size for each database and how much it is used.</p>
<p>The best way to setup tempdb is to put it on separate disk. Please keep initial size as max as you can afford because when it reaches auto grow situation performance decreases.</p>
<p>We need to make sure that SQL server runs on separate machine and there won’t be any other application on that machine. We need to keep some memory for operating system &amp; some more if it is part of cluster etc&#8230; . So most cases keep around 2 GB for the OS and rest will be for the SQL Server.</p>
<p><strong>Final conclusion:</strong> The above suggestions are for the basic performance tuning only. To do advanced performance tuning for the SQL Server, we need to dig deeper into each of the above areas.</p>
<p>Thanks<br />
Sripal V<br />
Database Performance Specialist<br />
<a href="http://www.soulstech.com">www.soulstech.com</a><br />
email: <em><strong>sripal@soulstech.com</strong></em></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlserverplanet.com/optimization/how-to-do-basic-performance-tuning-on-microsoft-sql-server/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>Creating log tables to track running processes</title>
		<link>https://sqlserverplanet.com/design/creating-log-tables-to-track-running-processes</link>
					<comments>https://sqlserverplanet.com/design/creating-log-tables-to-track-running-processes#comments</comments>
		
		<dc:creator><![CDATA[Derek Dieter]]></dc:creator>
		<pubDate>Wed, 20 May 2015 02:42:29 +0000</pubDate>
				<category><![CDATA[Design]]></category>
		<guid isPermaLink="false">https://sqlserverplanet.com/?p=3642</guid>

					<description><![CDATA[In any SQL Server system, you will have jobs that run on a schedule or at specified intervals. In these cases, it&#8217;s always nice to keep track of certain aspects over time, so you can compare when things go wrong or how performance has been affected over time. In my experience these are indepsensible when it comes to troubleshooting, and for running delta jobs. Here we&#8217;ll show a small example of the log tables you can create to facilitate this. First let&#8217;s look at some DDL for 2 different tables: [cc lang=&#8221;sql&#8221;] CREATE TABLE [dbo].[ProcessLogMaster]( [process_log_master_id] [int] IDENTITY(1,1) CONSTRAINT PK_process_log_master PRIMARY KEY CLUSTERED NOT NULL, [process_master_name] [varchar](100) NOT NULL, [datetime_start] [datetime] NULL DEFAULT (getdate()), [datetime_end] [datetime] NULL, [elapsed_ms] [int] NULL, [rows_updated] [int] NULL, [rows_inserted] [int] NULL, [rows_deleted] [int] NULL, [complete] [tinyint] NULL DEFAULT ((0)), [success] [tinyint] NULL, [error_description] [varchar](max) NULL ) CREATE TABLE [dbo].[ProcessLogDetail]( [process_log_detail_id] [int] IDENTITY(1,1) NOT NULL CONSTRAINT [PK_process_log_detail] PRIMARY KEY, [process_log_master_id] [int] NOT NULL, [process_detail_name] [varchar](100) NOT NULL, [datetime_start] [datetime] NULL, [datetime_end] [datetime] NULL, [elapsed_ms] [int] NULL, [rows_updated] [int] NULL, [rows_inserted] [int] NULL, [rows_deleted] [int] NULL, [complete] [tinyint] NULL, [success] [tinyint] NULL, [error_description] [varchar](max) NULL ) [/cc] What we have here are two different tables that can be used to describe job steps. The ProcessLogMaster table, is used to specify the Master or Top level of the job. If there are multiple steps to the job, that report back to the master, we would enter their entries into the ProcessLogDetail table. We could then sum up the [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>In any SQL Server system, you will have jobs that run on a schedule or at specified intervals.  In these cases, it&#8217;s always nice to keep track of certain aspects over time, so you can compare when things go wrong or how performance has been affected over time.  In my experience these are indepsensible when it comes to troubleshooting, and for running delta jobs.  Here we&#8217;ll show a small example of the log tables you can create to facilitate this.</p>
<p>First let&#8217;s look at some DDL for 2 different tables:</p>
<p>[cc lang=&#8221;sql&#8221;]<br />
CREATE TABLE [dbo].[ProcessLogMaster](<br />
	[process_log_master_id] [int] IDENTITY(1,1) CONSTRAINT PK_process_log_master PRIMARY KEY CLUSTERED NOT NULL,<br />
	[process_master_name] [varchar](100) NOT NULL,<br />
	[datetime_start] [datetime] NULL DEFAULT (getdate()),<br />
	[datetime_end] [datetime] NULL,<br />
	[elapsed_ms] [int] NULL,<br />
	[rows_updated] [int] NULL,<br />
	[rows_inserted] [int] NULL,<br />
	[rows_deleted] [int] NULL,<br />
	[complete] [tinyint] NULL DEFAULT ((0)),<br />
	[success] [tinyint] NULL,<br />
	[error_description] [varchar](max) NULL<br />
)</p>
<p>CREATE TABLE [dbo].[ProcessLogDetail](<br />
	[process_log_detail_id] [int] IDENTITY(1,1) NOT NULL CONSTRAINT [PK_process_log_detail] PRIMARY KEY,<br />
	[process_log_master_id] [int] NOT NULL,<br />
	[process_detail_name] [varchar](100) NOT NULL,<br />
	[datetime_start] [datetime] NULL,<br />
	[datetime_end] [datetime] NULL,<br />
	[elapsed_ms] [int] NULL,<br />
	[rows_updated] [int] NULL,<br />
	[rows_inserted] [int] NULL,<br />
	[rows_deleted] [int] NULL,<br />
	[complete] [tinyint] NULL,<br />
	[success] [tinyint] NULL,<br />
	[error_description] [varchar](max) NULL<br />
)<br />
[/cc]</p>
<p>What we have here are two different tables that can be used to describe job steps.  The ProcessLogMaster table, is used to specify the Master or Top level of the job.  If there are multiple steps to the job, that report back to the master, we would enter their entries into the ProcessLogDetail table.  We could then sum up the rows updated / inserted / deleted into the ProcessLogMaster table.  You&#8217;ll also notice the ProcessLogDetail table contains the foreign key to the ProcessLogMaster table.  We did not designate it as such here simply for performance reasons.</p>
<p>AN example would consist of creating an entry into these tables, often within your stored procedure, then update the corresponding id that relates to that portion of the job</p>
<p>[cc lang=&#8221;sql&#8221;]</p>
<p>CREATE PROCEDURE pr_example_proc_job<br />
AS</p>
<p>/********************************************************************<br />
*	declarations<br />
********************************************************************/<br />
DECLARE @process_master_name varchar(100) = &#8216;refresh_all_clients&#8217;;<br />
DECLARE @process_log_master_id int;<br />
DECLARE @now datetime = GETDATE();<br />
DECLARE @datetime_start datetime;<br />
DECLARE @datetime_since datetime;</p>
<p>INSERT INTO ProcessLogMaster<br />
(<br />
	process_master_name,<br />
	datetime_start<br />
)<br />
SELECT<br />
	@process_master_name,<br />
	getdate();</p>
<p>SET @process_log_master_id = SCOPE_IDENTITY();	</p>
<p>BEGIN TRY<br />
&#8212; this will create an entry in the processLogDetail table<br />
EXEC [pr_example_detail_proc_1]<br />
	@process_log_master_id = @process_log_master_id;</p>
<p>&#8212; this will create an entry in the processLogDetail table<br />
EXEC [pr_example_detail_proc_2]<br />
	@process_log_master_id = @process_log_master_id;</p>
<p>&#8212; gather detail logging<br />
DECLARE @total_rows_updated int;<br />
DECLARE @total_rows_inserted int;<br />
DECLARE @total_rows_deleted int;</p>
<p>SELECT<br />
	@total_rows_updated = ISNULL(SUM(rows_updated),0),<br />
	@total_rows_inserted= ISNULL(SUM(rows_inserted),0),<br />
	@total_rows_deleted = ISNULL(SUM(rows_deleted),0)<br />
FROM ProcessLogDetail<br />
WHERE process_log_master_id = @process_log_master_id;</p>
<p>END TRY<br />
BEGIN CATCH<br />
 	&#8212; handle error<br />
	UPDATE ProcessLogMaster<br />
	SET datetime_end = GETDATE(),<br />
		elapsed_ms = DATEDIFF(ms, @now, GETDATE()),<br />
		rows_updated = @total_rows_updated,<br />
		rows_inserted = @total_rows_inserted,<br />
		rows_deleted = @total_rows_deleted,<br />
		complete = 1,<br />
		success = 0,<br />
		error_description = ERROR_MESSAGE()<br />
	WHERE process_log_master_id = @process_log_master_id;</p>
<p>	RETURN;</p>
<p>END CATCH</p>
<p>	UPDATE ProcessLogMaster<br />
	SET datetime_end = GETDATE(),<br />
		elapsed_ms = DATEDIFF(ms, @now, GETDATE()),<br />
		rows_updated = @total_rows_updated,<br />
		rows_inserted = @total_rows_inserted,<br />
		rows_deleted = @total_rows_deleted,<br />
		complete = 1,<br />
		success = 1<br />
	WHERE process_log_master_id = @process_log_master_id;</p>
<p>[/cc]</p>
<p>As you can see, we summarize the updates, inserts, and deletes and insert the total into the ProcessLogMaster table, from the ProcessLogDetail table.  Also, if there is an error, the job never gets set to success = true.  This being the case, we can always start our job back up from the last successful timestamp if we are doing a delta process.</p>
<p>I&#8217;ve found over the years these tables to be a necessary part of keeping track of jobs.  Not only will it help you in viewing the duration of a job, but for doing delta processes, you can easily get the last time a job has succeeded and pick up from there.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlserverplanet.com/design/creating-log-tables-to-track-running-processes/feed</wfw:commentRss>
			<slash:comments>1</slash:comments>
		
		
			</item>
		<item>
		<title>How to cache stored procedure results using a hash key</title>
		<link>https://sqlserverplanet.com/design/caching-stored-procedure-results-using-a-hash-key</link>
					<comments>https://sqlserverplanet.com/design/caching-stored-procedure-results-using-a-hash-key#comments</comments>
		
		<dc:creator><![CDATA[Derek Dieter]]></dc:creator>
		<pubDate>Sat, 16 May 2015 00:46:43 +0000</pubDate>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Featured Articles]]></category>
		<category><![CDATA[Optimization]]></category>
		<guid isPermaLink="false">https://sqlserverplanet.com/?p=3633</guid>

					<description><![CDATA[There are a lot of different design patterns that lend themselves to creating the shortest path to the data. One of the most efficient is the caching of stored procedure result sets. In order to do this, we need to read the incoming parameters and create a cache key. This cache key is then stored along with the stored procedures result set as a unique identifier representing that combination of the stored procedures parameters. The caveat with this method is that the business requirement needs to allow stale data. There are times where you will need to use values other than the passed in parameters in order to create the cache key. Some examples include datetime data types or keys that are unique (like a customerkey). If the hash that gets created from the parameters is unique, then you will never reuse that dataset again. With this in mind you would even have determine whether the procedure is even cacheable. Another concern to keep in mind is the duration of time you can serve stale data. Maybe 30 seconds, 1 minute, or 1 hour? Any time increment is able to be worked with by clearning the cache tables at the desired interval. Design Let&#8217;s look at the basic workflow for how this procedure will work. First of all, we will need to hash all the parameters that are coming into the procedure (unless they are unique in which case we may not be able to cache, or we can possibly [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>There are a lot of different design patterns that lend themselves to creating the shortest path to the data. One of the most efficient is the caching of stored procedure result sets. In order to do this, we need to read the incoming parameters and create a cache key. This cache key is then stored along with the stored procedures result set as a unique identifier representing that combination of the stored procedures parameters. The caveat with this method is that the business requirement needs to allow stale data.</p>
<p>There are times where you will need to use values other than the passed in parameters in order to create the cache key. Some examples include datetime data types or keys that are unique (like a customerkey). If the hash that gets created from the parameters is unique, then you will never reuse that dataset again. With this in mind you would even have determine whether the procedure is even cacheable. Another concern to keep in mind is the duration of time you can serve stale data. Maybe 30 seconds, 1 minute, or 1 hour? Any time increment is able to be worked with by clearning the cache tables at the desired interval.</p>
<h4>Design</h4>
<p>Let&#8217;s look at the basic workflow for how this procedure will work. First of all, we will need to hash all the parameters that are coming into the procedure (unless they are unique in which case we may not be able to cache, or we can possibly use another value).</p>
<h4>Caching the parameters</h4>
<p>[cc lang=&#8221;sql&#8221;]<br />
&#8212; example parameters<br />
DECLARE @cust_type INT = 5<br />
DECLARE @cust_location int = 10<br />
DECLARE @location_category int = 20<br />
DECLARE @page_number int = 2<br />
DECLARE @records_per_page int = 100</p>
<p>DECLARE @param_hash varbinary(20)</p>
<p>SET @param_hash = HASHBYTES(&#8216;MD5&#8217;,<br />
coalesce(convert(nvarchar(MAX),@cust_type),&#8221;) +<br />
coalesce(convert(nvarchar(MAX),@cust_location),&#8221;) +<br />
coalesce(convert(nvarchar(MAX),@location_category),&#8221;) +<br />
coalesce(convert(nvarchar(MAX),@page_number),&#8221;) +<br />
coalesce(convert(nvarchar(MAX),@records_per_page),&#8221;)<br />
)</p>
<p>SELECT @param_hash<br />
[/cc]</p>
<p>Now we have the cache key that will correspond to the recordset that we either will create, or will look to see exists.</p>
<h4>Managing the cache</h4>
<p>You may have thought this through enough already and realized if we store all the result sets for the procedure in one table, then when we delete or update based on whether the cache is expired, we&#8217;re going run into either contention, or an empty result set. This is where our <em>race condition safe &#8212; caching solution</em> comes into play. It involves using 3 tables instead of 1 or 2 in order to store the cache results. Why 3? Hold on, here comes my attempt at explaining.</p>
<p>If we use 1 table, then when we need to invalidate the cache, we would have to perform an update on all the records for each existant cache key as they come in. This will no doubt cause contention with blocking.</p>
<p>If we use 2 tables, then we can switch over to table 2 once the cache is expired for table 1, then trucate table 1. However, a subtle race condition comes into play in this scenario. When the procedure is executed and it is determined that table 1 is the valid table to use, unless you wrap the entire code in an explicit transaction, by the time it reaches the bottom of the procedure, the cache may have switched to table 2 as being the primary cache table, meaning table 1 has now been truncated. Guess what, you&#8217;re returning no data now.</p>
<p>So that is why we use 3 tables. We do not truncate table 1 when table 2 represents the current cache table. That way, when we reach the end of the procedure, we can still serve a result set because nothing has been truncated. Instead we truncate table 3, which is two tables back from being used (surely not being queried any longer). We then round robin through the tables in this manner.</p>
<p>Here&#8217;s an example of the cache invalidator job that would run at a set interval</p>
<p>[cc lang=&#8221;sql&#8221;]</p>
<p>USE cachedb<br />
GO</p>
<p>DECLARE @cache_table_old int<br />
DECLARE @transaction_error int<br />
DECLARE @debug_mode int = 0</p>
<p>BEGIN TRANSACTION</p>
<p>&#8212; find the table we need to truncate<br />
SET @cache_table_old =<br />
(<br />
SELECT CASE WHEN cache_table_current = 1 THEN cache_table_count ELSE cache_table_current &#8211; 1 END<br />
FROM proc_mutex pm<br />
WHERE pm.[procedure_name] = &#8216;pr_procedure_example&#8217;<br />
);</p>
<p>&#8212; set the new cache table (the cached procedure pr_procedure_example will read this table to determine cache)<br />
UPDATE TOP(1) pm<br />
SET cache_table_current = CASE WHEN cache_table_current = cache_table_count THEN 1 ELSE cache_table_current + 1 END,<br />
datetime_modified = GETDATE()<br />
FROM proc_mutex pm<br />
WHERE pm.[procedure_name] = &#8216;pr_procedure_example&#8217;</p>
<p>set @transaction_error = @@ERROR;</p>
<p>IF @transaction_error != 0<br />
BEGIN<br />
ROLLBACK<br />
END<br />
ELSE<br />
BEGIN</p>
<p>COMMIT TRANSACTION<br />
&#8212; truncate the old cache table<br />
IF @transaction_error = 0<br />
BEGIN</p>
<p>IF @cache_table_old = 1<br />
BEGIN<br />
TRUNCATE TABLE cachedb.tmp.pr_procedure_example_cachedb_1<br />
END<br />
ELSE IF @cache_table_old = 2<br />
BEGIN<br />
TRUNCATE TABLE cachedb.tmp.pr_procedure_example_cachedb_2<br />
END<br />
ELSE IF @cache_table_old = 3<br />
BEGIN<br />
TRUNCATE TABLE cachedb.tmp.pr_procedure_example_cachedb_3<br />
END</p>
<p>END</p>
<p>END<br />
[/cc]</p>
<p>And here is the ddl for the proc_mutex table. This table will hold all the procedure names that you cache, along with the current cache table designation.</p>
<p>[cc lang=&#8221;sql&#8221;]<br />
CREATE TABLE [dbo].[proc_mutex](<br />
[proc_mutex_id] [int] IDENTITY(1,1) NOT NULL,<br />
[procedure_name] [varchar](255) NOT NULL,<br />
[cache_table_current] [int] NOT NULL,<br />
[cache_table_count] [int] NOT NULL,<br />
[datetime_modified] [datetime] NOT NULL<br />
)<br />
[/cc]</p>
<div class="block style04">
						<img decoding="async" src="http://sqlserverplanet.com/wp-content/themes/sqlserverplanet/images/ico9.gif" alt="" class="ico" />
						<div class="text-holder">It is recommended you keep the caching on a separate database which is stored on a PCIE SSD card, or at least an SSD</div>
					</div>
<h4>Creating the procedure</h4>
<p>Now that we have created the cache invalidator mechanism, along with the proc_mutex, we will write the first half of the procedure. This will ideally be a template you can follow:</p>
<p>[cc lang=&#8221;sql&#8221;]<br />
USE proddb<br />
GO</p>
<p>CREATE PROCEDURE dbo.pr_procedure_example<br />
@cust_type INT = 5,<br />
@cust_location int = 10,<br />
@location_category int = 20<br />
AS<br />
BEGIN</p>
<p>/**********************************************************<br />
* setup param_hash_key<br />
***********************************************************/<br />
DECLARE @procedure_name sysname ;<br />
DECLARE @cache_table_number tinyint ;</p>
<p>SET @procedure_name = OBJECT_NAME(@@PROCID);<br />
SET @cache_table_number = (SELECT TOP(1) pm.cache_table_current FROM cachedb.dbo.proc_mutex pm with (nolock) WHERE pm.[procedure_name] = @procedure_name);</p>
<p>DECLARE @param_hash varbinary(20)</p>
<p>SET @param_hash = HASHBYTES(&#8216;MD5&#8217;,<br />
coalesce(convert(nvarchar(MAX),@cust_type),&#8221;) +<br />
coalesce(convert(nvarchar(MAX),@cust_location),&#8221;) +<br />
coalesce(convert(nvarchar(MAX),@location_category),&#8221;) +<br />
coalesce(convert(nvarchar(MAX),@page_number),&#8221;) +<br />
coalesce(convert(nvarchar(MAX),@records_per_page),&#8221;)<br />
)</p>
<p>/**************************************************************<br />
* determine which table may have our param_hash_key<br />
* if the key does exist, we will simply return our result set<br />
* this will result in a fast return &lt; 5ms<br />
***************************************************************/</p>
<p>IF @cache_table_number = 1<br />
BEGIN</p>
<p>IF EXISTS(<br />
SELECT 1<br />
FROM cachedb.tmp.pr_procedure_example_cachedb_1<br />
WHERE param_hash = @param_hash<br />
)<br />
BEGIN</p>
<p>SELECT *<br />
FROM cachedb.tmp.pr_procedure_example_cachedb_1<br />
WHERE param_hash = @param_hash</p>
<p>RETURN</p>
<p>END<br />
END<br />
ELSE IF @cache_table_number = 2<br />
BEGIN</p>
<p>IF EXISTS(<br />
SELECT 1<br />
FROM cachedb.tmp.pr_procedure_example_cachedb_2<br />
WHERE param_hash = @param_hash<br />
)<br />
BEGIN</p>
<p>SELECT *<br />
FROM cachedb.tmp.pr_procedure_example_cachedb_2<br />
WHERE param_hash = @param_hash</p>
<p>RETURN</p>
<p>END<br />
END<br />
ELSE IF @cache_table_number = 3<br />
BEGIN</p>
<p>IF EXISTS(<br />
SELECT 1<br />
FROM cachedb.tmp.pr_procedure_example_cachedb_3<br />
WHERE param_hash = @param_hash<br />
)<br />
BEGIN</p>
<p>SELECT *<br />
FROM cachedb.tmp.pr_procedure_example_cachedb_3<br />
WHERE param_hash = @param_hash</p>
<p>RETURN</p>
<p>END<br />
END<br />
[/cc]</p>
<p>Above, we are looking up to see if the result set has already been created for that combination of parameters. If it does, we will return the result quite fast. Depending on the size of data and the disk speed it could be somewhere between 1ms to 10ms. Let&#8217;s take a look at the full example.</p>
<h4>Full cached procedure example</h4>
<p>[cc lang=&#8221;sql&#8221;]<br />
USE proddb<br />
GO</p>
<p>CREATE PROCEDURE dbo.pr_procedure_example<br />
@cust_type INT = 5,<br />
@cust_location int = 10,<br />
@location_category int = 20<br />
AS<br />
BEGIN</p>
<p>/**********************************************************<br />
* setup param_hash_key<br />
***********************************************************/<br />
DECLARE @procedure_name sysname ;<br />
DECLARE @cache_table_number tinyint ;</p>
<p>SET @procedure_name = OBJECT_NAME(@@PROCID);<br />
SET @cache_table_number = (SELECT TOP(1) pm.cache_table_current FROM cachedb.dbo.proc_mutex pm with (nolock) WHERE pm.[procedure_name] = @procedure_name);</p>
<p>DECLARE @param_hash varbinary(20)</p>
<p>SET @param_hash = HASHBYTES(&#8216;MD5&#8217;,<br />
coalesce(convert(nvarchar(MAX),@cust_type),&#8221;) +<br />
coalesce(convert(nvarchar(MAX),@cust_location),&#8221;) +<br />
coalesce(convert(nvarchar(MAX),@location_category),&#8221;)<br />
)</p>
<p>/**************************************************************<br />
* determine which table may have our param_hash_key<br />
* if the key does exist, we will simply return our result set<br />
* this will result in a fast return &lt; 5ms<br />
***************************************************************/</p>
<p>IF @cache_table_number = 1<br />
BEGIN</p>
<p>IF EXISTS(<br />
SELECT 1<br />
FROM cachedb.tmp.pr_procedure_example_cachedb_1<br />
WHERE param_hash = @param_hash<br />
)<br />
BEGIN</p>
<p>SELECT *<br />
FROM cachedb.tmp.pr_procedure_example_cachedb_1<br />
WHERE param_hash = @param_hash</p>
<p>RETURN</p>
<p>END<br />
END<br />
ELSE IF @cache_table_number = 2<br />
BEGIN</p>
<p>IF EXISTS(<br />
SELECT 1<br />
FROM cachedb.tmp.pr_procedure_example_cachedb_2<br />
WHERE param_hash = @param_hash<br />
)<br />
BEGIN</p>
<p>SELECT *<br />
FROM cachedb.tmp.pr_procedure_example_cachedb_2<br />
WHERE param_hash = @param_hash</p>
<p>RETURN</p>
<p>END<br />
END<br />
ELSE IF @cache_table_number = 3<br />
BEGIN</p>
<p>IF EXISTS(<br />
SELECT 1<br />
FROM cachedb.tmp.pr_procedure_example_cachedb_3<br />
WHERE param_hash = @param_hash<br />
)<br />
BEGIN</p>
<p>SELECT *<br />
FROM cachedb.tmp.pr_procedure_example_cachedb_3<br />
WHERE param_hash = @param_hash</p>
<p>RETURN</p>
<p>END<br />
END</p>
<p>/**************************************************************<br />
* procedure logic goes here<br />
***************************************************************/<br />
SELECT *<br />
INTO #holding_table<br />
FROM proc_example_table<br />
WHERE cust_type = @cust_type<br />
AND cust_location = @cust_location<br />
AND location_category = @location_category</p>
<p>/**************************************************************<br />
* insert result set to cache table if not exists<br />
***************************************************************/<br />
IF @cache_table_number = 1<br />
BEGIN</p>
<p>INSERT INTO cachedb.tmp.pr_procedure_example_cachedb_1<br />
SELECT<br />
param_hash = @param_hash,<br />
*<br />
FROM #holding_table<br />
&#8212; prevent duplicate result sets<br />
WHERE NOT EXISTS (SELECT 1 FROM cachedb.tmp.pr_procedure_example_cachedb_1 WHERE param_hash = @param_hash);</p>
<p>END<br />
ELSE IF @cache_table_number = 2<br />
BEGIN</p>
<p>INSERT INTO cachedb.tmp.pr_procedure_example_cachedb_2<br />
SELECT<br />
param_hash = @param_hash,<br />
*<br />
FROM #holding_table<br />
WHERE NOT EXISTS (SELECT 1 FROM cachedb.tmp.pr_procedure_example_cachedb_2 WHERE param_hash = @param_hash);</p>
<p>END<br />
ELSE IF @cache_table_number = 3<br />
BEGIN</p>
<p>INSERT INTO cachedb.tmp.pr_procedure_example_cachedb_3<br />
SELECT param_hash = @param_hash,<br />
*<br />
FROM #holding_table<br />
WHERE NOT EXISTS (SELECT 1 FROM cachedb.tmp.pr_procedure_example_cachedb_3 WHERE param_hash = @param_hash);</p>
<p>END</p>
<p>/**************************************************************<br />
* return back result set<br />
***************************************************************/<br />
IF @cache_table_number = 1<br />
BEGIN<br />
SELECT *<br />
FROM cachedb.tmp.pr_procedure_example_cachedb_1<br />
WHERE param_hash = @param_hash<br />
END<br />
ELSE IF @cache_table_number = 2<br />
BEGIN<br />
SELECT *<br />
FROM cachedb.tmp.pr_procedure_example_cachedb_2<br />
WHERE param_hash = @param_hash<br />
END<br />
ELSE IF @cache_table_number = 3<br />
BEGIN<br />
SELECT *<br />
FROM cachedb.tmp.pr_procedure_example_cachedb_3<br />
WHERE param_hash = @param_hash<br />
END</p>
<p>END</p>
<p>[/cc]</p>
<p>There it is. Needless to say, the respective tables (cachedb.tmp.pr_procedure_example_cachedb_1) need to have the same schema as the procedure that&#8217;s being returned. And again, put these tables on a PCIE SSD, and setup the job to invalidate the cache at a set interval, and there you have it, a procedure cache mechanism to help you scale.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://sqlserverplanet.com/design/caching-stored-procedure-results-using-a-hash-key/feed</wfw:commentRss>
			<slash:comments>2</slash:comments>
		
		
			</item>
	</channel>
</rss>
