<?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>Glorf IT &#187; Performance</title>
	<atom:link href="http://www.glorf.it/blog/category/sql-talk/performance/feed" rel="self" type="application/rss+xml" />
	<link>http://www.glorf.it/blog</link>
	<description>Bedenkliches aus dem IT-Alltag</description>
	<lastBuildDate>Sun, 29 Aug 2010 17:05:18 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=abc</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Bessere Performance durch Datenkompression in Datenbanken?</title>
		<link>http://www.glorf.it/blog/2008/08/08/sql-server/bessere-performance-durch-datenkompression-datenbanken</link>
		<comments>http://www.glorf.it/blog/2008/08/08/sql-server/bessere-performance-durch-datenkompression-datenbanken#comments</comments>
		<pubDate>Fri, 08 Aug 2008 15:51:52 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[SQL Server]]></category>
		<category><![CDATA[Datenkompression]]></category>

		<guid isPermaLink="false">http://www.glorf.it/blog/?p=1386</guid>
		<description><![CDATA[Bei TecChannel.de einen Artikel zu Datenbanken zu finden, ist schon eher ungew&#246;hnlich. Der Artikel &#34;Datenkompression in SQL-Datenbanken&#34; ist etwa auf Level 100 bis 200 und somit ein ganz guter Einstieg in das Thema. Weil heutzutage das Lesen und Schreiben von Daten tats&#228;chlich noch in fast allen F&#228;llen der Flaschenhals ist, ist das alte Thema immer [...]]]></description>
			<content:encoded><![CDATA[<p>Bei TecChannel.de einen Artikel zu Datenbanken zu finden, ist schon eher ungew&#246;hnlich. Der Artikel &quot;<a href="http://www.tecchannel.de/server/sql/1765800/">Datenkompression in SQL-Datenbanken</a>&quot; ist etwa auf Level 100 bis 200 und somit ein ganz guter Einstieg in das Thema. Weil heutzutage das Lesen und Schreiben von Daten tats&#228;chlich noch in fast allen F&#228;llen der Flaschenhals ist, ist das alte Thema immer noch relevant.</p>
<p>Folgende Aspekte werde hier unter dem Stichwort &quot;Komprimierung&quot; genannt, die dahin abzielen, dass auf eine Datenseite m&#246;glichst viele Datens&#228;tze passen:</p>
<ul>
<li>Verwendung numerischer Typen anstelle von Zeichenketten</li>
<li>Verwendung von Unicode nur wo es wirklich n&#246;tig ist (SQL-Server speichert Unicode-Zeichen das als <a href="http://de.wikipedia.org/wiki/UTF-16">UTF-16</a>): Der Platzbedarf sei typischerweise um 40 bis 60 Prozent h&#246;her.</li>
<li>Verwendung von numerischen Werten mit variablen Feldgr&#246;&#223;e (geht nur mit der <a href="http://www.microsoft.com/sqlserver/2008/en/us/enterprise.aspx">SQL Server 2008 Enterprise Edition</a>)</li>
<li>Seiten- oder Tabellen-Kompression bei vielen doppelten Werten nutzen (auch das geht nur mit der teuren <a href="http://www.microsoft.com/sqlserver/2008/en/us/enterprise.aspx">SQL Server 2008 Enterprise Edition</a>). Einen Performancevergleich findet man <a href="http://sqlblog.com/blogs/linchi_shea/archive/2008/05/16/sql-server-2008-page-compression-performance-impact-on-table-scans.aspx">hier</a>. Das beeindruckt.</li>
</ul>
<p>Wer richtig gro&#223;e Datenbanken betreut und sich die Enterprise-Edition leisten kann, sollte sich die neuen Features dazu mal ansehen. Aber auch im Kleinen lohnt es sich Platz zu sparen.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.glorf.it/blog/2008/08/08/sql-server/bessere-performance-durch-datenkompression-datenbanken/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SQL-Server-2005: PIVOT auf JOINs</title>
		<link>http://www.glorf.it/blog/2008/05/09/sql-talk/performance/sql-server-2005-pivot-auf-joins</link>
		<comments>http://www.glorf.it/blog/2008/05/09/sql-talk/performance/sql-server-2005-pivot-auf-joins#comments</comments>
		<pubDate>Fri, 09 May 2008 16:50:37 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[Pivot]]></category>
		<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://www.glorf.it/blog/2008/05/09/sql-talk/performance/sql-server-2005-pivot-auf-joins</guid>
		<description><![CDATA[Als ich mit dem PIVOT-Konstrukt arbeitete und performante Ergebnisse haben wollte, muss ich erst verstehen, wie es intern arbeitet. Das beschrieb ich im Artikel &#34;SQL-Server-2005: schnelles Pivot?&#34;. Diese Serie soll meine Erfahrungen weitergeben und bei Verstehen der Zusammenh&#228;nge helfen. Mit ein paar Kniffen kann man dann die Ausf&#252;hrung erheblich beschleunigen. Der Quellcode zu diesem letzten [...]]]></description>
			<content:encoded><![CDATA[<p>Als ich mit dem PIVOT-Konstrukt arbeitete und performante Ergebnisse haben wollte, muss ich erst verstehen, wie es intern arbeitet. Das beschrieb ich im Artikel &quot;SQL-Server-2005: schnelles Pivot?&quot;. Diese Serie soll meine Erfahrungen weitergeben und bei Verstehen der Zusammenh&#228;nge helfen. Mit ein paar Kniffen kann man dann die Ausf&#252;hrung erheblich beschleunigen. Der Quellcode zu diesem letzten Artikel steht &#252;brigens <a href="http://www.glorf.it/files/200710_pivot/08_pivot_join_performance.sql">hier</a>.</p>
<ul>
<li><a href="http://www.glorf.it/blog/2008/05/03/sql-talk/performance/sql-server-2005-schnelles-pivot">Artikel 1: Wie wird Pivot abgearbeitet?</a></li>
<li><a href="http://www.glorf.it/blog/2008/05/05/sql-talk/performance/sql-server-2005-stoerer-beim-pivot-eliminieren">Artikel 2: St&#246;rer beim PIVOT eliminieren</a></li>
<li><a href="http://www.glorf.it/blog/2008/05/07/sql-talk/performance/sql-server-2005-pivot-und-performance">Artikel 3: Pivot-Performance</a></li>
<li><a href="http://www.glorf.it/blog/2008/05/09/sql-talk/performance/sql-server-2005-pivot-auf-joins">Artikel 4: Pivot-Performance mit Joins</a></li>
</ul>
<p><strong>Performance von &quot;gejointen&quot; Daten</strong></p>
<p>Wenn die Basistabelle des PIVOTs aus einer Derived-Table besteht, die mittels Join die Daten aus mehreren Tabellen sammelt, dann h&#228;ngt es von relativ vielen Faktoren ab, ob das PIVOT schnell ist. Das gleiche gilt nat&#252;rlich auch, wenn der PIVOT auf eine View oder eine Inline-Table-Valued-Function durchgef&#252;hrt wird. </p>
<p>Man kann es aber auf den Nenner bringen: Kann der Optimizer das PIVOT so begrenzen, dass die Gruppierung allein auf den Werten einer Tabelle basiert, die mittels Clustered-Index gelesen werden k&#246;nnen, dann ist es schnell. In den anderen F&#228;llen muss man mit mehr oder weniger starken Performanceeinbu&#223;en rechnen. </p>
<p>Als Beispiel m&#246;chte ich einen relativ g&#228;ngigen Sonderfall darstellen: Das Beispiel des <a href="http://www.glorf.it/blog/2008/05/03/sql-talk/performance/sql-server-2005-schnelles-pivot">ersten Artikels</a> wird so erweitert, dass die Spalten&#252;berschriften in einer Translation-Table TranTab stehen:<br />
<img src='http://www.glorf.it/blog/wp-content/uploads/2008/05/translationtable.jpg' alt='Translation-Table' /></p>
<p>In der Tabelle OpenSchema steht dann der Verweis auf den Namen des Attributes:<br />
<img src='http://www.glorf.it/blog/wp-content/uploads/2008/05/tabopenschema_mit_translation.jpg' alt='Tabelle Openschema mit &#220;bersetzungstabelle' /></p>
<p>Um das gew&#252;nschte Ergebnis zu bekommen, muss man PIVOT auf eine Derived-Table ausf&#252;hren:</p>
<div class="codesnip-container" >
<div class="sql codesnip" style="font-family:monospace;"><span class="kw1">SELECT</span> ID<span class="sy0">,</span> <span class="st0">&quot;Typ&quot;</span><span class="sy0">,</span> <span class="st0">&quot;Datum&quot;</span><span class="sy0">,</span> <span class="st0">&quot;Anzahl&quot;</span><span class="sy0">,</span> <span class="st0">&quot;Dings&quot;</span><span class="sy0">,</span> <span class="st0">&quot;Bums&quot;</span><br />
<span class="kw1">FROM</span> &nbsp; &nbsp;<span class="br0">&#40;</span><span class="kw1">SELECT</span> os<span class="sy0">.</span>ID<span class="sy0">,</span> tt<span class="sy0">.</span>Attribute<span class="sy0">,</span> os<span class="sy0">.</span><span class="st0">&quot;Value&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FROM</span> OpenSchema <span class="kw1">AS</span> os<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">JOIN</span> TransTab <span class="kw1">AS</span> tt <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ON</span> os<span class="sy0">.</span>AttId<span class="sy0">=</span>tt<span class="sy0">.</span>AttId<span class="br0">&#41;</span> <span class="kw1">AS</span> dt<br />
&nbsp; &nbsp; &nbsp; &nbsp; PIVOT<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Max<span class="br0">&#40;</span><span class="st0">&quot;Value&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FOR</span> Attribute <span class="kw1">IN</span> <span class="br0">&#40;</span><span class="st0">&quot;Typ&quot;</span><span class="sy0">,</span> <span class="st0">&quot;Datum&quot;</span><span class="sy0">,</span> <span class="st0">&quot;Anzahl&quot;</span><span class="sy0">,</span> <span class="st0">&quot;Dings&quot;</span><span class="sy0">,</span> <span class="st0">&quot;Bums&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span> <span class="kw1">AS</span> pvt<br />
<span class="kw1">ORDER</span> <span class="kw1">BY</span> ID</div>
</div>
<p>Die Performance verschlechtert sich dadurch dramatisch. Im Vergleich zum reinen Lesen der Daten fast um Faktor 4. Das liegt am ung&#252;nstigen Zugriffsplan:<br />
<img src='http://www.glorf.it/blog/wp-content/uploads/2008/05/zugriffsplan_mit_join.jpg' alt='Zugriffsplan' /></p>
<p>Wenn man einen PIVOT ohne den Join durchf&#252;hrt, dann erh&#228;lt man die gewohnte, gute Performance: </p>
<div class="codesnip-container" >
<div class="sql codesnip" style="font-family:monospace;"><span class="kw1">SELECT</span> ID<span class="sy0">,</span> <span class="st0">&quot;1&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;Typ&quot;</span><span class="sy0">,</span> <span class="st0">&quot;2&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;Datum&quot;</span><span class="sy0">,</span> <span class="st0">&quot;3&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;Anzahl&quot;</span><span class="sy0">,</span> <span class="st0">&quot;4&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;Dings&quot;</span><span class="sy0">,</span> <span class="st0">&quot;5&quot;</span> <span class="kw1">AS</span> <span class="st0">&quot;Bums&quot;</span><br />
<span class="kw1">FROM</span> &nbsp; &nbsp;<span class="br0">&#40;</span><span class="kw1">SELECT</span> os<span class="sy0">.</span>ID<span class="sy0">,</span> os<span class="sy0">.</span>AttId <span class="kw1">AS</span> Attribute<span class="sy0">,</span> os<span class="sy0">.</span><span class="st0">&quot;Value&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FROM</span> OpenSchema <span class="kw1">AS</span> os <span class="br0">&#41;</span> <span class="kw1">AS</span> dt<br />
&nbsp; &nbsp; &nbsp; &nbsp; PIVOT<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Max<span class="br0">&#40;</span><span class="st0">&quot;Value&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FOR</span> Attribute <span class="kw1">IN</span> <span class="br0">&#40;</span><span class="st0">&quot;1&quot;</span><span class="sy0">,</span> <span class="st0">&quot;2&quot;</span><span class="sy0">,</span> <span class="st0">&quot;3&quot;</span><span class="sy0">,</span> <span class="st0">&quot;4&quot;</span><span class="sy0">,</span> <span class="st0">&quot;5&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span> <span class="kw1">AS</span> pvt<br />
<span class="kw1">ORDER</span> <span class="kw1">BY</span> ID</div>
</div>
<p>Leider muss man dazu die Spaltenamen &quot;hart&quot; codieren. Das ist aber genau das, was man vermeiden wollte als man die Translation-Table einf&#252;hrte. Um auch mit flexiblen Spaltennamen zu einer guten Performance zu kommen, muss man im Batch zun&#228;chst die Spalten ermitteln, das PIVOT-Statement dann dynamisch zusammensetzen und ausf&#252;hren:</p>
<div class="codesnip-container" >
<div class="sql codesnip" style="font-family:monospace;">DECLARE @sqlcmd &nbsp; &nbsp; &nbsp; &nbsp; nvarchar<span class="br0">&#40;</span>1000<span class="br0">&#41;</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; @selectList &nbsp; &nbsp; nvarchar<span class="br0">&#40;</span>1000<span class="br0">&#41;</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; @pivotList&nbsp; &nbsp; &nbsp; nvarchar<span class="br0">&#40;</span>1000<span class="br0">&#41;</span>;</p>
<p><span class="kw1">SELECT</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">&#8211; zun&#228;chst die Spalten aus der SELECT-Liste ermitteln</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; @selectList <span class="sy0">=</span> STUFF<span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><span class="kw1">SELECT</span> N<span class="st0">&#x0027;, &#x0027;</span><span class="sy0">+</span>QUOTENAME<span class="br0">&#40;</span>AttId<span class="br0">&#41;</span><span class="sy0">+</span>N<span class="st0">&#x0027; AS &#x0027;</span></p>
<p>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">+</span>QUOTENAME<span class="br0">&#40;</span>Attribute<span class="br0">&#41;</span> <span class="kw1">AS</span> <span class="br0">&#91;</span>text<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FROM</span> TransTab<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ORDER</span> <span class="kw1">BY</span> AttId<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FOR</span> XML PATH<span class="br0">&#40;</span><span class="st0">&#x0027;&#x0027;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="nu0">1</span><span class="sy0">,</span> <span class="nu0">1</span><span class="sy0">,</span> N<span class="st0">&#x0027;&#x0027;</span><span class="br0">&#41;</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="co1">&#8211; dann die Liste der Spalten ermitteln</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; @pivotList <span class="sy0">=</span> STUFF<span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><span class="kw1">SELECT</span> N<span class="st0">&#x0027;, &#x0027;</span><span class="sy0">+</span>QUOTENAME<span class="br0">&#40;</span>AttId<span class="br0">&#41;</span> <span class="kw1">AS</span> <span class="br0">&#91;</span>text<span class="br0">&#40;</span><span class="br0">&#41;</span><span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FROM</span> TransTab<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ORDER</span> <span class="kw1">BY</span> AttId<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FOR</span> XML PATH<span class="br0">&#40;</span><span class="st0">&#x0027;&#x0027;</span><span class="br0">&#41;</span><span class="br0">&#41;</span><span class="sy0">,</span> <span class="nu0">1</span><span class="sy0">,</span> <span class="nu0">1</span><span class="sy0">,</span> N<span class="st0">&#x0027;&#x0027;</span><span class="br0">&#41;</span>;</p>
<p><span class="kw1">SET</span> @sqlcmd <span class="sy0">=</span> N<span class="st0">&#x0027;SELECT ID, &#x0027;</span><span class="sy0">+</span>@selectList <span class="sy0">+</span>N<span class="st0">&#x0027;<br />
&nbsp; &nbsp; &nbsp; &nbsp; FROM (SELECT os.ID, os.AttId as Attribute, os.&quot;Value&quot; <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; FROM OpenSchema as os) as O<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; PIVOT (Max(&quot;Value&quot;) FOR Attribute IN (&#x0027;</span><span class="sy0">+</span>@pivotList<span class="sy0">+</span>N<span class="st0">&#x0027;)) as P<br />
&nbsp; &nbsp; &nbsp; &nbsp; ORDER BY Id;&#x0027;</span></p>
<p>EXEC sp_executesql @sqlcmd;</p></div>
</div>
<p>Das ausgef&#252;hrte Statement sieht dann so aus:</p>
<div class="codesnip-container" >
<div class="sql codesnip" style="font-family:monospace;"><span class="kw1">SELECT</span> ID<span class="sy0">,</span> &nbsp;<span class="br0">&#91;</span>1<span class="br0">&#93;</span> <span class="kw1">AS</span> <span class="br0">&#91;</span>Typ<span class="br0">&#93;</span><span class="sy0">,</span> <span class="br0">&#91;</span>2<span class="br0">&#93;</span> <span class="kw1">AS</span> <span class="br0">&#91;</span>DATUM<span class="br0">&#93;</span><span class="sy0">,</span> <span class="br0">&#91;</span>3<span class="br0">&#93;</span> <span class="kw1">AS</span> <span class="br0">&#91;</span>Anzahl<span class="br0">&#93;</span><span class="sy0">,</span> <span class="br0">&#91;</span>4<span class="br0">&#93;</span> <span class="kw1">AS</span> <span class="br0">&#91;</span>Dings<span class="br0">&#93;</span><span class="sy0">,</span> <span class="br0">&#91;</span>5<span class="br0">&#93;</span> <span class="kw1">AS</span> <span class="br0">&#91;</span>Bums<span class="br0">&#93;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FROM</span> <span class="br0">&#40;</span><span class="kw1">SELECT</span> os<span class="sy0">.</span>ID<span class="sy0">,</span> os<span class="sy0">.</span>AttId <span class="kw1">AS</span> Attribute<span class="sy0">,</span> os<span class="sy0">.</span><span class="st0">&quot;Value&quot;</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FROM</span> OpenSchema <span class="kw1">AS</span> os<span class="br0">&#41;</span> <span class="kw1">AS</span> O<br />
&nbsp; &nbsp; &nbsp; &nbsp; PIVOT <span class="br0">&#40;</span>Max<span class="br0">&#40;</span><span class="st0">&quot;Value&quot;</span><span class="br0">&#41;</span> <span class="kw1">FOR</span> Attribute <span class="kw1">IN</span> <span class="br0">&#40;</span> <span class="br0">&#91;</span>1<span class="br0">&#93;</span><span class="sy0">,</span> <span class="br0">&#91;</span>2<span class="br0">&#93;</span><span class="sy0">,</span> <span class="br0">&#91;</span>3<span class="br0">&#93;</span><span class="sy0">,</span> <span class="br0">&#91;</span>4<span class="br0">&#93;</span><span class="sy0">,</span> <span class="br0">&#91;</span>5<span class="br0">&#93;</span><span class="br0">&#41;</span><span class="br0">&#41;</span> <span class="kw1">AS</span> P<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">ORDER</span> <span class="kw1">BY</span> Id;</div>
</div>
<p>Die Gesamtperformance dieses Beispiels ist wieder sehr gut.<br />
Insgesamt ergeben sich mit Pivot eine Menge neuer M&#246;glichkeiten. Wenn dabei die Performance wichtig ist, dann muss man sich jedoch genau &#252;berlegen, wie man das Statement auch wirklich schnell ausf&#252;hren kann.</p>
<div class="small">Diese Reihe schrieb ich letzten Oktober als Reaktion auf den Aufruf im SQL-PASS-Newsletter. M&#246;glicherweise erscheint der Artikel noch dort. Ich ver&#246;ffentliche ihn jetzt aber lieber mal bevor er mit dem bald erscheinenden SQL-Server-2008 m&#246;glicherweise veraltet&#8230; <img src='http://www.glorf.it/blog/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </div>
]]></content:encoded>
			<wfw:commentRss>http://www.glorf.it/blog/2008/05/09/sql-talk/performance/sql-server-2005-pivot-auf-joins/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SQL-Server-2005: PIVOT und Performance</title>
		<link>http://www.glorf.it/blog/2008/05/07/sql-talk/performance/sql-server-2005-pivot-und-performance</link>
		<comments>http://www.glorf.it/blog/2008/05/07/sql-talk/performance/sql-server-2005-pivot-und-performance#comments</comments>
		<pubDate>Wed, 07 May 2008 16:58:11 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[Pivot]]></category>
		<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://www.glorf.it/blog/2008/05/07/sql-talk/performance/sql-server-2005-pivot-und-performance</guid>
		<description><![CDATA[Als ich mit dem PIVOT-Konstrukt arbeitete und performante Ergebnisse haben wollte, muss ich erst verstehen, wie es intern arbeitet. Das beschrieb ich im Artikel &#34;SQL-Server-2005: schnelles Pivot?&#34;. Diese Serie soll meine Erfahrungen weitergeben und bei Verstehen der Zusammenh&#228;nge helfen. Mit ein paar Kniffen kann man dann die Ausf&#252;hrung erheblich beschleunigen. Der Quellcode zu diesem vorletzten [...]]]></description>
			<content:encoded><![CDATA[<p>Als ich mit dem PIVOT-Konstrukt arbeitete und performante Ergebnisse haben wollte, muss ich erst verstehen, wie es intern arbeitet. Das beschrieb ich im Artikel &quot;SQL-Server-2005: schnelles Pivot?&quot;. Diese Serie soll meine Erfahrungen weitergeben und bei Verstehen der Zusammenh&#228;nge helfen. Mit ein paar Kniffen kann man dann die Ausf&#252;hrung erheblich beschleunigen. Der Quellcode zu diesem vorletzten Artikel steht &#252;brigens <a href="http://www.glorf.it/files/200710_pivot/05_pivot_performance.sql">hier</a>. </p>
<ul>
<li><a href="http://www.glorf.it/blog/2008/05/03/sql-talk/performance/sql-server-2005-schnelles-pivot">Artikel 1: Wie wird Pivot abgearbeitet?</a></li>
<li><a href="http://www.glorf.it/blog/2008/05/05/sql-talk/performance/sql-server-2005-stoerer-beim-pivot-eliminieren">Artikel 2: St&#246;rer beim PIVOT eliminieren</a></li>
<li><a href="http://www.glorf.it/blog/2008/05/07/sql-talk/performance/sql-server-2005-pivot-und-performance">Artikel 3: Pivot-Performance</a></li>
<li><a href="http://www.glorf.it/blog/2008/05/09/sql-talk/performance/sql-server-2005-pivot-auf-joins">Artikel 4: Pivot-Performance mit Joins</a></li>
</ul>
<p><strong>Performance</strong></p>
<p>Um ein Gef&#252;hl daf&#252;r zu bekommen, wird die im <a href="http://www.glorf.it/blog/2008/05/03/sql-talk/performance/sql-server-2005-schnelles-pivot">ersten Artikel</a> beschriebene Tabelle &quot;OpenSchema&quot; mit 1,9 Mio Datens&#228;tzen gef&#252;llt und ausgelesen. Interessant ist, wie viel &quot;Overhead&quot; ein PIVOT ausmacht. Wenn man ein SELECT auf die ganze Tabelle mit einem PIVOT im gleichen Batch ausf&#252;hrt und dazu mit &quot;Strg+M&quot; den tats&#228;chlichen Zugriffsplan anzeigen l&#228;sst, dann bekommt man neben den Zugriffspl&#228;nen auch den gesch&#228;tzten, prozentualen Anteil der Statements im Batch.</p>
<div class="codesnip-container" >
<div class="sql codesnip" style="font-family:monospace;"><span class="kw1">SELECT</span> ID<span class="sy0">,</span> Attribute<span class="sy0">,</span> <span class="st0">&quot;Value&quot;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FROM</span> OpenSchema</p>
<p><span class="kw1">SELECT</span> ID<span class="sy0">,</span> attr1 <span class="kw1">AS</span> Typ<span class="sy0">,</span> attr2 <span class="kw1">AS</span> Datum<span class="sy0">,</span> attr3 <span class="kw1">AS</span> Anzahl<span class="sy0">,</span> attr4 <span class="kw1">AS</span> Dings<span class="sy0">,</span> attr5 <span class="kw1">AS</span> Bums<br />
<span class="kw1">FROM</span> &nbsp; &nbsp;OpenSchema<br />
&nbsp; &nbsp; &nbsp; &nbsp; PIVOT<br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Max<span class="br0">&#40;</span><span class="st0">&quot;Value&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FOR</span> Attribute <span class="kw1">IN</span> <span class="br0">&#40;</span><span class="st0">&quot;attr1&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr2&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr3&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr4&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr5&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span> <span class="kw1">AS</span> pvt<br />
<span class="kw1">ORDER</span> <span class="kw1">BY</span> ID</div>
</div>
<p>Liegt ein Clustered-Index auf der Spalte ID, dann ist das Verh&#228;ltnis 44% zu 56%. Das bedeutet, der Optimizer sch&#228;tzt etwa 22% Aufschlag f&#252;r den PIVOT gegen&#252;ber dem einfachen Auslesen. Das deckt sich mit dem Zugriffsplan des PIVOT: Hier ben&#246;tigt das einfache Lesen auch 78%:</p>
<p>Das &#228;ndert sich allerdings dramatisch, wenn kein geeigneter Index vorhanden ist. Dann ist das Verh&#228;ltnis im Batch 1% zu 99%.</p>
<p>Da f&#252;r die Gruppierung bereits anhand der ID sortiert werden muss, hat das nachfolgende „ORDER BY ID“ tats&#228;chlich keinerlei Auswirkungen auf den Zugriffsplan. Das sieht allerdings anders aus, falls man nach anderen Kriterien sortieren l&#228;sst. Das wird aber nur ben&#246;tigt, wenn man Joins im PIVOT verwendet. dazu komme ich im letzten Artikel.</p>
<p>Zusammenfassend kann man sagen, dass ein PIVOT immer dann &quot;schnell&quot; ist, wenn ein Clustered Index auf den Gruppierungsfeldern liegt. Ein normaler Index hilft hier nicht weiter. Wie schnell er tats&#228;chlich ist, h&#228;ngt &#8211; wie immer &#8211; von der darunter liegenden Hardware ab&#8230;</p>
<div class="small">Diese Reihe schrieb ich letzten Oktober als Reaktion auf den Aufruf im SQL-PASS-Newsletter. M&#246;glicherweise erscheint der Artikel noch dort. Ich ver&#246;ffentliche ihn jetzt aber lieber mal bevor er mit dem bald erscheinenden SQL-Server-2008 m&#246;glicherweise veraltet&#8230; <img src='http://www.glorf.it/blog/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </div>
]]></content:encoded>
			<wfw:commentRss>http://www.glorf.it/blog/2008/05/07/sql-talk/performance/sql-server-2005-pivot-und-performance/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SQL-Server-2005: St&#246;rer beim PIVOT eliminieren</title>
		<link>http://www.glorf.it/blog/2008/05/05/sql-talk/performance/sql-server-2005-stoerer-beim-pivot-eliminieren</link>
		<comments>http://www.glorf.it/blog/2008/05/05/sql-talk/performance/sql-server-2005-stoerer-beim-pivot-eliminieren#comments</comments>
		<pubDate>Mon, 05 May 2008 16:57:32 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[Pivot]]></category>
		<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://www.glorf.it/blog/2008/05/05/sql-talk/performance/sql-server-2005-stoerer-beim-pivot-eliminieren</guid>
		<description><![CDATA[Als ich mit dem PIVOT-Konstrukt arbeitete und performante Ergebnisse haben wollte, muss ich erst verstehen, wie es intern arbeitet. Das beschrieb ich im Artikel &#34;SQL-Server-2005: schnelles Pivot?&#34;. Diese Serien soll meine Erfahrungen weitergeben und bei Verstehen der Zusammenh&#228;nge helfen. Mit ein paar Kniffen kann man dann die Ausf&#252;hrung erheblich beschleunigen. Das kommt im n&#228;chsten Artikel. [...]]]></description>
			<content:encoded><![CDATA[<p>Als ich mit dem PIVOT-Konstrukt arbeitete und performante Ergebnisse haben wollte, muss ich erst verstehen, wie es intern arbeitet. Das beschrieb ich im Artikel &quot;<a href="http://www.glorf.it/blog/2008/05/03/sql-talk/performance/sql-server-2005-schnelles-pivot">SQL-Server-2005: schnelles Pivot?</a>&quot;. Diese Serien soll meine Erfahrungen weitergeben und bei Verstehen der Zusammenh&#228;nge helfen. Mit ein paar Kniffen kann man dann die Ausf&#252;hrung erheblich beschleunigen. Das kommt im n&#228;chsten Artikel. Der Quellcode zu diesem Artikel steht &#252;brigens <a href="http://www.glorf.it/files/200710_pivot/04_stoerer_eliminieren.sql">hier</a>.</p>
<ul>
<li><a href="http://www.glorf.it/blog/2008/05/03/sql-talk/performance/sql-server-2005-schnelles-pivot">Artikel 1: Wie wird Pivot abgearbeitet?</a></li>
<li><a href="http://www.glorf.it/blog/2008/05/05/sql-talk/performance/sql-server-2005-stoerer-beim-pivot-eliminieren">Artikel 2: St&#246;rer beim PIVOT eliminieren</a></li>
<li><a href="http://www.glorf.it/blog/2008/05/07/sql-talk/performance/sql-server-2005-pivot-und-performance">Artikel 3: Pivot-Performance</a></li>
<li><a href="http://www.glorf.it/blog/2008/05/09/sql-talk/performance/sql-server-2005-pivot-auf-joins">Artikel 4: Pivot-Performance mit Joins</a></li>
</ul>
<p><strong>Eliminierung von St&#246;rern</strong></p>
<p>Wenn in der Tabelle weitere Spalten enthalten sind, dann kann das mit PIVOT zu unerwarteten Ergebnissen f&#252;hren. Wenn die Tabelle OpenSchema bspw. auch noch die Spalte „Stoerer“ enth&#228;lt:<br />
<img src='http://www.glorf.it/blog/wp-content/uploads/2008/05/tabopenschema_mit_stoerer.jpg' alt='Tabelle Openschema mit St&#246;rer' /></p>
<p>Wenn man nun einfach das PIVOT aus dem <a href="http://www.glorf.it/blog/2008/05/03/sql-talk/performance/sql-server-2005-schnelles-pivot">letzten Artikel</a> einfach verwendet, dann f&#252;hrt das zu komischen Ergebnissen.</p>
<div class="codesnip-container" >
<div class="sql codesnip" style="font-family:monospace;"><span class="kw1">SELECT</span> ID<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; attr1 <span class="kw1">AS</span> Typ<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; attr2 <span class="kw1">AS</span> Datum<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; attr3 <span class="kw1">AS</span> Anzahl<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; attr4 <span class="kw1">AS</span> Dings<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; attr5 <span class="kw1">AS</span> Bums<br />
<span class="kw1">FROM</span> OpenSchema<br />
&nbsp; &nbsp; &nbsp; &nbsp; PIVOT &nbsp; <span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MAX<span class="br0">&#40;</span><span class="st0">&quot;Value&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FOR</span> Attribute <span class="kw1">IN</span> <span class="br0">&#40;</span><span class="st0">&quot;attr1&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr2&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr3&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr4&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr5&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span> <span class="kw1">AS</span> pvt<br />
<span class="kw1">ORDER</span> <span class="kw1">BY</span> ID</div>
</div>
<p><img src='http://www.glorf.it/blog/wp-content/uploads/2008/05/unerwartetes_ergebnis.jpg' alt='Unerwartetes Ergebnis' /></p>
<p>Das liegt an der impliziten Gruppierung aller Felder aus der Basistabelle. Es kann also passieren, dass durch eine unbedachte Tabellenerweiterung einige SELECTs ein v&#246;llig anderes Ergebnis liefern. Das ist nicht im Geiste von SQL, ist unangenehm und soll vermieden werden. Daher rate ich dazu PIVOT-Statements immer so zu formulieren, dass sie nicht auf der Basistabelle arbeiten, sondern einer Derived-Table: </p>
<div class="codesnip-container" >
<div class="sql codesnip" style="font-family:monospace;"><span class="kw1">SELECT</span> ID<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; attr1 <span class="kw1">AS</span> Typ<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; attr2 <span class="kw1">AS</span> Datum<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; attr3 <span class="kw1">AS</span> Anzahl<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; attr4 <span class="kw1">AS</span> Dings<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; attr5 <span class="kw1">AS</span> Bums<br />
<span class="kw1">FROM</span> <span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">SELECT</span> ID<span class="sy0">,</span> Attribute<span class="sy0">,</span> Value<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FROM</span> OpenSchema<span class="br0">&#41;</span> <span class="kw1">AS</span> dt<br />
&nbsp; &nbsp; &nbsp; &nbsp; PIVOT &nbsp; <span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MAX<span class="br0">&#40;</span><span class="st0">&quot;Value&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FOR</span> Attribute <span class="kw1">IN</span> <span class="br0">&#40;</span><span class="st0">&quot;attr1&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr2&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr3&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr4&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr5&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span> <span class="kw1">AS</span> pvt<br />
<span class="kw1">ORDER</span> <span class="kw1">BY</span> ID</div>
</div>
<p>Das liefert nicht nur in diesem Fall das erwartete Ergebnis, sondern auch nach weiteren Tabellen&#228;nderungen. Der Zugriffsplan ist entspricht dem <a href="http://www.glorf.it/blog/2008/05/03/sql-talk/performance/sql-server-2005-schnelles-pivot">ersten Artikel</a>.</p>
<div class="small">Diese Reihe schrieb ich letzten Oktober als Reaktion auf den Aufruf im SQL-PASS-Newsletter. M&#246;glicherweise erscheint der Artikel noch dort. Ich ver&#246;ffentliche ihn jetzt aber lieber mal bevor er mit dem bald erscheinenden SQL-Server-2008 m&#246;glicherweise veraltet&#8230; <img src='http://www.glorf.it/blog/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </div>
]]></content:encoded>
			<wfw:commentRss>http://www.glorf.it/blog/2008/05/05/sql-talk/performance/sql-server-2005-stoerer-beim-pivot-eliminieren/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SQL-Server-2005: schnelles Pivot?</title>
		<link>http://www.glorf.it/blog/2008/05/03/sql-talk/performance/sql-server-2005-schnelles-pivot</link>
		<comments>http://www.glorf.it/blog/2008/05/03/sql-talk/performance/sql-server-2005-schnelles-pivot#comments</comments>
		<pubDate>Sat, 03 May 2008 16:51:13 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[Pivot]]></category>
		<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://www.glorf.it/blog/2008/05/03/sql-talk/performance/sql-server-2005-schnelles-pivot</guid>
		<description><![CDATA[Als ich mit dem PIVOT-Konstrukt arbeitete und performante Ergebnisse haben wollte, muss ich erst verstehen, wie es intern arbeitet. Mit ein paar Kniffen kann man dann die Ausf&#252;hrung erheblich beschleunigen. Diese Serien soll meine Erfahrungen weitergeben und bei Verstehen der Zusammenh&#228;nge helfen. Der Quellcode zu diesem Artikel steht &#252;brigens hier.

Artikel 1: Wie wird Pivot abgearbeitet?
Artikel [...]]]></description>
			<content:encoded><![CDATA[<p>Als ich mit dem PIVOT-Konstrukt arbeitete und performante Ergebnisse haben wollte, muss ich erst verstehen, wie es intern arbeitet. Mit ein paar Kniffen kann man dann die Ausf&#252;hrung erheblich beschleunigen. Diese Serien soll meine Erfahrungen weitergeben und bei Verstehen der Zusammenh&#228;nge helfen. Der Quellcode zu diesem Artikel steht &#252;brigens <a href="http://www.glorf.it/files/200710_pivot/01_selfmade_pivot.sql">hier</a>.</p>
<ul>
<li><a href="http://www.glorf.it/blog/2008/05/03/sql-talk/performance/sql-server-2005-schnelles-pivot">Artikel 1: Wie wird Pivot abgearbeitet?</a></li>
<li><a href="http://www.glorf.it/blog/2008/05/05/sql-talk/performance/sql-server-2005-stoerer-beim-pivot-eliminieren">Artikel 2: St&#246;rer beim PIVOT eliminieren</a></li>
<li><a href="http://www.glorf.it/blog/2008/05/07/sql-talk/performance/sql-server-2005-pivot-und-performance">Artikel 3: Pivot-Performance</a></li>
<li><a href="http://www.glorf.it/blog/2008/05/09/sql-talk/performance/sql-server-2005-pivot-auf-joins">Artikel 4: Pivot-Performance mit Joins</a></li>
</ul>
<p><strong>Wie wird PIVOT ausgef&#252;hrt?</strong></p>
<p>Angenommen die Tabelle „OpenSchema“ besteht aus drei Spalten: einer ID, einem Attributbezeichner und einer Wert-Spalte. Das s&#228;he etwa so aus:<br />
<img src='http://www.glorf.it/blog/wp-content/uploads/2008/05/tabopenschema_2.jpg' alt='Tabelle Openschema' /></p>
<div class="small">Diese Tabelle ist &#252;brigens einem Beispiel von Itzik Ben-Gan (siehe <a href="http://www.sql.co.il">www.sql.co.il</a>) entnommen, weil er das PIVOT-Statement am besten erkl&#228;ren kann. Ehre wem Ehre geb&#252;hrt.</div>
<p>Um die Ergebnisse dieser &quot;Kennziffer-Wert-Tabelle&quot; in &quot;relationaler&quot; Form zu pr&#228;sentieren, m&#252;ssen die Ergebnisse pivotiert werden:<br />
<img src='http://www.glorf.it/blog/wp-content/uploads/2008/05/gewuenschtesergebnis_2.jpg' alt='gew&#252;nschtes Ergebnis'  /></p>
<p>Vor dem SQL-Server-2005 war dazu ein recht kompliziert aussehendes Konstrukt notwendig: Man musste pro Ergebnis eine Aggregatfunktion auswerten, dass intern mittels CASE nur die gew&#252;nschten Ergebnisse auswertet:</p>
<div class="codesnip-container" >
<div class="sql codesnip" style="font-family:monospace;"><span class="kw1">SELECT</span> ID<span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; MAX<span class="br0">&#40;</span>CASE WHEN <span class="st0">&quot;Attribute&quot;</span> <span class="sy0">=</span> <span class="st0">&#x0027;attr1&#x0027;</span> THEN <span class="st0">&quot;VALUE&quot;</span> END<span class="br0">&#41;</span> <span class="kw1">AS</span> <span class="st0">&quot;Typ&quot;</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; MAX<span class="br0">&#40;</span>CASE WHEN <span class="st0">&quot;Attribute&quot;</span> <span class="sy0">=</span> <span class="st0">&#x0027;attr2&#x0027;</span> THEN <span class="st0">&quot;VALUE&quot;</span> END<span class="br0">&#41;</span> <span class="kw1">AS</span> <span class="st0">&quot;Datum&quot;</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; MAX<span class="br0">&#40;</span>CASE WHEN <span class="st0">&quot;Attribute&quot;</span> <span class="sy0">=</span> <span class="st0">&#x0027;attr3&#x0027;</span> THEN <span class="st0">&quot;VALUE&quot;</span> END<span class="br0">&#41;</span> <span class="kw1">AS</span> <span class="st0">&quot;Anzahl&quot;</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; MAX<span class="br0">&#40;</span>CASE WHEN <span class="st0">&quot;Attribute&quot;</span> <span class="sy0">=</span> <span class="st0">&#x0027;attr4&#x0027;</span> THEN <span class="st0">&quot;VALUE&quot;</span> END<span class="br0">&#41;</span> <span class="kw1">AS</span> <span class="st0">&quot;Dings&quot;</span><span class="sy0">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; MAX<span class="br0">&#40;</span>CASE WHEN <span class="st0">&quot;Attribute&quot;</span> <span class="sy0">=</span> <span class="st0">&#x0027;attr5&#x0027;</span> THEN <span class="st0">&quot;VALUE&quot;</span> END<span class="br0">&#41;</span> <span class="kw1">AS</span> <span class="st0">&quot;Bums&quot;</span><br />
<span class="kw1">FROM</span> OpenSchema<br />
<span class="kw1">GROUP</span> <span class="kw1">BY</span> ID <br />
<span class="kw1">ORDER</span> <span class="kw1">BY</span> ID</div>
</div>
<p>In dieser Form stellten es Rozenshtein, Abranovich und Birger schon 1992 im &quot;SQL Server Forum 12/1992&quot;  vor. Damals gab es nur das CASE-Konstrukt noch nicht, deswegen verwendeten sie verschiedene Tricks, um ein CASE auf mathematische Weise zu erreichen. Der Vorteil bei dieser alten Schreibweise ist, dass man die Gruppierung explizit angeben kann.</p>
<p><strong>Pivotieren mit Pivot</strong></p>
<p>Im SQL-Server-2005 f&#252;hrte Microsoft bekanntlich das PIVOT-Konstrukt ein, um das Ganze zu vereinfachen. Im PIVOT-Statement wird die Gruppierung hingegen implizit durchgef&#252;hrt: Es wird nach allen Spalten der Tabelle gruppiert, die nicht in den Klammern des PIVOT genannt werden. Unten werden &quot;Value&quot; und &quot;Attribute&quot; genannt, also wird nach allen verbleibenden gruppiert: hier nur &quot;ID&quot;.</p>
<div class="codesnip-container" >
<div class="sql codesnip" style="font-family:monospace;"><span class="kw1">SELECT</span> ID<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; attr1 <span class="kw1">AS</span> Typ<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; attr2 <span class="kw1">AS</span> Datum<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; attr3 <span class="kw1">AS</span> Anzahl<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; attr4 <span class="kw1">AS</span> Dings<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; attr5 <span class="kw1">AS</span> Bums<br />
<span class="kw1">FROM</span> OpenSchema<br />
&nbsp; &nbsp; &nbsp; &nbsp; PIVOT &nbsp; <span class="br0">&#40;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; MAX<span class="br0">&#40;</span><span class="st0">&quot;Value&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">FOR</span> Attribute <span class="kw1">IN</span> <span class="br0">&#40;</span><span class="st0">&quot;attr1&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr2&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr3&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr4&quot;</span><span class="sy0">,</span> <span class="st0">&quot;attr5&quot;</span><span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span> <span class="kw1">AS</span> pvt<br />
<span class="kw1">ORDER</span> <span class="kw1">BY</span> ID</div>
</div>
<p>Das PIVOT-Konstrukt ist genauso implementiert, wie es bereits oben beschrieben wurde. Das schlie&#223;e ich daraus, dass in beiden F&#228;llen der gleiche Zugriffsplan verwendet wird:<br />
<img src='http://www.glorf.it/blog/wp-content/uploads/2008/05/pivot_executionplan.jpg' alt='Zugriffsplan' /></p>
<p>Nat&#252;rlich passiert es &#246;fters, dass der Optimizer einen semantisch &#228;quivalenten Zugriffsplan verwendet, wenn er effizienter ist. Das ist hier jedoch unwahrscheinlich.<br />
Die &quot;&#228;ltere&quot; Form hilft mir zu verstehen, wann ein PIVOT schnell ausgef&#252;hrt werden kann und wann er einfach langsam sein <em>muss</em>. Sie hat zudem den Vorteil, dass man pro Spalte entscheiden kann, welche Aggregatfunktion verwendet werden soll. Beim neuen PIVOT wird f&#252;r alle die gleiche genutzt.</p>
<div class="small">Diese Reihe schrieb ich letzten Oktober als Reaktion auf den Aufruf im SQL-PASS-Newsletter. M&#246;glicherweise erscheint der Artikel noch dort. Ich ver&#246;ffentliche ihn jetzt aber lieber mal bevor er mit dem bald erscheinenden SQL-Server-2008 veraltet&#8230; <img src='http://www.glorf.it/blog/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /> </div>
]]></content:encoded>
			<wfw:commentRss>http://www.glorf.it/blog/2008/05/03/sql-talk/performance/sql-server-2005-schnelles-pivot/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ergebnisse vom Profiler und PerfMon zusammenf&#252;hren</title>
		<link>http://www.glorf.it/blog/2008/02/15/sql-talk/performance/ergebnisse-vom-profiler-und-perfmon-zusammenfuehren</link>
		<comments>http://www.glorf.it/blog/2008/02/15/sql-talk/performance/ergebnisse-vom-profiler-und-perfmon-zusammenfuehren#comments</comments>
		<pubDate>Fri, 15 Feb 2008 17:20:11 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://www.glorf.it/blog/2008/02/15/sql-talk/performance/ergebnisse-vom-profiler-und-perfmon-zusammenfuehren</guid>
		<description><![CDATA[Alle, die nicht auf den erstklassigen Performance-Workshop des PSS-Teams am 14.4. gehen k&#246;nnen, empfehle ich den Artikel &#34;Integrating Profiler and PerfMon Log Files&#34; auf SQLTeam.com.
Darin wird beschrieben, wie man die Ergebnisse des PerfMon in den Profiler laden kann, um sich eine kombinierte Darstellung ansehen zu k&#246;nnen. Dadurch wird es bspw. m&#246;glich Infos zur Festplatten Nutzung [...]]]></description>
			<content:encoded><![CDATA[<p>Alle, die nicht auf den erstklassigen <a href="http://www.glorf.it/blog/2008/02/14/allgemein/performance-toolset-workshop">Performance-Workshop des PSS-Teams</a> am 14.4. gehen k&#246;nnen, empfehle ich den Artikel &quot;<a href="http://www.sqlteam.com/article/integrating-profiler-and-perfmon-log-files">Integrating Profiler and PerfMon Log Files</a>&quot; auf SQLTeam.com.</p>
<p>Darin wird beschrieben, wie man die Ergebnisse des PerfMon in den Profiler laden kann, um sich eine kombinierte Darstellung ansehen zu k&#246;nnen. Dadurch wird es bspw. m&#246;glich Infos zur Festplatten Nutzung oder der CPU-Nutzung mit den Traces des Profilers (also z.B. mit den SQL-Befehlen und deren Zeiten) zu kombinieren. Ohne diesen irren Trick ist man immer auf einem Auge blind&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.glorf.it/blog/2008/02/15/sql-talk/performance/ergebnisse-vom-profiler-und-perfmon-zusammenfuehren/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>beliebte Performance-Fallen</title>
		<link>http://www.glorf.it/blog/2007/08/12/sql-talk/performance/beliebte-performance-fallen</link>
		<comments>http://www.glorf.it/blog/2007/08/12/sql-talk/performance/beliebte-performance-fallen#comments</comments>
		<pubDate>Sun, 12 Aug 2007 10:20:44 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://www.glorf.it/blog/2007/08/12/sql-talk/performance/beliebte-performance-fallen</guid>
		<description><![CDATA[Einer meiner Jobs ist die Analyse von Performanceproblemen bei unseren Anwendungen. Deswegen habe ich hier einfach mal ein paar typische Performance-Fallen bei datenbanknutzenden Anwendungen geschrieben. Nat&#252;rlich ist es schwierig pauschale Ratschl&#228;ge abzugeben. Es besteht immer die Gefahr platt oder arrogant zu wirken. So ist es nicht gemeint.
Das alles l&#228;uft unter dem Motto: Gefahren, die man [...]]]></description>
			<content:encoded><![CDATA[<p>Einer meiner Jobs ist die Analyse von Performanceproblemen bei unseren Anwendungen. Deswegen habe ich hier einfach mal ein paar typische Performance-Fallen bei datenbanknutzenden Anwendungen geschrieben. Nat&#252;rlich ist es schwierig pauschale Ratschl&#228;ge abzugeben. Es besteht immer die Gefahr platt oder arrogant zu wirken. So ist es nicht gemeint.<br />
Das alles l&#228;uft unter dem Motto: Gefahren, die man kennt, kann man umschiffen&#8230;</p>
<p>* Nur die wirklich notwendigen Datens&#228;tze anfordern<br />
Werden in der Anwendung beispielsweise Cursor verwendet (gerne bei ODBC oder MFC), dann werden oft nur die ersten 30 Datens&#228;tze lesen (mehr passt nicht auf den Bildschirm, der Rest wird dann nur im Bedarfsfalls gelesen (z.B. in einem virtuellen List-Control). Je nachdem welchen Cursor-Typ man verwendet und wie komplex das Statement ist, muss der SQL-Server aber dennoch alle vom Datensatz betroffenen Datens&#228;tze lesen, z.B. 10000, obwohl in der Regel nur die aktuellesten 20 tats&#228;chlich von Kunden tats&#228;chlich werden. Das gilt h&#228;ufig dann, wenn der SQL-Server die Datens&#228;tze sortiert liefern soll, aber kein entsprechender Index da ist.</p>
<p>* Immer alle ben&#246;tigen Daten auf einen Schlag anfordern<br />
Ich sehe immer wieder, dass eine Reihe von Datens&#228;tzen aus einer Tabelle gelesen wird. Sagen wir mal die Daten einer Rechnung. Dann wird pro Rechnung nachgelesen wie der verantwortliche Sachbearbeiter &#8211; von dem ja nur die ID in dem Datensatz steckt &#8211; hei&#223;t. Dann wird der Name des Kunden (aus dem Gleichen Grund) pro Rechnung nachgelesen. Das sollte man bitte auf keinen Fall machen. Damit belegt man nur, dass der Chef das Geld oder die Zeit f&#252;r eine SQL-Schulung nicht ausgeben wollte. Und kauft damit eine schlechte Performance ein.<br />
Statt dessen verwendet man einen Join, um die beiden Namen zusammen mit den Rechnungsinformationen zu lesen. Das ist einfach und effizient.</p>
<p>* die Objektorientierung nicht um seiner selbst Willen durchziehen<br />
Leider passiert es sonst regelm&#228;&#223;ig, dass die aufgerufene Klasse, die ja meist nicht wei&#223; in welchem Kontext sie gerufen wird, sich unsinnig verh&#228;lt.<br />
Beispielsweise soll der Status von allen Bestellungen eines Kunden auf &quot;fakturiert&quot; gesetzt werden, wenn die Rechnung daf&#252;r raus ging. Die beste Performance bekommt man wenn man nur ein einziges UPDATE daf&#252;r absetzt. Wenn man aber (vermeintlich) &quot;objektorientiert&quot; vorgeht, dann werden in einer Schleife alle Bestellungen durch genudelt, und pro Bestellung ein UPDATE abgesetzt. Das kostet unglaublich Performance und ist so als ob man jede Morgen f&#252;r jedes einzelne Br&#246;tchen wieder einzeln zum B&#228;cker f&#228;hrt. Dabei k&#246;nnte man doch mit einer Fuhre alle 10 Br&#246;tchen mitbringen.</p>
<p>* Schleifen<br />
Das gilt nat&#252;rlich nicht nur f&#252;r die objekt-orientierte Vorgehensweise: Das gilt f&#252;r alle Programmiersprachen und Architekturen. Deswegen nenne ich es noch als eigenen Punkt: Jede Schleife kostet unn&#246;tig Zeit. Mehr Zeit als man denkt.</p>
<p>* keine oder unsinnige Clustered-Indexe<br />
Den Prim&#228;rschl&#252;ssel zum Clustered-Index zu machen ist nach meiner Erfahrung meistens suboptimal und lohnt sich nur bei zusammengesetzten Prim&#228;rschl&#252;sseln. Wenn man eine ID verwendet, dann sollte man Clustered-Index lieber auf ein oder mehrere fachliche Felder legen, die unter Umst&#228;nden sogar Fremdschl&#252;ssel irgendwohin sind. Dadurch erreicht man, dass fachlich zusammengeh&#246;rige Felder auf der gleichen oder wenigstens auf benachbarten Datenseiten liegen.<br />
Der Clustered-Index sollte &#252;brigens nicht zu &quot;breit&quot; sein, weil am SQL-Server-2005 die anderen Index nicht mehr auf die Datenseiten verweisen, sondern auf den Eintrag im Clustered-Index&#8230;</p>
<p>Fr&#252;her hatte ich auch noch eine ganze Palette an Tricks zur Mikro-Optimierung auf Lager. Aber der Optimizer des SQL-Servers ist jetzt so clever geworden, dass die meisten zum alten Eisen geh&#246;ren. Au&#223;erdem habe ich echt die Erfahrung gemacht, dass die dicken Brocken so viel Zeit verschlingen, dass sich die Mikrogeschichten (wie z.B. Parametrisierung) erst sehr sp&#228;t rentieren&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.glorf.it/blog/2007/08/12/sql-talk/performance/beliebte-performance-fallen/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Trigger &#8211; wie viele S&#228;tze sind betroffen?</title>
		<link>http://www.glorf.it/blog/2007/05/18/sql-talk/performance/trigger-wie-viele-saetze-sind-betroffen</link>
		<comments>http://www.glorf.it/blog/2007/05/18/sql-talk/performance/trigger-wie-viele-saetze-sind-betroffen#comments</comments>
		<pubDate>Fri, 18 May 2007 18:23:20 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://www.glorf.it/blog/2007/05/18/sql-talk/performance/trigger-wie-viele-saetze-sind-betroffen</guid>
		<description><![CDATA[Gestern las ich bei coreworxx den Artikel &#34;UPDATE Trigger &#8211; schon mal reingefallen?&#34; und wollte auch gleich einen Kommentar schreiben. Weil ich dazu aber einen Google-Account einrichten m&#252;sste, antworte ich lieber hier und setze einen Trackback&#8230;
Schade, weil ich jetzt erst mal den Kontext herstellen muss, damit man nicht erst den einen Artikel lesen muss, um [...]]]></description>
			<content:encoded><![CDATA[<p>Gestern las ich bei coreworxx den Artikel &quot;<a href="http://coreworxx.blogspot.com/2007/05/update-trigger-schon-mal-reingefallen.html">UPDATE Trigger &#8211; schon mal reingefallen?</a>&quot; und wollte auch gleich einen Kommentar schreiben. Weil ich dazu aber einen Google-Account einrichten m&#252;sste, antworte ich lieber hier und setze einen Trackback&#8230;<br />
Schade, weil ich jetzt erst mal den Kontext herstellen muss, damit man nicht erst den einen Artikel lesen muss, um diesen zu verstehen. &#220;brigens liebe ich es Artikel &#252;ber SQL-Themen zu lesen: also bitte nicht abschrecken lassen&#8230;</p>
<p>Andreas G&#252;mbel beschreibt in dem Artikel, wie man mittels der Tabellen INSERTED und DELETED mitbekommt wie die Werte des ge&#228;nderten Datensatzes vor und nach der &#196;nderung kommt. Hier eines seiner Beispiele:</p>
<div class="codesnip-container" >
<div class="sql codesnip" style="font-family:monospace;"><span class="kw1">CREATE</span> <span class="kw1">TRIGGER</span> <span class="br0">&#91;</span>TRIGGER_NAME_Update<span class="br0">&#93;</span> <span class="kw1">ON</span> <span class="br0">&#91;</span>dbo<span class="br0">&#93;</span><span class="sy0">.</span><span class="br0">&#91;</span>myTableName<span class="br0">&#93;</span> <br />
<span class="kw1">FOR</span> <span class="kw1">UPDATE</span><br />
<span class="kw1">AS</span><br />
DECLARE @myInsertId int<br />
<span class="kw1">SELECT</span> @myInsertId<span class="sy0">=</span>myTableId <span class="kw1">FROM</span> INSERTED<br />
<span class="br0">&#91;</span>weitere Verarbeitung <span class="sy0">&#8230;</span><span class="br0">&#93;</span></div>
</div>
<p>Das ist v&#246;llig korrekt beschrieben.<br />
Aber dennoch w&#252;rde ich von diesem Statement abraten. Es geht strikt davon aus, dann immer nur ein einziger Datensatz ge&#228;ndert wird. Um die optimale Performance zu erreichen werden in Datenbanksystemen aber immer m&#246;glichst alle betroffenen Datens&#228;tze mit einem Schwupps ge&#228;ndert.<br />
Ich merke immer wieder, dass es gerade altgedienten Programmierern schwer f&#228;llt, dass man jetzt nicht mehr m&#252;hsam alles &#252;ber eine Schleife machen muss: In SQL macht man das mit einem einzigen Befehl. Hier mein Lieblingsbeispiel: Alle Mitarbeiter der Datenbankbetreuung bekommen eine 10%ige Gehaltserh&#246;hung&#8230;</p>
<div class="codesnip-container" >
<div class="sql codesnip" style="font-family:monospace;"><span class="kw1">UPDATE</span> Stammdaten<span class="sy0">.</span>Mitarbeiter<br />
&nbsp; &nbsp; &nbsp;<span class="kw1">SET</span> Gehalt <span class="sy0">=</span> Gehalt <span class="sy0">*</span> 0<span class="sy0">.</span>1<br />
&nbsp; &nbsp; &nbsp;<span class="kw1">WHERE</span> team<span class="sy0">=</span><span class="st0">&#x0027;Datenbanktechnologie&#x0027;</span></div>
</div>
<p>Das Statement dauert fast genau so lange wie der Update eines <em>einzelnen</em> Datensatzes.  Deswegen macht man eher selten einen Update auf einen Satz. Und selbst wenn man jetzt fest &#252;berzeugt ist, dass man niemals mehrere S&#228;tze gleichzeitig wird. Sollte man das dennoch vorsehen, weil sich meiner Erfahrung nach sehr schnell die Randbedingungen &#228;ndern und &quot;Puff&quot; knallt der Trigger mitten im laufenden Betrieb an die Wand.<br />
Das ist &#252;brigens eine Tuningma&#223;nahme, die ich immer wieder bei Performanceanalysen empfehlen muss: Mengenupdates anstelle von Schleifen. In der ersten Runde geht es meist auf gar keinen Fall, weil man ja in dem jeweiligen Objekt keine Infos dar&#252;ber hat, welche anderen Objekte sonst noch betroffen sind. Wenn der Druck von Seiten der Kunden steigt, geht es meistens doch&#8230;</p>
<p>Daher: Im Trigger selber bitte immer die Verarbeitung von mehreren Datens&#228;tzen vorsehen.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.glorf.it/blog/2007/05/18/sql-talk/performance/trigger-wie-viele-saetze-sind-betroffen/feed</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>SQL-Server: Wer sind heute meine 5 Langl&#228;ufer?</title>
		<link>http://www.glorf.it/blog/2007/04/24/sql-server/sql-server-wer-sind-heute-meine-5-langlaeufer</link>
		<comments>http://www.glorf.it/blog/2007/04/24/sql-server/sql-server-wer-sind-heute-meine-5-langlaeufer#comments</comments>
		<pubDate>Tue, 24 Apr 2007 15:56:24 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Performance]]></category>
		<category><![CDATA[SQL Server]]></category>

		<guid isPermaLink="false">http://www.glorf.it/blog/2007/04/24/sql-server/sql-server-wer-sind-heute-meine-5-langlaeufer</guid>
		<description><![CDATA[Im Artikel Dynamic Management Views and Functions&#34; auf databasejournal.com fand ich folgende sehr n&#252;tzlich Abfrage zur Ermittlung der durchschnittlichen CPU-Zeit der 5 l&#228;ngsten Abfragen:

SELECT TOP 5 total_worker_time/execution_count &#x0027;Avg CPU Time&#x0027;,
&#160; &#160; SUBSTRING&#40;st.text, &#40;qs.statement_start_offset/2&#41;+1, 
&#160; &#160; &#160; &#160; &#40;&#40;CASE qs.statement_end_offset
&#160; &#160; &#160; &#160; &#160; WHEN -1 THEN DATALENGTH&#40;st.text&#41;
&#160; &#160; &#160; &#160; &#160;ELSE qs.statement_end_offset
&#160; &#160; &#160; &#160; [...]]]></description>
			<content:encoded><![CDATA[<p>Im Artikel <a href="http://www.databasejournal.com/features/mssql/print.php/3608296">Dynamic Management Views and Functions</a>&quot; auf databasejournal.com fand ich folgende sehr n&#252;tzlich Abfrage zur Ermittlung der durchschnittlichen CPU-Zeit der 5 l&#228;ngsten Abfragen:</p>
<div class="codesnip-container" >
<div class="sql codesnip" style="font-family:monospace;"><span class="kw1">SELECT</span> TOP <span class="nu0">5</span> total_worker_time<span class="sy0">/</span>execution_count <span class="st0">&#x0027;Avg CPU Time&#x0027;</span><span class="sy0">,</span><br />
&nbsp; &nbsp; SUBSTRING<span class="br0">&#40;</span>st<span class="sy0">.</span>text<span class="sy0">,</span> <span class="br0">&#40;</span>qs<span class="sy0">.</span>statement_start_offset<span class="sy0">/</span>2<span class="br0">&#41;</span><span class="sy0">+</span>1<span class="sy0">,</span> <br />
&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#40;</span><span class="br0">&#40;</span>CASE qs<span class="sy0">.</span>statement_end_offset<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; WHEN <span class="sy0">-</span>1 THEN DATALENGTH<span class="br0">&#40;</span>st<span class="sy0">.</span>text<span class="br0">&#41;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;ELSE qs<span class="sy0">.</span>statement_end_offset<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;END <span class="sy0">-</span> qs<span class="sy0">.</span>statement_start_offset<span class="br0">&#41;</span><span class="sy0">/</span>2<span class="br0">&#41;</span> <span class="sy0">+</span> 1<span class="br0">&#41;</span>statement_text<br />
<span class="kw1">FROM</span> sys<span class="sy0">.</span>dm_exec_query_stats qs <br />
<span class="kw1">CROSS</span> APPLY sys<span class="sy0">.</span>dm_exec_sql_text<span class="br0">&#40;</span>qs<span class="sy0">.</span>sql_handle<span class="br0">&#41;</span> st<br />
<span class="kw1">ORDER</span> <span class="kw1">BY</span> total_worker_time<span class="sy0">/</span>execution_count <span class="kw1">DESC</span>;</div>
</div>
<p>Risiken und Nebenwirkungen: In den &quot;Dynamic-Management-Views&quot; sind nur die Infos seit dem letzten Start des Dienstes enthalten&#8230;</p>
<p>PS: Wer wissen will, was aktuell gerade l&#228;uft, findet die Antwort mit folgender Abfrage von <a href="http://ursgehrig.wordpress.com/2007/04/19/was-lauft-auf-dem-server/">Urs Gehrig</a>:</p>
<div class="codesnip-container" >
<div class="sql codesnip" style="font-family:monospace;"><span class="kw1">SELECT</span> sp<span class="sy0">.</span>spid <span class="br0">&#91;</span>SPID<span class="br0">&#93;</span><span class="sy0">,</span> sp<span class="sy0">.</span>last_batch <span class="br0">&#91;</span>Executed at<span class="br0">&#93;</span><span class="sy0">,</span> st<span class="sy0">.</span>text <span class="br0">&#91;</span>SQL<span class="br0">&#93;</span><br />
<span class="kw1">FROM</span> sys<span class="sy0">.</span>sysprocesses sp<br />
&nbsp; <span class="kw1">OUTER</span> APPLY fn_get_sql<span class="br0">&#40;</span>sp<span class="sy0">.</span>sql_handle<span class="br0">&#41;</span> st<br />
<span class="kw1">WHERE</span> st<span class="sy0">.</span>text <span class="kw1">IS</span> <span class="kw1">NOT</span> <span class="kw1">NULL</span></div>
</div>
]]></content:encoded>
			<wfw:commentRss>http://www.glorf.it/blog/2007/04/24/sql-server/sql-server-wer-sind-heute-meine-5-langlaeufer/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SQL Server Wait Events</title>
		<link>http://www.glorf.it/blog/2007/04/04/sql-talk/performance/sql-server-wait-events</link>
		<comments>http://www.glorf.it/blog/2007/04/04/sql-talk/performance/sql-server-wait-events#comments</comments>
		<pubDate>Wed, 04 Apr 2007 16:43:47 +0000</pubDate>
		<dc:creator>Thomas</dc:creator>
				<category><![CDATA[Performance]]></category>

		<guid isPermaLink="false">http://www.glorf.it/blog/2007/04/04/sql-talk/performance/sql-server-wait-events</guid>
		<description><![CDATA[Bei SimpleTalk.com beschreibt Mario Broodbakker in dem Artikel &#34;SQL Server Wait Events: Taking the Guesswork out of Performance Profiling&#34; wie man am SQL-Server anhand der Warteschlangen Performancebremsen ausmachen kann. Der Artikel ist gut zu lesen. Allerdings st&#246;&#223;t mir etwas s&#228;uerlich auf, dass nicht deutlich genug rauskommt, dass diese Idee Tom Davidson schon vor Jahren beschrieben [...]]]></description>
			<content:encoded><![CDATA[<p>Bei SimpleTalk.com beschreibt Mario Broodbakker in dem Artikel &quot;<a href="http://www.simple-talk.com/sql/performance/sql-server-wait-events-taking-the-guesswork-out-of-performance-profiling/">SQL Server Wait Events: Taking the Guesswork out of Performance Profiling</a>&quot; wie man am SQL-Server anhand der Warteschlangen Performancebremsen ausmachen kann. Der Artikel ist gut zu lesen. Allerdings st&#246;&#223;t mir etwas s&#228;uerlich auf, dass nicht deutlich genug rauskommt, dass diese Idee Tom Davidson schon vor Jahren beschrieben und eingesetzt hat. </p>
<p>Ich finde den originalen Artikel &quot;<a href="http://www.microsoft.com/technet/prodtechnol/sql/bestpractice/performance_tuning_waits_queues.mspx">SQL Server 2005 Waits and Queues</a>&quot; von Davidson immer noch etwas besser. Am besten ist es vermutlich beide zu lesen, weil es doch eine Menge Holz auf einmal ist. Das Dokument &quot;<a href="http://www.microsoft.com/technet/prodtechnol/sql/2005/tsprfprb.mspx">Troubleshooting Performance Problems in SQL Server 2005</a>&quot; wird hingegen erw&#228;hnt. Auch dort stehen wirklich n&#252;tzliche Dinge drin, aber der in diesem Fall relevante Teil k&#246;nnte gerne l&#228;nger sein. Den Artikel w&#252;rde ich f&#252;r sp&#228;ter empfehlen.</p>
<p>Wenn man den Post von Broodbakker und den Original-Artikel gelesen hat, dann findet man die weiteren Tipps von Tom Davidson zu dem Thema sicher auch n&#252;tzlich: sie stehen im Artikel &quot;<a href="http://blogs.msdn.com/sqlcat/archive/2006/06/23/Tom-Davidson-SQLCAT-Best-Practices.aspx">Performance and Tuning Blue Prints</a>&quot;.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.glorf.it/blog/2007/04/04/sql-talk/performance/sql-server-wait-events/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>
