<?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"
	>

<channel>
	<title>Business Intelligence Blog</title>
	<atom:link href="http://csopro.de/biblog/feed/" rel="self" type="application/rss+xml" />
	<link>http://csopro.de/biblog</link>
	<description>Meine BI-Projekte im Microsoft-Umfeld</description>
	<pubDate>Tue, 28 Jun 2011 08:34:19 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5.1</generator>
	<language>en</language>
			<item>
		<title>Dynamisches Top n in SQL 2000</title>
		<link>http://csopro.de/biblog/2011/06/top-n-in-sql-2000/</link>
		<comments>http://csopro.de/biblog/2011/06/top-n-in-sql-2000/#comments</comments>
		<pubDate>Mon, 27 Jun 2011 16:19:19 +0000</pubDate>
		<dc:creator>biblog</dc:creator>
		
		<category><![CDATA[MS SQL Server]]></category>

		<guid isPermaLink="false">http://csopro.de/biblog/2011/06/top-n-in-sql-2000/</guid>
		<description><![CDATA[Ab SQL Server 2005 kann man TOP n &#252;ber SELECT TOP (@n) … erledigen:
declare @n integer     set @n = 2
select top (@n) a     from      (select 1 as a      union all      select 2 [...]]]></description>
			<content:encoded><![CDATA[<p>Ab SQL Server 2005 kann man TOP n &#252;ber SELECT TOP (@n) … erledigen:</p>
<blockquote><p>declare @n integer     <br />set @n = 2</p>
<p>select top (@n) a     <br />from      <br />(select 1 as a      <br />union all      <br />select 2      <br />union all      <br />select 3      <br />union all      <br />select 4      <br />union all      <br />select 5      <br />union all      <br />select 6      <br />) as t      <br />order by a desc      </p>
</blockquote>
<p>Das liefert als Ergebnis</p>
<p>a   <br />&#8212;&#8212;&#8212;&#8211;    <br />6    <br />5</p>
<p>(2 row(s) affected)   </p>
<p>In fr&#252;heren Versionen geht das leider nicht. Da hilft die Verwendung von set rowcount, das die Anzahl der zur&#252;ckgegebenen Zeilen definiert:</p>
<blockquote><p>declare @n integer      <br />set @n = 2</p>
<p>set rowcount @n</p>
<p>select distinct&#160; a       <br />from       <br />(select 1 as a       <br />union all       <br />select 2       <br />union all       <br />select 3       <br />union all       <br />select 4       <br />union all       <br />select 5       <br />union all       <br />select 6       <br />) as t       <br />order by a desc</p>
</blockquote>
<p>Das liefert das gleiche Ergebnis wie oben.</p>
<p>Allerdings muss man danach wieder </p>
<blockquote><p><font color="#333333">set rowcount 0</font></p>
</blockquote>
<p>absetzen, damit alle nachfolgenden SQL-Statements in dieser Session wieder alle Datens&#228;tze zur&#252;ckliefern. </p>
]]></content:encoded>
			<wfw:commentRss>http://csopro.de/biblog/2011/06/top-n-in-sql-2000/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Performanceprobleme bei SSAS-Dimensionsabfragen aus SSRS</title>
		<link>http://csopro.de/biblog/2011/03/performanceprobleme-bei-ssas-dimensionsabfragen-aus-ssrs/</link>
		<comments>http://csopro.de/biblog/2011/03/performanceprobleme-bei-ssas-dimensionsabfragen-aus-ssrs/#comments</comments>
		<pubDate>Sat, 12 Mar 2011 22:03:58 +0000</pubDate>
		<dc:creator>martin</dc:creator>
		
		<category><![CDATA[MS Integration Services]]></category>

		<category><![CDATA[MS Reporting Services]]></category>

		<category><![CDATA[Projekte]]></category>

		<guid isPermaLink="false">http://csopro.de/biblog/2011/03/performanceprobleme-bei-ssas-dimensionsabfargen-aus-ssrs/</guid>
		<description><![CDATA[Nachdem wir in einem Projekt unseren Test-Datenbestand (12 Mio Fakten) auf den Produktiv-Bestand (73 Mio Fakten) erweitert hatten, zeigten die Berichte (SSRS 2008 R2) massiv schlechte Antwortszeiten in bestimmten Berichten auf unseren Cube (SSAS 2008 R2) - jeweils nach der Cubeaufbereitung. Somit war klar, dass Ursache war, dass einige der im Bericht verwendeten Abfragen nicht [...]]]></description>
			<content:encoded><![CDATA[<p>Nachdem wir in einem Projekt unseren Test-Datenbestand (12 Mio Fakten) auf den Produktiv-Bestand (73 Mio Fakten) erweitert hatten, zeigten die Berichte (SSRS 2008 R2) massiv schlechte Antwortszeiten in bestimmten Berichten auf unseren Cube (SSAS 2008 R2) - jeweils nach der Cubeaufbereitung. Somit war klar, dass Ursache war, dass einige der im Bericht verwendeten Abfragen nicht im Cache waren, da dieser ja durch die inkrementelle Dimensionsaufbereitung und Aufbereitung einiger Cubepartitionen gel&#246;scht wird.</p>
<p>Naheliegende Strategien waren:</p>
<ul>
<li>Aufteilung in mehr Partitionen unter Angabe der Slice-Property</li>
<li>Verbesserte Aggregationen &#252;ber Usage based Aggregation Design</li>
<li>Cache Warming - Strategien</li>
</ul>
<p>All das brachte uns aber nicht wirklich weiter.</p>
<p>Letztendlich stellte sich aber heraus, dass die Ursache gar nicht in den (tlw. komplexen) Abfragen zur Ermittlung der Fakten zu suchen war, sondern in &#8220;Dimensions-Abfragen&#8221;. Damit meine ich folgendes: Unsere Berichte werden (aus einer eigenen Web-Applikation heraus) mit Parametern aufgerufen, die IDs sind (eigentlich die Member Unique Names, also z.B. Datum.Jahr.&amp;[2010]). Dabei haben die Parameter (aus Performancegr&#252;nden) keine Datasets, die alle verf&#252;gbaren Werte enthalten. Nun wollen wir aber in dem Bericht nat&#252;rlich auch den &#252;bergebenen Wert im Klartext anzeigen, also in obigem Beispiel das Jahr 2010. Deswegen mussten also einfache Abfragen her, die aus dem Member Unique Name den Klartext (Member Caption) ermitteln.</p>
<p>In unserem Projekt hatten wir uns darauf geeinigt, wenn m&#246;glich den SSRS-Designer f&#252;r SSAS-Abfragen zu verwenden, um eine gute Wartbarkeit (ohne tiefere MDX-Kenntnisse) zu erreichen.</p>
<p>Damit gibt es zwei einfache Arten, dieses Problem zu l&#246;sen:</p>
<p>Die erste Art, hat eine Spalte - das Jahr - und zus&#228;tzlich den (MultiSelect) Report Parameter als Parameter f&#252;r die Abfrage:</p>
<p><a title="Langsame Abfrage im SSRS-Designer f&#252;r SSAS-Abfragen" href="http://csopro.de/biblog/wp-content/uploads/2011/03/langsamwizard.jpg" target="_blank"><img alt="Wizard f&#252;r die langsame Abfrage" src="http://csopro.de/biblog/wp-content/uploads/2011/03/langsamwizard-small.jpg" width="450" height="182" /></a><br />
(Bild zum Verg&#246;&#223;ern anklicken!)</p>
<p>Als MDX ergibt sich:</p>
<blockquote>
<p>SELECT { } ON COLUMNS, { ([Datum].[Jahr].[Jahr].ALLMEMBERS ) } DIMENSION PROPERTIES MEMBER_CAPTION, MEMBER_UNIQUE_NAME ON ROWS FROM ( SELECT ( STRTOSET(@DatumJahr, CONSTRAINED) ) ON COLUMNS FROM [Verkaeufe]) CELL PROPERTIES VALUE</p>
</blockquote>
<p>Man kann auch die Abfrage etwas erweitern:</p>
<p>Man f&#252;gt ein berechnets Measure (in diesem Fall namens &#8220;UniqueName&#8221;) mit dem MDX</p>
<blockquote>
<p>[Datum].[Jahr].CurrentMember.UniqueName</p>
</blockquote>
<p>hinzu:</p>
<p><a title="Schnelle Abfrage im SSRS-Designer f&#252;r SSAS-Abfragen" href="http://csopro.de/biblog/wp-content/uploads/2011/03/schnellwizard.jpg" target="_blank"><img alt="Wizard der schnellen Abfrage" src="http://csopro.de/biblog/wp-content/uploads/2011/03/schnellwizard-small.jpg" width="450" height="236" /></a><br />
(Bild zum Verg&#246;&#223;ern anklicken!)</p>
<p>Damit ergibt sich folgendes MDX:</p>
<blockquote>
<p>WITH MEMBER [Measures].[UniqueName] AS [Datum].[Jahr].CurrentMember.UniqueName SELECT NON EMPTY { [Measures].[UniqueName] } ON COLUMNS, NON EMPTY { ([Datum].[Jahr].[Jahr].ALLMEMBERS ) } DIMENSION PROPERTIES MEMBER_CAPTION, MEMBER_UNIQUE_NAME ON ROWS FROM ( SELECT ( STRTOSET(@DatumJahr, CONSTRAINED) ) ON COLUMNS FROM [Verkaeufe]) CELL PROPERTIES VALUE, BACK_COLOR, FORE_COLOR, FORMATTED_VALUE, FORMAT_STRING, FONT_NAME, FONT_SIZE, FONT_FLAGS</p>
</blockquote>
<p>Anmerkung: Das dabei enstehende versteckte Dataset zum Bef&#252;llen der &#8220;verf&#252;gbaren Werte&#8221; des Parameters l&#246;schen wir, da wir ja keine &#8220;verf&#252;gbaren Werte&#8221; anzeigen wollen (unsere Dimension hatte zu viele Eintr&#228;ge).</p>
<p>Beide Abfragen scheinen dasselbe zu tun und mit der selben Performance. Dies ist aber ein Trugschluss!</p>
<p>Leeren wir zun&#228;chst (und vor Ausf&#252;hrung eines neuen Test-Statements) den SSAS-Cache mit</p>
<blockquote>
<p>&lt;ClearCache xmlns=&#8221;http://schemas.microsoft.com/analysisservices/2003/engine&#8221;&gt;<br />
&lt;Object&gt;<br />
&lt;DatabaseID&gt;SimpleCube&lt;/DatabaseID&gt;<br />
&lt;CubeID&gt;Verkaeufe&lt;/CubeID&gt;<br />
&lt;/Object&gt;<br />
&lt;/ClearCache&gt;</p>
</blockquote>
<p>(siehe dazu auch: <a href="http://www.ssas-info.com/analysis-services-faq/27-mdx/133-mdx-how-do-i-clear-analysis-services-ssas-database-cache" target="_blank">http://www.ssas-info.com/analysis-services-faq/27-mdx/133-mdx-how-do-i-clear-analysis-services-ssas-database-cache</a>)</p>
<p>Dann betrachten wir die Darstellung im SQL Server Profiler (mit den Standard-Einstellungen f&#252;r den SSAS):</p>
<p>Die erste Abfrage ergibt folgendes Bild:</p>
<p><a title="Ergebnis im SQL Server Profiler f&#252;r die langsame Abfrage" href="http://csopro.de/biblog/wp-content/uploads/2011/03/sqlprofilerlangsam.jpg" target="_blank"><img alt="SQL Profiler Ergebnis der langsamen Abfrage" src="http://csopro.de/biblog/wp-content/uploads/2011/03/sqlprofilerlangsam-small.jpg" width="450" height="182" /></a><br />
(Bild zum Verg&#246;&#223;ern anklicken!)</p>
<p>Schauen wir uns die einzelnen Zeilen an:</p>
<ul>
<li>Session Initialize - spricht f&#252;r sich</li>
<li>Query Begin: Hier f&#228;ngt die Abfrage an - im unteren Bereich sieht man das MDX und die Parameterwerte f&#252;r die @Parameter im MDX</li>
<li>Progress Report Begin: Daten aus einer Partition werden gelesen (!), d.h. von der Festplatte in den Speicher &#252;bernommen.</li>
<li>Progress Report End: das dazugeh&#246;rige Ende</li>
<li>Query SubCube: die gelesenen Daten werden verwendet, um die Abfrage zu beantworten (in diesem Fall Non-Cache)</li>
<li>Query End: Das Ende der Abfrage</li>
</ul>
<p>Die zweite Abfrage ergibt ein anderes Bild:</p>
<p><a title="Ergebnis im SQL Server Profiler f&#252;r die schnelle Abfrage" href="http://csopro.de/biblog/wp-content/uploads/2011/03/sqlprofilerschnell.jpg" target="_blank"><img alt="SQL Profiler Ergebnis der schnellen Abfrage" src="http://csopro.de/biblog/wp-content/uploads/2011/03/sqlprofilerschnell-small.jpg" width="450" height="59" /></a><br />
(Bild zum Verg&#246;&#223;ern anklicken!)</p>
<p>Hier sehen wir, dass kein Zugriff auf eine Partition erfolgt!</p>
<p>Der Zugriff auf die Partition ist nat&#252;rlich sch&#228;dlich, da er bei gro&#223;en Datenmengen lang dauern kann, zumal er gar nicht ben&#246;tigt wird, da wir ja nur Dimensions-Element-Bezeichnungen abfragen wollen.<br />
Es ist nicht erkl&#228;rbar, warum SSAS hier dennoch auf die Fakten zugreift.<br />
Deswegen ist die zweite Abfrage auf jeden Fall vorzuziehen!</p>
<p>Noch ein interessante Anmerkung zum Schluss: Wenn man die erste Abfrage im SQL Server Management Studio ausf&#252;hrt, ist sie auch nicht langsam. Da im Management Studio keine parametrisierten Abfragen m&#246;glich sind, muss man dazu die Parameter durch die entsprechenden Strings ersetzen, und erh&#228;lt somit folgendes MDX:</p>
<blockquote>
<p>SELECT { } ON COLUMNS, { ([Datum].[Jahr].[Jahr].ALLMEMBERS ) } DIMENSION PROPERTIES MEMBER_CAPTION, MEMBER_UNIQUE_NAME ON ROWS FROM ( SELECT ( STRTOSET(&#8221;{ [Datum].[Jahr].&amp;[2001],[Datum].[Jahr].&amp;[2008] }&#8221;, CONSTRAINED) ) ON COLUMNS FROM [Verkaeufe]) CELL PROPERTIES VALUE</p>
</blockquote>
<p>F&#252;hrt man dieses MDX aus (nachdem man den Cache geleert hat), so erh&#228;lt man folgendes Bild:</p>
<p><a title="Ergebnis im SQL Server Profiler f&#252;r die langsame Abfrage, ausgef&#252;hrt im Management Studio" href="http://csopro.de/biblog/wp-content/uploads/2011/03/profilerlangsamssms.jpg" target="_blank"><img alt="Ergebnis des Profilers der langsamen Abfrage ausgef&#252;hrt im Management Studio" src="http://csopro.de/biblog/wp-content/uploads/2011/03/profilerlangsamssms-small.jpg" width="450" height="170" /></a><br />
(Bild zum Verg&#246;&#223;ern anklicken!)</p>
<p>Man sieht hier also auch nur ein Query Begin und ein Query End, also auch keinen Zugriff auf die Cube-(bzw. Partitions-)Daten.</p>
<p>Dies hatte uns die Fehlersuche erschwert, da wir nat&#252;rlich zun&#228;chst auf der Suche nach dem verantwortlichen Statement alle Statements im SQL Server Profiler mitgeschnitten und dann einzeln im Management Studio ausgef&#252;hrt hatten, was - wie eben gesehen - das problematische Statement leider nicht offenbart.</p>
]]></content:encoded>
			<wfw:commentRss>http://csopro.de/biblog/2011/03/performanceprobleme-bei-ssas-dimensionsabfragen-aus-ssrs/feed/</wfw:commentRss>
		</item>
		<item>
		<title>S&#228;ulendiagramme mit Datum auf der x-Achse</title>
		<link>http://csopro.de/biblog/2011/02/saeulendiagramme-mit-datum-auf-der-x-achse/</link>
		<comments>http://csopro.de/biblog/2011/02/saeulendiagramme-mit-datum-auf-der-x-achse/#comments</comments>
		<pubDate>Wed, 23 Feb 2011 08:43:39 +0000</pubDate>
		<dc:creator>martin</dc:creator>
		
		<category><![CDATA[MS Reporting Services]]></category>

		<guid isPermaLink="false">http://csopro.de/biblog/2011/02/saeulendiagramme-mit-datum-auf-der-x-achse/</guid>
		<description><![CDATA[Bekannterma&#223;en kann man in Reporting Services alle Standard-Charts darstellen.
Bei den S&#228;ulen-Diagrammen hat man sogar - was ich heute zeigen m&#246;chte - die M&#246;glichkeit, auf der x-Achse Datumswerte anzuzeigen. Dies ist z.B. in (Pseudo-) Gantt-Charts interessant.
F&#252;r unser Beispiel wollen wir die Laufzeit von zwei Phasen eines Projekts darstellen:
Phase 1 l&#228;uft vom 1.3.2011 bis zum 20.3.2011, wobei [...]]]></description>
			<content:encoded><![CDATA[<p>Bekannterma&#223;en kann man in Reporting Services alle Standard-Charts darstellen.</p>
<p>Bei den S&#228;ulen-Diagrammen hat man sogar - was ich heute zeigen m&#246;chte - die M&#246;glichkeit, auf der x-Achse Datumswerte anzuzeigen. Dies ist z.B. in (Pseudo-) Gantt-Charts interessant.</p>
<p>F&#252;r unser Beispiel wollen wir die Laufzeit von zwei Phasen eines Projekts darstellen:</p>
<p>Phase 1 l&#228;uft vom 1.3.2011 bis zum 20.3.2011, wobei die Zeit ab dem 17.3.2011 rot dargestellt werden soll.<br />
Phase 2 l&#228;uft vom 15.3.2011 bis zum 7.4.2011, wobei die Zeit ab dem 1.4.2011 rot dargestellt werden soll.</p>
<p>Das Ziel sieht somit so aus:</p>
<p><a href="http://csopro.de/biblog/wp-content/uploads/2011/02/ziel-pseudo-gantt.jpg"><img alt="Ziel der 2 Phasen mit Datum auf x-Achse" src="http://csopro.de/biblog/wp-content/uploads/2011/02/ziel-pseudo-gantt-small.jpg" width="450" height="187" /></a><br />
(Zum Vergr&#246;&#223;ern anklicken)</p>
<p>Um dies zu erreichen, geben wir folgendes im Chart an:</p>
<ul>
<li>als Kategorie: die Phase</li>
<li>als Reihe: die Farbe</li>
<li>als Wert: das Datum</li>
</ul>
<p>Allerdings ist das Datum nicht einfach einzugeben. Wir m&#252;ssen vielmehr folgendes einstellen (examplarisch an der Phase 1 gezeigt):</p>
<ul>
<li>ein transparenter Balken bis zum 1.3.2011</li>
<li>ein gr&#252;ner Balken bis zum 17.3.2011, also 16 Tage breit</li>
<li>ein roter Balken bis zum 20.3.2011, also 3 Tage breit</li>
</ul>
<p>F&#252;r den ersten Balken k&#246;nnen wir als Wert einfach den 1.3.2011 angeben. F&#252;r die darauffolgenden Balken m&#252;ssen wir aber das Datum als Wert angeben, das die Anzahl der Tage auf den 30.12.1899 addiert, also</p>
<ul>
<li>f&#252;r einen Balken mit 1 Tag L&#228;nge: 31.12.1899</li>
<li>f&#252;r einen Balken mit 2 Tagen L&#228;nge: 1.1.1900</li>
<li>f&#252;r einen Balken mit 2,5 Tagen L&#228;nge: 1.1.1900 12:00</li>
<li>f&#252;r einen Balken mit 3 Tagen L&#228;nge: 2.1.1900</li>
<li>f&#252;r einen Balken mit 16 Tagen L&#228;nge: 15.1.1900</li>
<li>usw.</li>
</ul>
<p>Nat&#252;rlich m&#252;ssen wir darauf achten, dass die Reihenfolge der Balken stimmt. Deswegen muss die Reihe entsprechend sortiert sein.</p>
<p>Au&#223;erdem werden wir die x-Achse so formatieren, dass sie die Datumswerte sch&#246;n anzeigt.</p>
<p>Nun haben wir alles beisammen, um das Projekt zu schaffen:</p>
<p>Als SQL w&#228;hlen wir:</p>
<blockquote>
<p>SELECT &#8216;Phase 1&#8242; as Phase, &#8216;transparent&#8217; as Farbe, 1 as sort, convert(Datetime, &#8216;1.3.2011&#8242;, 104) as wert<br />
UNION ALL<br />
SELECT &#8216;Phase 1&#8242; as Phase, &#8216;green&#8217; as Farbe, 2 as sort, convert(Datetime, &#8216;15.1.1900&#8242;, 104) as wert<br />
UNION ALL<br />
SELECT &#8216;Phase 1&#8242; as Phase, &#8216;red&#8217; as Farbe, 3 as sort, convert(Datetime, &#8216;2.1.1900&#8242;, 104) as wert<br />
UNION ALL<br />
SELECT &#8216;Phase 2&#8242; as Phase, &#8216;transparent&#8217; as Farbe, 1 as sort, convert(Datetime, &#8216;15.3.2011&#8242;, 104) as wert<br />
UNION ALL<br />
SELECT &#8216;Phase 2&#8242; as Phase, &#8216;green&#8217; as Farbe, 2 as sort, convert(Datetime, &#8216;16.1.1900&#8242;, 104) as wert<br />
UNION ALL<br />
SELECT &#8216;Phase 2&#8242; as Phase, &#8216;red&#8217; as Farbe, 3 as sort, convert(Datetime, &#8216;5.1.1900&#8242;, 104) as wert</p>
</blockquote>
<p>Und im Chart muss folgendes eingestellt werden:</p>
<p>Es ist ein Stacked Bar Chart:</p>
<p><img alt="Stacked Bar Chart" src="http://csopro.de/biblog/wp-content/uploads/2011/02/stackedbar-1.jpg" width="363" height="218" /></p>
<p>Die Grundeinstellungen sieht man hier:</p>
<p><img alt="Einstellungen des Charts" src="http://csopro.de/biblog/wp-content/uploads/2011/02/chartsettings.jpg" width="435" height="314" /></p>
<p>Die genauen Eigenschaften der Kategorie (Phase) sind:</p>
<p><a href="http://csopro.de/biblog/wp-content/uploads/2011/02/category-general.jpg"><img alt="General Eigenschaften der Category" src="http://csopro.de/biblog/wp-content/uploads/2011/02/category-general-small.jpg" width="450" height="274" /></a><br />
(Zum Vergr&#246;&#223;ern anklicken)</p>
<p>und</p>
<p><a href="http://csopro.de/biblog/wp-content/uploads/2011/02/category-sorting.jpg"><img alt="Sorting Category" src="http://csopro.de/biblog/wp-content/uploads/2011/02/category-sorting-small.jpg" width="450" height="179" /></a><br />
(Zum Vergr&#246;&#223;ern anklicken)</p>
<p>Die genauen Eigenschaften der Reihe (Farbe) sind:</p>
<p><a href="http://csopro.de/biblog/wp-content/uploads/2011/02/series-general.jpg"><img alt="Generelle Eigenschaften der Reihe" src="http://csopro.de/biblog/wp-content/uploads/2011/02/series-general-small.jpg" width="450" height="299" /></a><br />
(Zum Vergr&#246;&#223;ern anklicken)</p>
<p>und</p>
<p><a href="http://csopro.de/biblog/wp-content/uploads/2011/02/series-sorting.jpg"><img alt="Sortier-Eigenschaften der Reihe" src="http://csopro.de/biblog/wp-content/uploads/2011/02/series-sorting-small.jpg" width="450" height="196" /></a><br />
(Zum Vergr&#246;&#223;ern anklicken)</p>
<p>Die genauen Eigenschaften der Werte sind:</p>
<p><img alt="Einstellungen der Werte 1" src="http://csopro.de/biblog/wp-content/uploads/2011/02/series-seriesdata.jpg" width="334" height="229" /><br />
(Hier darauf achten, dass nicht count(Wert) dort steht (wie es SSRS beim Anklicken erstellt), sondern nur wert!</p>
<p>und</p>
<p><img alt="F&#252;lleigenschaften der Werte" src="http://csopro.de/biblog/wp-content/uploads/2011/02/series-fill.jpg" width="356" height="309" /><br />
(Hier darauf achten, dass unter Farbe der Wert &#8220;=Fields!Farbe.Value&#8221; steht, zu erreichen &#252;ber das fx-Symbol)</p>
<p>Dann muss nur noch die x-Achse korrekt eingestellt werden:</p>
<p><a href="http://csopro.de/biblog/wp-content/uploads/2011/02/axisoptions.jpg"><img alt="Optionen der Achse" src="http://csopro.de/biblog/wp-content/uploads/2011/02/axisoptions-small.jpg" width="450" height="428" /></a><br />
(Zum Vergr&#246;&#223;ern anklicken)</p>
<p>Dabei sind in diesem Fall als Minimum &#8220;=DateSerial(2011, 2, 27)&#8221; und als Maximum &#8220;=DateSerial(2011,4,10)&#8221; eingestellt. Nat&#252;rlich sollte man das &#8220;in Wirklichkeit&#8221; aus den Daten &#252;ber min / max und ggf. dateadd von Tagen berechnen.</p>
<p>Als Datumsformat ist das deutsche Format eingestellt:</p>
<p><img alt="Datumsformat der x-Achse" src="http://csopro.de/biblog/wp-content/uploads/2011/02/axis-number.jpg" width="441" height="241" /></p>
<p>Um die kleinen Zacken pro Tag (ohne Datumsanzeige) zu haben (wie man sie oben in dem Ziel-Chart sehen kann), habe ich noch die minor tick marks auf 1 gesetzt:</p>
<p><a href="http://csopro.de/biblog/wp-content/uploads/2011/02/axis-minorticks.jpg"><img alt="Minor Tick Marks der x-Achse" src="http://csopro.de/biblog/wp-content/uploads/2011/02/axis-minorticks-small.jpg" width="450" height="428" /></a><br />
(Zum Vergr&#246;&#223;ern anklicken)</p>
<p>Die vollst&#228;ndige rdl-Datei kann <a title="Reporting Services File mit diesem Beispiel zu Pseudo Gantt-Diagrammen" href="http://www.csopro.de/download/PseudoGantt.rdl" target="_blank">hier</a> heruntergeladen werden.</p>
<p>Dieses Projekt l&#228;sst sich zu einem Pseudo-Gantt-Chart ausbauen, in dem dann (nur) die senkrechten Linie und Pfeile fehlen, was f&#252;r eine erste Orientierung allerdings ausreichend sein d&#252;rfte.</p>
<p>Nat&#252;rlich lassen sich auch andere Visualisierungen damit realisieren, z.B. Darstellung von Up-/Down-Time von Maschinen etc.</p>
]]></content:encoded>
			<wfw:commentRss>http://csopro.de/biblog/2011/02/saeulendiagramme-mit-datum-auf-der-x-achse/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Analysis Services Formatierungen (inkl. Excel-Bug bei $)</title>
		<link>http://csopro.de/biblog/2010/10/analysis-services-formatierungen-inkl-excel-bug-bei/</link>
		<comments>http://csopro.de/biblog/2010/10/analysis-services-formatierungen-inkl-excel-bug-bei/#comments</comments>
		<pubDate>Wed, 06 Oct 2010 07:40:41 +0000</pubDate>
		<dc:creator>martin</dc:creator>
		
		<category><![CDATA[MS Analysis Services]]></category>

		<guid isPermaLink="false">http://csopro.de/biblog/2010/10/analysis-services-formatierungen-inkl-excel-bug-bei/</guid>
		<description><![CDATA[In Analysis Services k&#246;nnen &#8220;echte&#8221; Measures und berechnete Measures formatiert werden (sprich mit Tausender-Trenner, Nachkommastellen und sonstigen Bezeichnern wie cm oder € versch&#246;nert werden). Das erh&#246;ht die Lesbarkeit und verhindert Verwechslungen (&#8221;Ist die Dauer in Stunden oder Minuten?&#8221;)
Als Formatstrings k&#246;nnen die g&#228;ngen Bezeichnungen verwendet werden:

, bedeutet einen Tausendertrenner
. bedeutet den Dezimalpunkt
0 bedeutet, dass diese Stelle [...]]]></description>
			<content:encoded><![CDATA[<p>In Analysis Services k&#246;nnen &#8220;echte&#8221; Measures und berechnete Measures formatiert werden (sprich mit Tausender-Trenner, Nachkommastellen und sonstigen Bezeichnern wie cm oder € versch&#246;nert werden). Das erh&#246;ht die Lesbarkeit und verhindert Verwechslungen (&#8221;Ist die Dauer in Stunden oder Minuten?&#8221;)</p>
<p>Als Formatstrings k&#246;nnen die g&#228;ngen Bezeichnungen verwendet werden:</p>
<ul>
<li>, bedeutet einen Tausendertrenner</li>
<li>. bedeutet den Dezimalpunkt</li>
<li>0 bedeutet, dass diese Stelle immer belegt ist</li>
<li># bedeutet, dass diese Stelle angezeigt wird, falls n&#246;tig</li>
<li>In Anf&#252;hrungszeichen (&#8221;) k&#246;nnen beliebige Texte eingegeben werden</li>
</ul>
<p>So ist #,##0.00 &#8220;€&#8221; meine Standardformatierung in €, also zum Beispiel 17,33 € oder 1.522,12 € (auf deutschen Clients).</p>
<p>Interessanter Weise schl&#228;gt das BIDS auf deutschen Clients #.##0,00 vor. Das ist aber falsch und wird leider nicht zum Ziel f&#252;hren.</p>
<p><img alt="Measure" src="http://csopro.de/biblog/wp-content/uploads/2010/10/measure.jpg" width="228" height="260" /></p>
<p>Bei berechneten Measures gilt an sich das gleiche. Allerdings m&#252;ssen die Strings mit &#8221; umschlossen werden. Falls in dem Formatstring selbst ein Anf&#252;hrungszeichen enthalten ist, muss es verdoppelt werden (wie in der alten VB-Syntax).</p>
<p>Obiger Formatstring ist dann &#8220;#,##0.00 &#8220;&#8221;€&#8221;"&#8221;</p>
<p><img alt="berechnetesMeasure" src="http://csopro.de/biblog/wp-content/uploads/2010/10/berechnetesmeasure.jpg" width="395" height="297" /></p>
<p><img alt="berechnetesMeasure2" src="http://csopro.de/biblog/wp-content/uploads/2010/10/berechnetesmeasure2.jpg" width="502" height="99" /></p>
<p>Verwendet man Excel als Client, ist ein Bug in Excel zu beachten:</p>
<p>Ich gehe davon aus, dass das Excel ein deutsches Excel ist. Obiger Formatstring funktioniert dann wunderbar. Auch der Formatstring &#8220;#,##0.00 &#8220;&#8221;£&#8221;"&#8221; funktioniert wunderbar. Allerdings funktioniert &#8220;#,##0.00 &#8220;&#8221;$&#8221;"&#8221; nicht, statt dessen zeigt Excel € an. Ich erkl&#228;re mir das so: Diese Formatierung entspricht der Formatierung &#8220;W&#228;hrung&#8221;, die auf deutschen Rechnern eben als € umgesetzt wird. Hier hilft vor oder hinter das W&#228;hrungssymbol $ ein Leerzeichen einzuf&#252;gen, also z.B. &#8220;#,##0.00&#8243;&#8221; $&#8221;"&#8221;</p>
]]></content:encoded>
			<wfw:commentRss>http://csopro.de/biblog/2010/10/analysis-services-formatierungen-inkl-excel-bug-bei/feed/</wfw:commentRss>
		</item>
		<item>
		<title>begin try in SQL-Batch-Statements</title>
		<link>http://csopro.de/biblog/2010/06/begin-try-in-sql-batch-statements/</link>
		<comments>http://csopro.de/biblog/2010/06/begin-try-in-sql-batch-statements/#comments</comments>
		<pubDate>Tue, 22 Jun 2010 19:51:00 +0000</pubDate>
		<dc:creator>martin</dc:creator>
		
		<category><![CDATA[MS Integration Services]]></category>

		<category><![CDATA[MS SQL Server]]></category>

		<guid isPermaLink="false">http://csopro.de/biblog/2010/06/begin-try-in-sql-batch-statements/</guid>
		<description><![CDATA[Es kommt &#246;fter vor, dass man im Batch mehrere SQL-Statements ausf&#252;hren m&#246;chte, sei es in einer Stored Procedure oder im Execute SQL-Task von SSIS.
Meistens hat man folgende Anforderung:
L&#228;uft ein Statement auf einen Fehler, soll ein Rollback der Statements gemacht werden. Au&#223;erdem soll nat&#252;rlich dem aufrufenden System der Fehler gemeldet werden.
L&#228;sst man einen Batch einfach so [...]]]></description>
			<content:encoded><![CDATA[<p>Es kommt &#246;fter vor, dass man im Batch mehrere SQL-Statements ausf&#252;hren m&#246;chte, sei es in einer Stored Procedure oder im Execute SQL-Task von SSIS.</p>
<p>Meistens hat man folgende Anforderung:</p>
<p>L&#228;uft ein Statement auf einen Fehler, soll ein Rollback der Statements gemacht werden. Au&#223;erdem soll nat&#252;rlich dem aufrufenden System der Fehler gemeldet werden.</p>
<p>L&#228;sst man einen Batch einfach so laufen, wird dieses Ziel nicht erreicht, da im Fehlerfall auch die Statements nach dem Statement, das den Fehler verursacht, ausgef&#252;hrt werden.</p>
<p>Beispiel:</p>
<blockquote>
<p>set nocount on<br />
select 1<br />
select 1/0<br />
select 2</p>
</blockquote>
<p>liefert:</p>
<blockquote>
<p>
&#8212;&#8212;&#8212;&#8211;<br />
1</p>
<p>
&#8212;&#8212;&#8212;&#8211;<br />
Msg 8134, Level 16, State 1, Line 3<br />
Divide by zero error encountered.</p>
<p>
&#8212;&#8212;&#8212;&#8211;<br />
2</p>
</blockquote>
<p>In Versionen vor SQL Server 2005 musste man die Error-Variable auslesen, etwa so:</p>
<blockquote>
<p>set nocount on<br />
declare @fehler as int<br />
set @fehler = 0<br />
select 1<br />
set @fehler = @fehler + @@error<br />
select 1/0<br />
set @fehler = @fehler + @@error<br />
select 2<br />
set @fehler = @fehler + @@error<br />
if @fehler&gt;0 begin<br />
 print &#8216;Ein Fehler ist aufgetreten&#8217;<br />
end</p>
</blockquote>
<p>was folgendes Ergebnis liefert:</p>
<blockquote>
<p>
&#8212;&#8212;&#8212;&#8211;<br />
1</p>
<p>
&#8212;&#8212;&#8212;&#8211;<br />
Msg 8134, Level 16, State 1, Line 6<br />
Divide by zero error encountered.</p>
<p>
&#8212;&#8212;&#8212;&#8211;<br />
2</p>
<p>Ein Fehler ist aufgetreten</p>
</blockquote>
<p>Das Problem ist, man muss die Zeile &#8220;set @fehler = @fehler + @@error&#8221; nach jedem Statement schreiben, da sie nach jedem (!) Statement zur&#252;ckgesetzt wird.</p>
<p>Leichter geht das in SQL 2005 mit begin try &#8230; end try - angelehnt an Konstrukte aus Programmiersprachen wie C#:</p>
<blockquote>
<p>set nocount on<br />
begin try</p>
<p>select 1<br />
select 1/0<br />
select 2</p>
<p>end try<br />
begin catch<br />
 print &#8216;Ein Fehler aufgetreten&#8217;<br />
end catch</p>
</blockquote>
<p>Am Ergebnis</p>
<blockquote>
<p>
&#8212;&#8212;&#8212;&#8211;<br />
1</p>
<p>
&#8212;&#8212;&#8212;&#8211;</p>
<p>Ein Fehler aufgetreten</p>
</blockquote>
<p>sieht man, dass nach dem fehlerhaften Statement die Bearbeitung beendet wird.</p>
<p>Nun fehlen nur noch 2 Anforderungen:</p>
<p>Dass keine Datenmanipulation statt findet, erreicht man &#252;ber eine Transaktion, die im catch-Block zur&#252;ckgerollt (rollback) wird.</p>
<p>Dass der Aufruf dennoch den Fehler mitbekommt, erreicht man &#252;ber einen raiserror.</p>
<p>Das fertige Skript sieht dann so aus:</p>
<blockquote>
<p>set nocount on</p>
<p>begin tran<br />
begin try</p>
<p>/* hier die eigentlichen SQL-Statements schreiben */<br />
select 1<br />
select 1/0<br />
select 2</p>
<p>end try<br />
begin catch<br />
if @@trancount &gt; 0 begin<br />
        rollback tran<br />
end<br />
declare @fehler_text nvarchar(4000)<br />
set @fehler_text = ERROR_MESSAGE()<br />
declare @fehler_severity int<br />
set @fehler_severity = ERROR_SEVERITY()<br />
declare @fehler_state int<br />
set @fehler_state = ERROR_STATE()<br />
RAISERROR (@fehler_text, &#8212; Message text.<br />
        @fehler_severity, &#8212; Severity.<br />
        @fehler_state &#8212; State.<br />
)</p>
<p>end catch<br />
if @@trancount &gt; 0 begin<br />
        commit tran<br />
end</p>
</blockquote>
]]></content:encoded>
			<wfw:commentRss>http://csopro.de/biblog/2010/06/begin-try-in-sql-batch-statements/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Measure-Meta-Informationen des Cube auslesen: AMO oder ADOMD</title>
		<link>http://csopro.de/biblog/2010/06/measure-meta-informationen-des-cube-auslesen-amo-oder-adomd/</link>
		<comments>http://csopro.de/biblog/2010/06/measure-meta-informationen-des-cube-auslesen-amo-oder-adomd/#comments</comments>
		<pubDate>Tue, 22 Jun 2010 19:28:50 +0000</pubDate>
		<dc:creator>martin</dc:creator>
		
		<category><![CDATA[C#]]></category>

		<category><![CDATA[MS Analysis Services]]></category>

		<guid isPermaLink="false">http://csopro.de/biblog/2010/06/measure-meta-informationen-des-cube-auslesen-amo-oder-adomd/</guid>
		<description><![CDATA[Um an die Meta-Informationen des Cubes heranzukommen, gibt es mehrere Zugriffsm&#246;glichkeiten, einmal mit AMO (Analysis Services Management Objects) oder ADOMD (das haupts&#228;chlich f&#252;r die Ausf&#252;hrung von MDX-Abfragen verwendet wird).
Beigef&#252;gt habe ich eine C#-Sollution, die &#252;ber ADOMD eine CSV-Datei mit allen Measures erzeugt.
Wenn Zeit ist, werde ich diesen Artikel sp&#228;ter noch erkl&#228;ren. Vorerst nur soviel:
Der Versuch [...]]]></description>
			<content:encoded><![CDATA[<p>Um an die Meta-Informationen des Cubes heranzukommen, gibt es mehrere Zugriffsm&#246;glichkeiten, einmal mit AMO (Analysis Services Management Objects) oder ADOMD (das haupts&#228;chlich f&#252;r die Ausf&#252;hrung von MDX-Abfragen verwendet wird).</p>
<p>Beigef&#252;gt habe ich eine <a href="http://csopro.de/blogdateien/MeasuresDokumentation.zip">C#-Sollution</a>, die &#252;ber ADOMD eine CSV-Datei mit allen Measures erzeugt.</p>
<p>Wenn Zeit ist, werde ich diesen Artikel sp&#228;ter noch erkl&#228;ren. Vorerst nur soviel:</p>
<p>Der Versuch mit ADOMD auf die Daten zuzugreifen, ist gescheitert, weil dort die berechneten Measures, die ich ebenfalls dokumentieren wollte, nicht einzeln abfragbar sind. Ich habe in einem <a href="http://csopro.de/biblog/2009/10/mdx-skripte-eines-cubes-ueber-c-code-anpassen/" target="_blank">Blogeintrag von letztem Oktober </a>bereits beschrieben, wie man mit AMO an die MDX-Skripte herankommt. In diesen Skripten gibt es auch CREATE MEMBER-Skripte, die dann durch den Cube in berechnete Measures umgesetzt werden. Wenn man also AMO verwenden will, m&#252;sste man diese Skripte parsen (hierzu ein interessanter Blog-Eintrag auf <a title="SSAS: Parsing Calculated Measures from MDX Script" href="http://geekswithblogs.net/darrengosbell/archive/2006/08/10/87561.aspx" target="_blank">geekswithblogs.net</a>). Wenn man nur an dem Inhalt interessiert ist und die berechneten Measures nicht &#228;ndern will, kann man aber darauf getrost verzichten und - wie ich auch in der beigef&#252;gten Sollution gemacht habe - stattdessen ADOMD verwenden.</p>
]]></content:encoded>
			<wfw:commentRss>http://csopro.de/biblog/2010/06/measure-meta-informationen-des-cube-auslesen-amo-oder-adomd/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Berechnete Elemente in MDX-Abfragen mit Anwendung in Reporting Services 2005</title>
		<link>http://csopro.de/biblog/2010/06/berechnete-elemente-in-mdx-abfragen-mit-anwendung-in-reporting-services-2005/</link>
		<comments>http://csopro.de/biblog/2010/06/berechnete-elemente-in-mdx-abfragen-mit-anwendung-in-reporting-services-2005/#comments</comments>
		<pubDate>Sun, 20 Jun 2010 18:14:05 +0000</pubDate>
		<dc:creator>martin</dc:creator>
		
		<category><![CDATA[MS Analysis Services]]></category>

		<category><![CDATA[MS Reporting Services]]></category>

		<guid isPermaLink="false">http://csopro.de/biblog/2010/06/berechnete-elemente-in-mdx-abfragen-mit-anwendung-in-reporting-services-2005/</guid>
		<description><![CDATA[Lange habe ich gesucht, um ein sinnvolles Beispiel f&#252;r ein berechnetes Dimensions-Element zu finden (berechnete Measures sind ja st&#228;ndig zu finden).
Ein Beispiel ist eine Matrix in Reporting Services 2005 (in 2008 ist mit der Tablix ja alles (bzw. vieles) besser).
Ich habe &#252;ber das Problem bereits zwei Artikel auf sqlservercentral.com ver&#246;ffentlicht (Reporting Services: Adding extra columns [...]]]></description>
			<content:encoded><![CDATA[<p>Lange habe ich gesucht, um ein sinnvolles Beispiel f&#252;r ein berechnetes Dimensions-Element zu finden (berechnete Measures sind ja st&#228;ndig zu finden).</p>
<p>Ein Beispiel ist eine Matrix in Reporting Services 2005 (in 2008 ist mit der Tablix ja alles (bzw. vieles) besser).</p>
<p>Ich habe &#252;ber das Problem bereits zwei Artikel auf sqlservercentral.com ver&#246;ffentlicht (<a href="http://www.sqlservercentral.com/articles/Reporting+Services+%28SSRS%29/63415/">Reporting Services: Adding extra columns / rows to a matrix</a> und <a href="http://www.sqlservercentral.com/articles/Linked+Server/63867/">Reporting Services: Read Data from SSAS and SQL Server in One Dataset</a>). Heute m&#246;chte ich das Problem auf eine andere Weise l&#246;sen.</p>
<p>Nehmen wir an, wir wollen in den Spalten alle Monate sehen und die Summe und einen Planwert und die Differenz zwischen Summe und Planwert. Wenn wir es jetzt schaffen, das ganze in einem MDX-Statement zu laden, so kann auch die Matrix von SQL Server Reporting Services 2005 das anzeigen.</p>
<p>Alle Monate in MDX anzuzeigen, ist einfach. Das Beispiel zeigt je Produkt und Monat den Verkaufswert an (ein konstruiertes, vereinfachtes Beispiel - ich habe sogar auf die Monatsnamen verzichtet und zeige die Monate im Format JJJJMM an):</p>
<blockquote>
<p>select non empty [Datum].[Monat].[Monat].members on columns,<br />
non empty [Produkt].[Produkt].[Produkt].members on rows<br />
from [Verkaeufe]<br />
where ([Measures].[Verkaufswert], [Datum].[Jahr].&amp;[2010])</p>
<p>liefert:</p>
<p><img alt="Monate mal Produkte" src="http://csopro.de/biblog/wp-content/uploads/2010/06/monatexprodukte.jpg" width="183" height="82" /></p>
</blockquote>
<p>Die Spalte Summe geht auch einfach. Dazu m&#252;ssen wir den Alle-Member der Attribut-Hierarchie Monat anzeigen:</p>
<blockquote>
<p>select non empty {[Datum].[Monat].[Monat].members, [Datum].[Monat].[All]} on columns,<br />
non empty [Produkt].[Produkt].[Produkt].members on rows<br />
from [Verkaeufe]<br />
where ([Measures].[Verkaufswert], [Datum].[Jahr].&amp;[2010])</p>
<p>liefert:</p>
<p><img alt="Monate inkl. Alle Mal Produkt" src="http://csopro.de/biblog/wp-content/uploads/2010/06/monateallxprodukte.jpg" width="211" height="80" /></p>
</blockquote>
<p>F&#252;r den Planwert konstruieren wir ein einfache Element der Attribut-Hierarchie Monat, das konstant 10 ist:</p>
<blockquote>
<p>with member [Datum].[Monat].[Plan] as 10<br />
select non empty {[Datum].[Monat].[Monat].members, [Datum].[Monat].[All], [Datum].[Monat].[Plan]} on columns,<br />
non empty [Produkt].[Produkt].[Produkt].members on rows<br />
from [Verkaeufe]<br />
where ([Measures].[Verkaufswert], [Datum].[Jahr].&amp;[2010])</p>
<p>liefert:</p>
<p><img alt="Monate inkl. Alle und Plan mal Produkte" src="http://csopro.de/biblog/wp-content/uploads/2010/06/monatplanxprodukte.jpg" width="269" height="102" /></p>
</blockquote>
<p>(Die unsch&#246;ne unbekannt-Zeile kann man nat&#252;rlich leicht loswerden, aber das ist hier nicht unser Thema)</p>
<p>Das war noch eine leichte &#220;bung. Aber in diesen Berechnungen k&#246;nnen wir auch rechnen, wie in jedem MDX. Somit k&#246;nnen wir einfach die Differenz anzeigen:</p>
<blockquote>
<p>with member [Datum].[Monat].[Plan] as 10<br />
member [Datum].[Monat].[Differenz] as [Datum].[Monat].[All] - [Datum].[Monat].[Plan]<br />
select non empty {[Datum].[Monat].[Monat].members, [Datum].[Monat].[All], [Datum].[Monat].[Plan], [Datum].[Monat].[Differenz]} on columns,<br />
non empty [Produkt].[Produkt].[Produkt].members on rows<br />
from [Verkaeufe]<br />
where ([Measures].[Verkaufswert], [Datum].[Jahr].&amp;[2010])</p>
<p>liefert:</p>
<p><img alt="Monate inkl. Alle, Plan, Differenz Mal Produkte" src="http://csopro.de/biblog/wp-content/uploads/2010/06/monatediffxprodukte.jpg" width="329" height="100" /></p>
</blockquote>
<p>Das finde ich ein sehr sch&#246;nes Beispiel, wie man berechnete Dimensions-Elemente einsetzt.</p>
<p>Um das ganze in Reporting Services zu verwenden, muss man alles nat&#252;rlich auf die Zeilen bringen und nur das Measure in den Spalten haben - aber das ist trivial.</p>
<p>Au&#223;erdem muss man in der Matrix noch die Sortierung l&#246;sen, aber &#252;ber iif-Berechnungen in dem Sortierungsfeld ist das auch leicht zu l&#246;sen.</p>
]]></content:encoded>
			<wfw:commentRss>http://csopro.de/biblog/2010/06/berechnete-elemente-in-mdx-abfragen-mit-anwendung-in-reporting-services-2005/feed/</wfw:commentRss>
		</item>
		<item>
		<title>SELECTS beim Lookup dynamisch zusammenstellen</title>
		<link>http://csopro.de/biblog/2010/04/selects-beim-lookup-dynamisch-zusammenstellen/</link>
		<comments>http://csopro.de/biblog/2010/04/selects-beim-lookup-dynamisch-zusammenstellen/#comments</comments>
		<pubDate>Sun, 11 Apr 2010 17:02:33 +0000</pubDate>
		<dc:creator>martin</dc:creator>
		
		<category><![CDATA[MS Integration Services]]></category>

		<guid isPermaLink="false">http://csopro.de/biblog/2010/04/selects-beim-lookup-dynamisch-zusammenstellen/</guid>
		<description><![CDATA[Manchmal reicht es nicht aus, feste SQL-Statements als Quelle oder bei Lookups zu hinterlegen.
Bei einer OLE-DB-Quelle kann man ganz einfach das SQL-Statement in einer Variable ablegen.
Wie man das auch bei Lookups machen kann, zeigt dieser Eintrag.
Zun&#228;chst zur Motivation:
Man k&#246;nnte sich vorstellen, dass innerhalb eines Datenflusstasks immer nur Daten eines Tages in eine Tabelle geschrieben werden. [...]]]></description>
			<content:encoded><![CDATA[<p>Manchmal reicht es nicht aus, feste SQL-Statements als Quelle oder bei Lookups zu hinterlegen.</p>
<p>Bei einer OLE-DB-Quelle kann man ganz einfach das SQL-Statement in einer Variable ablegen.</p>
<p>Wie man das auch bei Lookups machen kann, zeigt dieser Eintrag.</p>
<p>Zun&#228;chst zur <u>Motivation</u>:</p>
<p>Man k&#246;nnte sich vorstellen, dass innerhalb eines Datenflusstasks immer nur Daten eines Tages in eine Tabelle geschrieben werden. Der Tag steht dabei in einer Variablen. Nun soll bei jedem Lookup &#252;berpr&#252;ft werden, ob der Primary Key der Daten an diesem Tag schon in einer Tabelle steht. Da der Lookup alle Daten cacht, macht es Sinn nur die Daten des Datums aus der Variablen zu lesen.</p>
<p>In unserem Beispiel hei&#223;t die Variable &#8220;Tag_deutsch&#8221; und speichert das Datum als String mit deutschem Datumsformat.</p>
<p>Das Lookup-SQL m&#252;sste also so aussehen:</p>
<blockquote>
<p>select * from ZahlenProTag<br />
Where Tag = CONVERT(date, &#39;7.5.2010&#39;, 104)</p>
</blockquote>
<p>oder allgemein</p>
<blockquote>
<p>select * from ZahlenProTag<br />
Where Tag = CONVERT(date, &#39;&lt;Inhalt der Variablen Tag_deutsch&gt;&#39;, 104)</p>
</blockquote>
<p>Deswegen legen wir eine berechnete (EvaluateAsExpression:=true) Variable &#8220;Lookup-SQL&#8221; an, mit der Expression:</p>
<blockquote>
<p>&quot;select * from ZahlenProTag<br />
Where Tag = CONVERT(date, &#39;&quot; + @[User::Tag_deutsch] + &quot;&#39;, 104)&quot;</p>
</blockquote>
<p>Jetzt bauen wir erst mal unseren Data Flow Task, noch mit dem kompletten Lookup auf ZahlenProTag:</p>
<p><img alt="BeispielDataFlow" src="http://csopro.de/biblog/wp-content/uploads/2010/04/beispieldataflow.jpg" width="369" height="319" /></p>
<p>Zu den einzelnen Komponenten:</p>
<ul dir="ltr">
<li>
<div>alle Zahlen von 1 bis 10 habe ich bereits in meinem letzten Blogeintrag verwendet (s. <a href="http://csopro.de/biblog/2010/04/temporaere-tabellen-in-ssis-verwenden/">http://csopro.de/biblog/2010/04/temporaere-tabellen-in-ssis-verwenden/</a>). Es liefert alle Zahlen von 1 bis 10</div>
</li>
<li>
<div>Eine abgeleitete Spalte mit dem Namen Datum wird hinzugef&#252;gt, mit folgender Formel:<br />
(DT_DBDATE)@[User::Tag_deutsch]<br />
ACHTUNG. Dabei muss als LocaleID deutsch eingestellt sein, da der String ja in deutschem Datumsformat steht</div>
</li>
<li>
<div>Der Lookup sieht auf dem ersten Tab so aus:<br />
<img alt="LookupAllgemein" src="http://csopro.de/biblog/wp-content/uploads/2010/04/lookupallgemein.jpg" width="450" height="431" /><br />
und so auf dem zweiten:<br />
<img alt="LookupVerbindung" src="http://csopro.de/biblog/wp-content/uploads/2010/04/lookupverbindung.jpg" width="308" height="286" /><br />
und so auf dem dritten:<br />
<img alt="LookupSpalten" src="http://csopro.de/biblog/wp-content/uploads/2010/04/lookupspalten.jpg" width="404" height="378" /><br />
Man beachte, dass zum Lookup nur die Zahl und nicht das Datum verwendet wird.<br />
Letzteres k&#246;nnte man nat&#252;rlich machen. Dann w&#252;rde aber die gesamte Tabelle in den Cache geladen werden, obwohl wir wissen, dass nur ein kleiner Bruchteil ben&#246;tigt wird.</div>
</li>
<li>
<div>Das OLE-DB-Ziel ist wieder naheliegend:<br />
Tab 1:<br />
<img alt="ZielVerbindung" src="http://csopro.de/biblog/wp-content/uploads/2010/04/zielverbindung.jpg" width="359" height="339" /><br />
Tab 2:<br />
<img alt="ZielZuordnungen" src="http://csopro.de/biblog/wp-content/uploads/2010/04/zielzuordnungen.jpg" width="404" height="408" /></div>
</li>
</ul>
<p>Soweit so gut.</p>
<p>Nun wollen wir das Lookup-SQL auf die oben angelegte Variable umstellen. Dazu gehen wir in die Ablaufsteuerung / Workflow und selektieren den Datenfluss-Task, so dass wir im Eigenschaftsfenster dessen Eigenschaften sehen.</p>
<p>Dort f&#228;llt auf, dass es unter Sonstiges eine Eigenschaft mit dem Titel [Zahl an diesem Tag schon da?].[SqlCommand] gibt:</p>
<p><img alt="SqlCommand" src="http://csopro.de/biblog/wp-content/uploads/2010/04/sqlcommand.jpg" width="443" height="210" /></p>
<p>[Zahl an diesem Tag schon da?] ist ja der Name der Lookup-Komponente.</p>
<p>SqlCommand gibt das SQL an, das zum Bef&#252;llen des Caches verwendet wird (wir hatten ja den Standard Vollcache belassen)</p>
<p>SqlCommandParam w&#252;rde man nur bei den anderen Cache-Typen ben&#246;tigen.</p>
<p>Nun k&#246;nnen wir &#252;ber Expressions des Data Flow Tasks diese Eigenschaft &#252;berschreiben:</p>
<p><img alt="Expressions&#220;berschreiben" src="http://csopro.de/biblog/wp-content/uploads/2010/04/expressionsueberschreiben.jpg" width="438" height="498" /></p>
<p>So jetzt geht&#8217;s!</p>
<p>Im Status bzw. Protokoll kann man kontrollieren, wieviele Zeilen der Lookup gecacht hat:</p>
<blockquote>
<p>[Zahl an diesem Tag schon da? [13]] Informationen: &#8216;Komponente &#8216;Zahl an diesem Tag schon da?&#8217; (13)&#8217; hat insgesamt 0 Zeilen zwischengespeichert.</p>
</blockquote>
<p>Diese Zeile kommt, wenn man die Variable auf ein neues Datum stellt.</p>
<p>Wenn die Variable auf einem bereits verwendeten Datum steht, kommt:</p>
<blockquote>
<p>[Zahl an diesem Tag schon da? [13]] Informationen: &#8216;Komponente &#8216;Zahl an diesem Tag schon da?&#8217; (13)&#8217; hat insgesamt 10 Zeilen zwischengespeichert.</p>
</blockquote>
<p>Also ist alles OK!</p>
]]></content:encoded>
			<wfw:commentRss>http://csopro.de/biblog/2010/04/selects-beim-lookup-dynamisch-zusammenstellen/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Anf&#252;hrungszeichen im Blog - zum internen Gebrauch</title>
		<link>http://csopro.de/biblog/2010/04/anfuehrungszeichen-im-blog-zum-internen-gebrauch/</link>
		<comments>http://csopro.de/biblog/2010/04/anfuehrungszeichen-im-blog-zum-internen-gebrauch/#comments</comments>
		<pubDate>Sun, 11 Apr 2010 16:12:10 +0000</pubDate>
		<dc:creator>martin</dc:creator>
		
		<category><![CDATA[Allgemeines]]></category>

		<guid isPermaLink="false">http://csopro.de/biblog/2010/04/anfuehrungszeichen-im-blog-zum-internen-gebrauch/</guid>
		<description><![CDATA[Damit aus Code im Blog die Anf&#252;hrungszeichen korrekt kopiert werden k&#246;nnen, folgendes verwenden:
&#38;#39; f&#252;r &#39;
&#38;quot; f&#252;r &#34;
]]></description>
			<content:encoded><![CDATA[<p>Damit aus Code im Blog die Anf&#252;hrungszeichen korrekt kopiert werden k&#246;nnen, folgendes verwenden:</p>
<p>&amp;#39; f&#252;r &#39;</p>
<p>&amp;quot; f&#252;r &quot;</p>
]]></content:encoded>
			<wfw:commentRss>http://csopro.de/biblog/2010/04/anfuehrungszeichen-im-blog-zum-internen-gebrauch/feed/</wfw:commentRss>
		</item>
		<item>
		<title>tempor&#228;re Tabellen in SSIS verwenden</title>
		<link>http://csopro.de/biblog/2010/04/temporaere-tabellen-in-ssis-verwenden/</link>
		<comments>http://csopro.de/biblog/2010/04/temporaere-tabellen-in-ssis-verwenden/#comments</comments>
		<pubDate>Sun, 11 Apr 2010 15:05:41 +0000</pubDate>
		<dc:creator>martin</dc:creator>
		
		<category><![CDATA[MS Integration Services]]></category>

		<guid isPermaLink="false">http://csopro.de/biblog/2010/04/temporaere-tabellen-in-ssis-verwenden/</guid>
		<description><![CDATA[F&#252;r manche Aufgaben erscheint es sinnvoll, Daten zun&#228;chst in tempor&#228;re Tabellen zu &#252;bertragen, um sie dann zum Beispiel per Insert oder Update in die Zieltabelle zu &#252;berf&#252;hren (damit kann man ein &#8220;Bulk Update&#8221; durchf&#252;hren).
Dazu kann man nat&#252;rlich im SQL-Server beliebige Tabellen anlegen, die dann nur &#8220;logisch&#8221; tempor&#228;r sind, da sie ja st&#228;ndig in der Datenbank [...]]]></description>
			<content:encoded><![CDATA[<p>F&#252;r manche Aufgaben erscheint es sinnvoll, Daten zun&#228;chst in tempor&#228;re Tabellen zu &#252;bertragen, um sie dann zum Beispiel per Insert oder Update in die Zieltabelle zu &#252;berf&#252;hren (damit kann man ein &#8220;Bulk Update&#8221; durchf&#252;hren).</p>
<p>Dazu kann man nat&#252;rlich im SQL-Server beliebige Tabellen anlegen, die dann nur &#8220;logisch&#8221; tempor&#228;r sind, da sie ja st&#228;ndig in der Datenbank sind.</p>
<p>Der SQL Server bietet aber auch tempor&#228;re Tabellen an, die sessionweit (# - sobald die Connection geschlossen wird, verschwindet die Tabelle) oder global (## - sobald die letzte Connection geschlossen wird, die diese Tabelle verwendet, verschwindet sie) gelten.</p>
<p>Diese in SSIS zu verwenden, mag z.B. Sinn machen, wenn man keine Tabellen-Erstellungs-Rechte auf dem Zielserver hat.</p>
<p>Sie zu verwenden, ist nicht ganz einfach:</p>
<p>Nehmen wir an, wir wollen eine tempor&#228;re Tabelle mit einer einzigen numerischen Spalte anlegen, so geht das so:</p>
<blockquote>
<p>
CREATE TABLE [dbo].[#ZielTabelle]<br />
(<br />
[zahl] [int] NOT NULL<br />
)</p>
<p><u>Randbemerkung:<br /></u>Man kann auch Primary Keys vergeben, z.B. mit folgendem Statement:</p>
<blockquote>
<p>CREATE TABLE [dbo].[#ZielTabelle]<br />
(<br />
[zahl] [int] NOT NULL,<br />
CONSTRAINT ZielTabelle_PK PRIMARY KEY CLUSTERED<br />
(<br />
[zahl] ASC<br />
)<br />
)</p>
</blockquote>
<p>Das w&#228;re aber nicht so gut, da dann ein Vorteil verloren geht: Eine solche tempor&#228;re Tabelle (#) kann mehrfach existieren (je Connection einmal). Verwendet man aber einen festen Primary-Key-Namen, so geht das nicht mehr. Man k&#246;nnte in den Namen des Primary Key eine GUID einbauen und h&#228;tte wieder Eindeutigkeit.</p>
</blockquote>
<p>Dieses Create-Table-Statement baut man in eine Execute-SQL-Task und kann dann folgenden Workflow abbilden:</p>
<p><img alt="Workflow" src="http://csopro.de/biblog/wp-content/uploads/2010/04/workflow.jpg" width="179" height="458" /></p>
<p>Den DROP TABLE braucht man nicht unbedingt, da nach Schlie&#223;en der Connection die Tabelle eh verschwindet. Aber aus Gr&#252;nden der Hygiene empfehle ich es.</p>
<p>Unser Datenflusstask soll im Beispiel einfach alle Zahlen von 1 bis 10 in die tempor&#228;re Tabelle schreiben:</p>
<p><img alt="DataFlow" src="http://csopro.de/biblog/wp-content/uploads/2010/04/dataflow.jpg" width="262" height="149" /></p>
<p>Die Skript-Komponente hat folgenden Code:</p>
<blockquote>
<p>public override void CreateNewOutputRows()<br />
{<br />
for (int i = 1; i &lt;= 10; i++)<br />
{<br />
AusgabeBuffer.AddRow();<br />
AusgabeBuffer.Zahl = i;<br />
}<br />
}</p>
</blockquote>
<p>Nun haben wir aber ein Problem: Die temp-Tabelle taucht nicht in der Liste der Tabellen auf. Deswegen empfiehlt es sich, auf dem Entwicklungsrechner eine Tabelle mit der selben Struktur anzulegen, und diese dann auszuw&#228;hlen (hier &#8220;ZielTab_gibtsNetProduktiv&#8221;):</p>
<p><img alt="Ziel" src="http://csopro.de/biblog/wp-content/uploads/2010/04/ziel.jpg" width="447" height="500" /></p>
<p>Nun kann man auf dem n&#228;chsten Reiter die Zuordnungen - wie gewohnt - einstellen (was in diesem Beispiel sehr einfach ist <img src='http://csopro.de/biblog/wp-includes/images/smilies/icon_smile.gif' alt=':-)' class='wp-smiley' /> ):</p>
<p><img alt="Zuordnungen" src="http://csopro.de/biblog/wp-content/uploads/2010/04/zuordnungen.jpg" width="319" height="232" /></p>
<p>Noch haben wir aber nichts gewonnen, da die tempor&#228;re Tabelle nicht verwendet wird.</p>
<p>Es m&#252;ssen deshalb noch ein paar Einstellungen vorgenommen werden:</p>
<ul dir="ltr">
<li>
<div>Unter Eigenschaften des OLEDB-Ziels muss man in OpenRowset die tempor&#228;re Tabelle eintragen:<br />
<u>Vorher:<br /></u><img alt="nachher" src="http://csopro.de/biblog/wp-content/uploads/2010/04/vorher.jpg" width="423" height="216" /><br />
<u>Nachher:</u><br />
<img alt="nachher" src="http://csopro.de/biblog/wp-content/uploads/2010/04/nachher.jpg" width="424" height="249" /></div>
</li>
<li>
<div>Die weitere Eigenschaft &#8220;ValidateExternalMetaData&#8221; des OLEDB-Ziels muss auf &#8220;false&#8221; gestellt, da zum Zeitpunkt der Entwicklung bzw. des Starten des Pakets die tempor&#228;re Tabelle noch nicht existiert.</div>
</li>
</ul>
<p>Jetzt funktioniert das Paket immernoch nicht. Es tritt folgender Fehler auf:</p>
<blockquote>
<p>[Speichern in temp Tabelle [16]] Fehler: Fehler beim &#214;ffnen eines FastLoad-Rowsets f&#252;r &#8216;#ZielTabelle&#8217;. &#220;berpr&#252;fen Sie, ob das Objekt in der Datenbank vorhanden ist.</p>
</blockquote>
<p>Offensichtlich kennt der DataFlow Task die Tabelle nicht. Dies liegt daran, dass der CREATE TABLE-Befehl und der Datenfluss-Task nicht in der selben Connection ausgef&#252;hrt werden. Deswegen m&#252;ssen wir noch die Eigenschaft der Connection &#8220;RetainSameConnection&#8221; auf true setzen:</p>
<p><img alt="RetainSameConnection" src="http://csopro.de/biblog/wp-content/uploads/2010/04/retainsameconnection.jpg" width="260" height="178" /></p>
<p>Jetzt funktioniert es.</p>
<p><u>Eine Anmerkung:<br /></u>Man kann die Zuordnungen des OLEDB-Ziels nicht mehr mit dem normalen Editor bearbeiten, da zum Design-Zeitpunkt die tempor&#228;re Tabelle nicht existiert. Man kann das durch das oben gezeigte Umstellen auf eine nur auf dem Entwicklungsserver existierende Tabelle umgehen.</p>
]]></content:encoded>
			<wfw:commentRss>http://csopro.de/biblog/2010/04/temporaere-tabellen-in-ssis-verwenden/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>

