<?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>ixto-Blog &#187; Integration Services 2008</title>
	<atom:link href="http://www.ixto.de/blog/category/sql-server-2008/integration-services-2008/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.ixto.de/blog</link>
	<description>Business Intelligence, Database Consulting, Software Development</description>
	<lastBuildDate>Mon, 26 Sep 2011 09:23:18 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Surrogate-Keys f&#252;r Parent-Child-Hierarchie mit SSIS erzeugen und Hierarchiestufen ausgeben</title>
		<link>http://www.ixto.de/blog/sql-server-2008/integration-services-2008/surrogate-keys-fr-parent-child-hierarchie-mit-ssis-erzeugen-und-hierarchiestufen-ausgeben/</link>
		<comments>http://www.ixto.de/blog/sql-server-2008/integration-services-2008/surrogate-keys-fr-parent-child-hierarchie-mit-ssis-erzeugen-und-hierarchiestufen-ausgeben/#comments</comments>
		<pubDate>Mon, 26 Sep 2011 09:16:17 +0000</pubDate>
		<dc:creator>Christopher Glomb</dc:creator>
				<category><![CDATA[Integration Services 2008]]></category>

		<guid isPermaLink="false">http://www.ixto.de/blog/?p=587</guid>
		<description><![CDATA[Das folgende Szenario bezieht sich auf die Microsoft Demo-Datenbank AdventureWorks 2008. Diese kann auf http://msftdbprodsamples.codeplex.com/ heruntergeladen werden. Die Umsetzung dieses Beispiels erfolgte mit Visual Studio 2008 und SQL Server 2008.
&#160;
Ich möchte in diesem Blogbeitrag erklären, wie man Surrogate-Keys für Parent-Child-Hierarchien und die jeweilige Hierarchiestufe des Mitarbeiters anlegen kann. Dabei hilft uns die Angestellten Tabelle der [...]]]></description>
			<content:encoded><![CDATA[<p>Das folgende Szenario bezieht sich auf die Microsoft Demo-Datenbank AdventureWorks 2008. Diese kann auf <a href="http://msftdbprodsamples.codeplex.com/">http://msftdbprodsamples.codeplex.com/</a> heruntergeladen werden. Die Umsetzung dieses Beispiels erfolgte mit Visual Studio 2008 und SQL Server 2008.</p>
<p>&#160;</p>
<p>Ich möchte in diesem Blogbeitrag erklären, wie man Surrogate-Keys für Parent-Child-Hierarchien und die jeweilige Hierarchiestufe des Mitarbeiters anlegen kann. Dabei hilft uns die Angestellten Tabelle der AdventureWorks Datenbank. Diese findet man unter dem Tabellennamen Employee im Schema HumanResources. Der Mitarbeiter wird über die Spalte EmployeeID identifiziert und der Vorgesetzte über die Spalte ManagerID. Angestellte die keinen Vorgesetzten haben, sind mit entsprechender ManagerID auf NULL gesetzt. Dementsprechend ist der/sind die Mitarbeiter der/die keinen Vorgesetzten hat/haben, die höchste Stufe in unserer Organisation (in unserem Fall also der Chef).</p>
<p>&#160;</p>
<p> <span id="more-587"></span>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb.png" width="585" height="287" /></a></p>
<p>&#160;</p>
<p>Wir müssen in unserer Tabelle in der später unsere Daten landen sollen zunächst zwei zusätzliche Spalten einfügen. Das wäre zum einen die Spalte <i>EmployeeBK</i> und <i>ManagerBK</i>. Die Erklärung dazu folgt dann später im Text.</p>
<p>&#160;</p>
<p>Außerdem benötigen wir in unserer Ausgangstabelle (Employee) eine Hilfsspalte (ich habe mich i.F. für istImportiert entschieden (NULL ist zulässig)) mit beliebigen Namen und dem Datentyp bit.</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_3.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_3.png" width="585" height="137" /></a></p>
<p>&#160;</p>
<p>Als nächstes müssen wir dafür sorgen, dass alle Werte in dieser neuen Spalte den Wert 0 enthalten. Damit das Ganze gleich in SSIS passiert, ziehen wir uns einen SQL-Task auf unsere Arbeitsfläche und fügen den Befehl <strong>update [HumanResources].[Employee] set istImportiert = 0</strong> ein.</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_4.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_4.png" width="600" height="490" /></a></p>
<p>&#160;</p>
<p>Für den nächsten Schritt benötigen wir eine Laufvariable mit dem Datentyp Integer und dem Standardwert 0. Danach legen wir einen for-Schleifencontainer an. Nachdem Ihr die Eigenschaften festgelegt habt, legen wir einen Datenflusstask in den Container.</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_5.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_5.png" width="600" height="505" /></a></p>
<p>&#160;</p>
<p>Vorweg schon mal die Ansicht, wie es später in unserem Datenflusstask aussehen wird:</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_6.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_6.png" width="600" height="558" /></a></p>
<p>&#160;</p>
<p>&#160;</p>
<p><b><font size="3">Schritt 1</font></b></p>
<p><strong></strong></p>
<p>Wie üblich brauchen wir zunächst eine Quelle. Entscheidend ist hier natürlich, dass wir neben der ManagerID und der EmployeeID die Abfrage darauf einschränken, dass nur Daten geladen werden, wo der Wert für istImportiert = 0 ist.</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_7.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_7.png" width="590" height="616" /></a></p>
<p>&#160;</p>
<p><b><font size="3">Schritt 2</font></b></p>
<p><strong><font size="3"></font></strong></p>
<p>In der Komponente für bedingtes Teilen erfolgt die Überprüfung, welche Daten unsere Wurzelknoten (also unsere Manager mit dem Eintrag null) sind. Alle anderen Mitarbeiter werden als Knoten ausgegeben. </p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_8.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_8.png" width="591" height="351" /></a></p>
<p>&#160;</p>
<p><b><font size="3">Schritt 3</font></b></p>
<p><strong></strong></p>
<p>Wir kümmern uns zunächst um die Mitarbeiter, die unseren Wurzelknoten repräsentieren. Dazu legen wir eine Abgeleitete Spalte mit dem folgenden Ausdruck an: </p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_9.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_9.png" width="600" height="69" /></a></p>
<p>&#160;</p>
<p>Im zweiten Teil von Schritt 3 müssen die anderen Hierarchiestufen überprüft werden. Hierfür legen wir eine Lookup Komponente an und verbinden diese mit der Ausgabe für Knoten aus der Conditional-Split Komponente. Damit wir es später nicht vergessen, ändern wir unter Allgemein die Einstellung wie nicht getroffene Einträge behandelt werden (auf Fehler ignorieren stellen). Unter Verbindung fügen wir ein SQL-Statement ein, das unsere Tabelle abfragt, in der letztendlich unsere Daten landen sollen. Jetzt kommt eine der erwähnten Spalten aus der Einleitung ins Spiel. EmployeeBK enthält später die Mitarbeiternummer welche aus EmployeeID kommt.</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_10.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_10.png" width="409" height="392" /></a></p>
<p>&#160;</p>
<p>Wir verknüpfen die ManagerID mit EmployeeBK und lassen uns die EmployeeID (im SQL-Statement umbenannt zu EmployeeParentID) ausgeben. Überall wo wir keinen Treffer haben, wird NULL ausgegeben.</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_11.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_11.png" width="600" height="304" /></a></p>
<p>&#160;</p>
<p><b><font size="3">Schritt 4</font></b></p>
<p><strong><font size="3"></font></strong></p>
<p>Mit dem nächsten Conditional Split überprüfen wir, ob EmployeeParentID einen Wert erhalten hat oder nicht. Alle Zeilen für die es einen Treffer gibt, gehen in diesem Beispiel an die Ausgabe „Vater gefunden“.</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_12.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_12.png" width="600" height="354" /></a></p>
<p>&#160;</p>
<p><b><font size="3">Schritt 5</font></b></p>
<p><strong><font size="3"></font></strong></p>
<p>Mit Hilfe einer Union All Komponente verknüpfen wir die eingehenden Zeilen.</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_13.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_13.png" width="599" height="587" /></a></p>
<p>&#160;</p>
<p><b><font size="3">Schritt 6</font></b></p>
<p><strong></strong></p>
<p>Über die Multicast Komponente lassen wir uns Kopien des Datasets erzeugen.</p>
<p>&#160;</p>
<p><b><font size="3">Schritt 7</font></b></p>
<p><strong></strong></p>
<p>In der Komponente OLE DB Befehl fügen wir für unsere Quelltabelle Employee ein Update Statement ein.</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_14.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_14.png" width="600" height="441" /></a></p>
<p>&#160;</p>
<p>Außerdem muss in den Spaltenzuordnungen EmployeeID mit Param_0 verknüpft werden.</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_15.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_15.png" width="598" height="323" /></a></p>
<p>&#160;</p>
<p><b><font size="3">Schritt 8</font></b></p>
<p><strong></strong></p>
<p>Als Erstes betrachten wir die Zuordnungen für unser Ziel (OLE_DST EmployeeNeu). Wie bereits erwähnt, stellt die EmployeeID in unserer Zieltabelle den Surrogate-Key dar. Außerdem muss darauf geachtet werden, dass unsere EmployeeParentID mit der ManagerID verknüpft wird.</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_16.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_16.png" width="593" height="548" /></a></p>
<p>&#160;</p>
<p>Da wir uns immer noch im Schleifencontainer befinden, müssen wir im letzten Schritt dafür sorgen, dass kein Durchlauf mehr erfolgt, wenn unsere Laufvariable einen Wert ungleich 0 hat. Wir fügen eine Komponente „SQL ausführen“ (SQL UPDATE Employee) ein (siehe Bild)</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_17.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_17.png" width="365" height="475" /></a></p>
<p>&#160;</p>
<p>und legen folgende Einstellungen fest:</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_18.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_18.png" width="600" height="421" /></a></p>
<p>&#160;</p>
<p>Wenn noch nicht geschehen, muss im ResultSet unsere Laufvariable ausgegeben werden.</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_19.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_19.png" width="600" height="113" /></a></p>
<p>&#160;</p>
<p><b><font size="3">Hierarchiestufe</font></b></p>
<p><strong><font size="3"></font></strong></p>
<p>Da ich abschließend wissen möchte, wie viele Vorgesetzte ein Mitarbeiter über sich hat, benötige ich eine zusätzliche Variable, die für jeden Durchlauf des Containers hochgezählt und dann in unsere Zieltabelle weggeschrieben wird. Hierfür lege ich zunächst eine Variable (iHierarchiestufe) mit dem Datentyp integer an (Startwert steht auf 1) und ergänze meine Zieltabelle um die Spalte Hierarchiestufe. Den Wert der Variablen füge ich mit Hilfe der Komponente „abgeleitete Spalte“ in eine neue Spalte ein.</p>
<p> <a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_20.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_20.png" width="607" height="569" /></a>
<p>&#160;</p>
<p>Außerdem muss ich noch das Mapping wie in Schritt 8 beschrieben ergänzen falls nicht schon geschehen. </p>
<p>&#160;</p>
<p>Natürlich muss dem Schleifencontainer noch mitgeteilt werden, dass unsere Variable pro Durchlauf hochgezählt wird (AssignExpression). Die Einstellung dafür bitte dem Bild For-Schleifen-Editor entnehmen.</p>
<p>&#160;</p>
<p>Abschließend schauen wir uns noch das Ergebnis für unsere Zieltabelle „EmployeeNeu“ an. Im ersten Durchlauf finden wir genau einen Mitarbeiter der keinen Vorgesetzten hat. Er steht dementsprechend an erster Stelle in der Hierarchie.</p>
<p>&#160;</p>
<p>Erster Durchlauf:</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_21.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_21.png" width="600" height="74" /></a></p>
<p>&#160;</p>
<p>Im zweiten Durchlauf (zweite Ebene in der Hierarchie) haben wir schon mehr Mitarbeiter getroffen. Die ehemalige EmployeeID aus der Quelltabelle befindet sich nun in der Spalte EmployeeBK. Unter ManagerBK finden wir die zugehörige Kennzeichnung des Vorgesetzten (ManagerID aus der Quelltabelle). Unter ManagerID befindet sich der neu erzeugte Surrogate Key.</p>
<p>&#160;</p>
<p>Zweiter Durchlauf:</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_22.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/8ad168261186_9839/image_thumb_22.png" width="600" height="190" /></a></p>
<p>&#160;</p>
<p>Da alle weiteren Hierarchiestufen nach dem gleichem Schema gefüllt werden, spare ich mir an dieser Stelle die Fotos für die nächsten Durchläufe. Viel Spaß beim Ausprobieren.</p>
<p>&#160;</p>
<p>Wenn ich euch mit dieser Anleitung helfen konnte, ihr weitere Fragen, Anmerkungen, Verbesserungsvorschläge oder Lösungen habt, freue ich mich auf Kommentare!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ixto.de/blog/sql-server-2008/integration-services-2008/surrogate-keys-fr-parent-child-hierarchie-mit-ssis-erzeugen-und-hierarchiestufen-ausgeben/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>ExecutionValue beim Execute SQL-Task in SSIS</title>
		<link>http://www.ixto.de/blog/sql-server-2008/integration-services-2008/executionvalue-beim-execute-sql-task-in-ssis-2/</link>
		<comments>http://www.ixto.de/blog/sql-server-2008/integration-services-2008/executionvalue-beim-execute-sql-task-in-ssis-2/#comments</comments>
		<pubDate>Fri, 22 Jul 2011 09:56:18 +0000</pubDate>
		<dc:creator>David Veenhuis</dc:creator>
				<category><![CDATA[Integration Services 2008]]></category>

		<guid isPermaLink="false">http://www.ixto.de/blog/allgemein/executionvalue-beim-execute-sql-task-in-ssis-2/</guid>
		<description><![CDATA[Es gibt einen einfachen Weg um festzustellen, wieviele Zeilen von der Ausführung von SQL-Anweisungen wie DELETE oder UPDATE mit dem Execute-SQL-Task betroffen sind. Dazu kann die ExecutionValue-Property des Execute-SQL-Tasks verwendet werden. Das wird hier am Beispiel einer DELETE-Anweisung vorgestellt.
&#160;
Auf die Property kann nicht direkt zugegriffen werden. Stattdessen wird der Property ExecValueVariable eine Package-Variable zugeordnet. In [...]]]></description>
			<content:encoded><![CDATA[<p>Es gibt einen einfachen Weg um festzustellen, wieviele Zeilen von der Ausführung von SQL-Anweisungen wie DELETE oder UPDATE mit dem Execute-SQL-Task betroffen sind. Dazu kann die ExecutionValue-Property des Execute-SQL-Tasks verwendet werden. Das wird hier am Beispiel einer DELETE-Anweisung vorgestellt.<span id="more-583"></span></p>
<p>&#160;</p>
<p>Auf die Property kann nicht direkt zugegriffen werden. Stattdessen wird der Property ExecValueVariable eine Package-Variable zugeordnet. In diese wird dann zur Laufzeit des Paketes die Anzahl der Zeilen, die von der ausgeführten SQL-Anweisung betroffen waren, eingetragen. Diese Variable kann im weiteren Package-Ablauf verwendet werden. Für die Anzahl der Zeilen wird eine Int-Variabel verwendet, die in disem Beispiel iExecValue genannt wird.</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/ExecutionValue-beim-Execute-SQL-Task-in-_A742/image.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/ExecutionValue-beim-Execute-SQL-Task-in-_A742/image_thumb.png" width="430" height="107" /></a></p>
<p>&#160;</p>
<p>Als nächstes wird ein Execute-SQL-Task erstellt, der die Beispiel-Tabelle Example mittels einer DELETE-Anweisung leert. Die Tabelle enthält 3 Datensätze und somit erwarten wir die Zahl 3 als Anzahl der betroffenen Zeilen. Im Editor des Tasks wird die Connection ausgewählt und die Anweisung „DELETE FROM Example“ als Direkteingabe eingegeben.</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/ExecutionValue-beim-Execute-SQL-Task-in-_A742/image_3.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/ExecutionValue-beim-Execute-SQL-Task-in-_A742/image_thumb_3.png" width="511" height="435" /></a></p>
<p>&#160;</p>
<p>Durch Rechtsklick auf den Task und Auswahl von „Eigenschaften“ wird der Eigenschaften-Editor des Tasks geöffnet. Da wird der Property ExecValueVariable über die Dropdpwn-Liste die vorher angelegte Variabel iExecValue aus. </p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/ExecutionValue-beim-Execute-SQL-Task-in-_A742/image_4.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/ExecutionValue-beim-Execute-SQL-Task-in-_A742/image_thumb_4.png" width="337" height="515" /></a></p>
<p>&#160;</p>
<p>Um nach Start des Paketes das Ergebnis zu prüfen, setzen wir einen Breakpoint auf das PostExecute-Ereignis des Tasks (Rechtsklick auf den Task, Haltepunkte bearbeiten wählen und dann den Eintrag für „OnPostExecute“ anhaken). Damit kann direkt nach Ausführung des SQL-Tasks geprüft werden, ob die Variable tatsächlich die gewünschte Anzahl der betroffenen Zeilen enthält.</p>
<p>&#160;</p>
<p>Anschließende wird das Paket ausgeführt. Bei Erreichen des Breakpoints fügen wir über Debuggen-&gt;Fenster-&gt;Überwachungsfenster1. Dort tippen wir in die Namensspalte den Namen unserer Variabel ein. Anschließend sehen wir, dass unsere Variable tatsächlich die Anzahl der von der SQL-Anweisung betroffenen Zeilen enthält:</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/ExecutionValue-beim-Execute-SQL-Task-in-_A742/image_5.png"><img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/ExecutionValue-beim-Execute-SQL-Task-in-_A742/image_thumb_5.png" width="526" height="174" /></a>#</p>
<p>&#160;</p>
<p>Es kann auch eine Stored Procedure aufgerufen werden, die wie in unserem Beispiel eine DELETE-Anweisung ausführt. In der Stored Procedure darf dann nicht SET NOCOUNT ON“ verwenden, da dann die Zahl der betroffenen Zeilen nicht ermittelt wird.</p>
<p>&#160;</p>
<p>In dem Beispiel kann TRUNCATE nicht verwendet werden, da bei TRUNCATE nicht zeilenweise gelöscht wird, wie bei DELETE. Bei TRUNCATE werden stattdessen die Datenseiten der betroffenen Tabelle deallokiert. </p>
]]></content:encoded>
			<wfw:commentRss>http://www.ixto.de/blog/sql-server-2008/integration-services-2008/executionvalue-beim-execute-sql-task-in-ssis-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Pr&#252;fung auf Nachkommastellen bei Importen (SSIS)</title>
		<link>http://www.ixto.de/blog/allgemein/prfung-auf-nachkommastellen-bei-importen-ssis/</link>
		<comments>http://www.ixto.de/blog/allgemein/prfung-auf-nachkommastellen-bei-importen-ssis/#comments</comments>
		<pubDate>Mon, 30 May 2011 16:07:57 +0000</pubDate>
		<dc:creator>Ricardo Radke</dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Integration Services 2005]]></category>
		<category><![CDATA[Integration Services 2008]]></category>

		<guid isPermaLink="false">http://www.ixto.de/blog/?p=549</guid>
		<description><![CDATA[Es gibt Mittel und Wege, Gültigkeitsprüfungen für Gleitkommazahlen innerhalb der Integration Services zu realisieren. Die Überlegungen zum Vorgehen können dabei von einfachen Konvertierungen bis hin zu komplexen Script-Tasks reichen.    

In diesem Beitrag möchte ich eine Möglichkeit erläutern, die so simpel ist, dass der ein oder andere vielleicht gar nicht erst daran gedacht [...]]]></description>
			<content:encoded><![CDATA[<p>Es gibt Mittel und Wege, Gültigkeitsprüfungen für Gleitkommazahlen innerhalb der Integration Services zu realisieren. Die Überlegungen zum Vorgehen können dabei von einfachen Konvertierungen bis hin zu komplexen Script-Tasks reichen.    </p>
<p></br>
<p>In diesem Beitrag möchte ich eine Möglichkeit erläutern, die so simpel ist, dass der ein oder andere vielleicht gar nicht erst daran gedacht hat – frei nach dem Motto: „Ich seh‘ den Wald vor lauter Bäumen nicht“.    </p>
<p> <span id="more-549"></span> <br /></br>  Nehmen wir an, es handelt sich bei unserer Zahl um einen Prozentwert, dessen Anzahl der Nachkommastellen wir auf 5 begrenzen und im Falle der Überschreitung einen Fehler ausgeben lassen wollen (z.B. wenn die Zielspalte in der Datenbank nicht mehr als 5 zulässt). Dies würde sich zwar mit einfachen Mitteln realisieren lassen, jedoch unter Umständen nicht immer zum gewünschten Ergebnis führen.    </p>
<p></br>Angenommen es handelt sich um den Wert <i>12,12345<b>00 </b></i>– so würden die beiden letzten Nachkommastellen nach „Schema-F“ abgeschnitten und ein Fehler erzeugt werden. Da es sich in diesem Falle aber um das Abschneiden von lediglich zwei nullen handelt und somit der numerische Wert trotzdem unverändert und somit gültig bleibt, muss hier eine andere Lösung her.     </p>
<p></br>
<p>Wie? Ganz einfach! Man vergleiche die Zahl mit sich selbst – und zwar konvertiert in einen Gleitkommawert mit 5, sowie mit 6 Nachkommastellen. Das ganze könnte dann z.B. so aussehen:    </p>
<p></br><i>(DT_NUMERIC, 10, 5)Wert == (DT_NUMERIC, 10, 6)Wert      <br /></i></p>
<p></br>
<p>Die folgende Abbildung sollte das Prinzip noch einmal veranschaulichen und weitere Erläuterungen überflüssig machen <img style="border-bottom-style: none; border-left-style: none; border-top-style: none; border-right-style: none" class="wlEmoticon wlEmoticon-smile" alt="Smiley" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/Prfung-auf-Nachkommastellen-bei-Importen_FE03/wlEmoticon-smile.png" />     </p>
<p></br><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/Prfung-auf-Nachkommastellen-bei-Importen_FE03/ssis_conversion.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="ssis_conversion" border="0" alt="ssis_conversion" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/Prfung-auf-Nachkommastellen-bei-Importen_FE03/ssis_conversion_thumb.png" width="494" height="263" /></a></p>
<p></br></p>
]]></content:encoded>
			<wfw:commentRss>http://www.ixto.de/blog/allgemein/prfung-auf-nachkommastellen-bei-importen-ssis/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Jahreswerte auf Monate verlustfrei verteilen &#8211; Teil 1</title>
		<link>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/jahreswerte-auf-monate-verlustfrei-verteilen-teil-1/</link>
		<comments>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/jahreswerte-auf-monate-verlustfrei-verteilen-teil-1/#comments</comments>
		<pubDate>Wed, 23 Feb 2011 09:15:00 +0000</pubDate>
		<dc:creator>Chris Jacob</dc:creator>
				<category><![CDATA[Integration Services 2005]]></category>
		<category><![CDATA[Integration Services 2008]]></category>
		<category><![CDATA[SQL Server 2005]]></category>
		<category><![CDATA[SQL Server 2008]]></category>

		<guid isPermaLink="false">http://www.ixto.de/blog/?p=510</guid>
		<description><![CDATA[Ich habe wieder ein schönes Beispiel gefunden an der man die Flexibilität der Integration Services demonstrieren kann. Die Aufgabe ist recht einfach: es geht darum, einen Jahreswert mit Dezimalstellen (in unserem Fall ein numeric(6,2), z.B. ein Euro-Wert) gleichmäßig auf 12 Monate zu verteilen. Die Division sollte uns hoffentlich keine Kopfschmerzen bereiten, die Herausforderung liegt darin, [...]]]></description>
			<content:encoded><![CDATA[<p>Ich habe wieder ein schönes Beispiel gefunden an der man die Flexibilität der Integration Services demonstrieren kann. Die Aufgabe ist recht einfach: es geht darum, einen Jahreswert mit Dezimalstellen (in unserem Fall ein numeric(6,2), z.B. ein Euro-Wert) gleichmäßig auf 12 Monate zu verteilen. Die Division sollte uns hoffentlich keine Kopfschmerzen bereiten, die Herausforderung liegt darin, dass unser Datentyp nur zwei Nachkommastellen erlaubt und daher viele Werte bei der Division einen Genauigkeitsverlust erleiden würden.<br />
<span id="more-510"></span><br />
Ein kurzes Beispiel: teilen wir den Wert 100,00 € durch 12 erhalten wir pro Monat einen Wert von 8,3333. € Würden wir nun 12 mal den Wert 8,33 € als Monatswert in unsere Datenbank übernehmen, würden uns über das Jahr hinweg gesehen 0,04 € fehlen. Das Ziel wäre hier, diese 4 Eurocent (also den Differenzbetrag) auf den letzten Monat aufzuschlagen.</p>
<p>&#160;</p>
<p>Das Problem begegnet uns immer dann, wenn in der Datenbank Werte in einer anderen Granularität gespeichert werden (in diesem Fall monatsgenau) als diese von der darauf aufsetzenden Anwendung geliefert werden (diese erlaubt nur jahresgenaue Werte). Für spätere Auswertungen oder Änderungen möchten wir monatsgenaue Werte speichern, ohne den korrekten Jahreswert (die Summe der 12 Monate) zu verlieren.</p>
<p>&#160;</p>
<p>Auch hier führen wieder viele Wege nach Rom, daher zeige ich im ersten Teil neben der eigentlichen Problemstellung und dem Kern der Lösung eine von vier (!) Möglichkeiten die ich gefunden habe um die Aufgabe zu lösen.</p>
<p>&#160;</p>
<p>Zunächst einmal überlege ich mir geeignete Ausdrücke um die oben genannte Anforderung innerhalb eines SSIS-Datenfluss&#8217; umsetzen zu können. Für die Monate 1 bis 11 genügt folgender Ausdruck:</p>
<p>&#160;</p>
<p><font face="Consolas">(DT_NUMERIC,6,2)(Value / 12)</font></p>
<p>&#160;</p>
<p>Der Monat 12 erfordert eine Sonderbehandlung, welche durch folgenden Ausdruck gewährleistet wird:</p>
<p>&#160;</p>
<p><font face="Consolas">(DT_NUMERIC,6,2)(Value &#8211; (DT_NUMERIC,6,2)(11 * (DT_NUMERIC,6,2)(Value / 12)))</font></p>
<p>&#160;</p>
<p>Im Grunde ermittelt dieser Ausdruck die Differenz zwischen dem Jahreswert und der Summe der 11 Monatswerte um den Wert für Monat 12 zu berechnen. Beachten Sie dabei, dass es egal ist ob bei der Ermittlung der Monatswerte ab- oder aufgerundet oder gar abgeschnitten wird (was hier der Fall ist), der Wert für Monat 12 wird stets korrekt ermittelt.</p>
<p>&#160;</p>
<p>Einer der naheliegendsten Wege um diese Logik umzusetzen ist die Nutzung eines Cross Joins zwischen dem Jahreswert und 12 Monaten. Damit wird der Jahreswert von einer Zeile auf 12 Zeilen verteilt. Anschließend kann in einem &#8220;Abgeleitete Spalte&#8221;-Task der Wert mit Hilfe der oben beschriebenen Ausdrücke manipuliert werden.</p>
<p>&#160;</p>
<p>Folgender Screenshot zeigt den Datenfluss und zwei Data Viewer: einen nach dem Cross Join und einem nach dem Anwenden der Ausdrücke. Beachten Sie, dass sich der Wert für Monat 12 von allen anderen unterscheidet um einen korrekten Jahreswert zu gewährleisten.</p>
<p>&#160;</p>
<p><a href="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/cc00279c2c81_1174B/image.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/cc00279c2c81_1174B/image_thumb.png" width="572" height="291" /></a></p>
<p>&#160;</p>
<p>Im zweiten Teil zeige ich drei (!) weitere Vorgehensweisen, sowie die Ergebnisse eines Performancetests um zu ermitteln, wie sich die unterschiedlichen Wege im produktiven Einsatz bewähren könnten.</p>
<p></br></p>
]]></content:encoded>
			<wfw:commentRss>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/jahreswerte-auf-monate-verlustfrei-verteilen-teil-1/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>SSIS Error Handling Design Pattern: Transaktionen</title>
		<link>http://www.ixto.de/blog/sql-server-2008/integration-services-2008/ssis-error-handling-design-pattern-transaktionen/</link>
		<comments>http://www.ixto.de/blog/sql-server-2008/integration-services-2008/ssis-error-handling-design-pattern-transaktionen/#comments</comments>
		<pubDate>Fri, 15 Oct 2010 17:27:19 +0000</pubDate>
		<dc:creator>Ricardo Radke</dc:creator>
				<category><![CDATA[Integration Services 2008]]></category>
		<category><![CDATA[SSIS Transaktion Error Handling Errorhandling]]></category>

		<guid isPermaLink="false">http://www.ixto.de/blog/?p=217</guid>
		<description><![CDATA[Die Konfigurationsmöglichkeiten für das Transaktionsverhalten in SSIS Paketen reichen in bestimmten Fällen nur bedingt aus. Stellen wir uns einmal folgende Ausgangssituation am Beispiel eines betriebswirtschaftlichen Szenarios vor: Wir benötigen ein SSIS-Paket zum Import der monatlichen Verteilungsschlüssel von Hauptkostenstellen auf Nebenkostenstellen. Um die Richtigkeit der Daten zu gewährleisten, prüfen wir diese Verteilung, bevor die Daten importiert [...]]]></description>
			<content:encoded><![CDATA[<p>Die Konfigurationsmöglichkeiten für das Transaktionsverhalten in SSIS Paketen reichen in bestimmten Fällen nur bedingt aus. Stellen wir uns einmal folgende Ausgangssituation am Beispiel eines betriebswirtschaftlichen Szenarios vor: Wir benötigen ein SSIS-Paket zum Import der monatlichen Verteilungsschlüssel von <strong>Hauptkostenstellen</strong> auf <strong>Nebenkostenstellen</strong>. Um die Richtigkeit der Daten zu gewährleisten, prüfen wir diese Verteilung, bevor die Daten importiert werden. Wird eine <strong>Hauptkostenstelle</strong> nicht zu 100% verteilt, müssen wir auf diesen Fall reagieren.  <br />
<span id="more-217"></span><br />
<br\><br />
Hierbei wird ersichtlich, dass es sich nicht um einen technischen Fehler handelt (der etwa unser Paket zum Absturz bringt), sondern um einen <strong>logischen</strong> <strong>Fehler</strong>, der bei Nicht-Berücksichtigung für größere Schwierigkeiten nach dem Import aufgrund inhaltlich fehlerhafter Daten sorgen kann. Aber auch unter Berücksichtigung der Problematik stellt sich die Frage:<br />
<br\><br />
Macht es Sinn, einen nur teilweise erfolgreichen Import durchzuführen?<br />
<br\><br />
Diese Frage lässt sich nicht pauschal beantworten, da je nach Szenario ein bestimmtes Verhalten erwünscht sein kann. Im produktiven Einsatz würde sie höchstwahrscheinlich mit einem klaren „nein“ beantwortet werden, hingegen kann ein unvollständiger Import im Entwicklungs- und Testbetrieb in bestimmten Fällen durchaus sinnvoll sein.<br />
<br\><br />
Für genau solche Anforderungen haben wir ein Design-Pattern entwickelt, welches uns ermöglicht, ein transaktions-ähnliches Verhalten für inhaltliche, also nicht technische, Fehler innerhalb eines SSIS-Pakets per „Schalter“ ein- und ausschalten zu können.<br />
Dieses Verfahren werde ich Ihnen nun Schritt für Schritt vorstellen.<br />
<br\><br />
Am Ende des Beitrags finden Sie eine Übersicht der fertigen Lösung, die Ihnen hilft, das Gesamtbild nachzuvollziehen.<br />
<br\><br />
Werfen wir zunächst einen Blick auf die zu importierenden Quelldaten:<br />
<br\><br />
<img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/RR_Transaktionen/01.png" alt="" /><br />
<br\><br />
Die Hauptkostenstelle <strong>4712 </strong>wird nicht zu 100% verteilt &#8211; auf diesen Fall möchten wir reagieren. Dazu gehen wir wie folgt vor:<br />
<br\><br />
Zu Beginn wird dem Quelldatenfluss eine neue Spalte <em>JoinColumn </em>mithilfe der <em>DerivedColumn </em>- Komponente (dt.: abgeleitete Spalte) hinzugefügt.<br />
Den Wert setzten wir auf 0.<br />
<br\><br />
<img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/RR_Transaktionen/02.png" alt="" /><br />
<br\><br />
Dieses „Flag“ dient im späteren Verlauf dazu, eine in jedem Fall wahre Join-Bedingung zu erzeugen, da die Integration Services standardmäßig keinen <em>Cross-Join</em> unterstützen. Hintergrund ist, dass nach der Fehlerüberprüfung <strong>aller</strong> Zeilen an jeder <strong>einzelnen</strong> Zeile abzulesen sein soll, ob irgendwann ein ungültiger Datensatz aufgetreten ist.<br />
<br\><br />
Mithilfe eines <em>Conditional Split</em> (dt.: bedingtes Teilen) trennen wir die gültigen (ID 2 und 3) von den ungültigen Datensätzen (ID 5 und 6).<br />
<br\><br />
<em>Abhängig von Ihren Anforderungen, können sich in diesem Bereich auch mehrere komplexe, miteinander verkettete Operationen ansiedeln, um die gewünschte Trennung zwischen „gültig“ und „ungültig“ zu realisieren.</em><br />
<br\><br />
Am Ausgang der ungültigen Datensätze führen wir ein <em>RowSampling</em> (dt.: Zeilenstichprobe) mit folgenden Einstellungen durch:</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/RR_Transaktionen/03.png" alt="" /><br />
<br\><br />
<img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/RR_Transaktionen/04.png" alt="" /><br />
<br\><br />
Das <em>RowSampling</em> bewirkt, dass entweder <strong>keine oder maximal eine Zeile</strong> weitergeleitet werden. Es dient somit lediglich als Indikator, ob ungültige Datensätze aufgetreten sind oder nicht, unabhängig von ihrer Anzahl.<br />
<br\><br />
Als nächstes legen wir eine neue Variable namens <em>FailOnError </em>vom Datentyp <em>Int32</em> an. Diese Variable entspricht dem oben angekündigten Schalter. Mit ihrer Hilfe schalten wir das Transaktionsverhalten ein oder aus.<br />
<br\><br />
<em>Denkbar wäre hier ein erweitertes Szenario, in dem man durch Paketkonfiguration den Wert der Variable außerhalb des Pakets setzen kann (z.B. Web-Frontend).</em><br />
<br\><br />
In unserem Fall setzten den Wert initial auf <em>0 – </em>das Transaktionsverhalten ist somit vorerst ausgeschaltet.<br />
<br\><br />
<img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/RR_Transaktionen/05.png" alt="" /><br />
<br\><br />
Nun fügen wir eine weitere Spalte Namens <em>ErrorFlag </em>mithilfe einer<em> DerivedColumn </em>am Ausgang der Zeilenstichprobe hinzu. Den Wert belegen wir mit dem der <em>FailOnError</em>-Variable, die je nach Konfiguration den Zustand 0 oder 1 hat<strong> ODER </strong>aber auch<strong> </strong><em>NULL</em><strong> </strong>sein kann, nämlich dann, wenn keine ungültigen Datensätze identifiziert wurden.</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/RR_Transaktionen/06.png" alt="" /><br />
<br\><br />
Im nächsten Schritt sortieren wir die Daten mithilfe der Sort-Komponente. Dieser Schritt ist notwendig, da die darauffolgende <em>Merge-Join</em> &#8211; Komponente nur mit Daten funktioniert, welche die Eigenschaft <em>isSorted = true</em> besitzen (diese Eigenschaft wird u.a. von der <em>Sort</em>-Komponente gesetzt).</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/RR_Transaktionen/07.png" alt="" /><br />
<br\><br />
Anschließend „joinen<em>“</em> wir den Wert des <em>ErrorFlags</em> mithilfe eines <em>Merge-Joins</em> (dt.: Zusammenführungsjoin) und unserer <em>JoinColumn</em> an die gültigen Datensätze (auch hier ist das „Vorschalten“ der <em>Sort</em>-Komponente notwendig).<br />
<br\><br />
Die Konfiguration des <em>Merge-Join’s </em>sieht dabei folgendermaßen aus:</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/RR_Transaktionen/08.png" alt="" /><br />
<br\><br />
Am Ausgang des <em>Merge-Join‘s</em> sehen unsere Daten dann so aus:</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/RR_Transaktionen/09.png" alt="" /><br />
<br\><br />
Die letzte und gleichzeitig entscheidende Komponente ist ein <em>Conditional Split</em> mit folgender Konfiguration:</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/RR_Transaktionen/10.png" alt="" /><br />
<br\><br />
An dieser Stelle „trennt sich die Spreu vom Weizen“ – Abhängig davon, welchen Wert die <em>FailOnError</em> Variable hat, werden hier <strong>entweder</strong> alle gültigen <strong>ODER</strong> keine Datensätze weitergeleitet.<br />
<br\><br />
Nicht vergessen: ändern Sie den Wert der Variable von 0 auf <strong>1</strong>, um das Transaktionsverhalten <strong>ein</strong>zuschalten.<br />
<br\><br />
Zu guter Letzt das versprochene Gesamtbild:</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/RR_Transaktionen/11.png" alt="" /></p>
]]></content:encoded>
			<wfw:commentRss>http://www.ixto.de/blog/sql-server-2008/integration-services-2008/ssis-error-handling-design-pattern-transaktionen/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MS-DOS lebt – im ForEachLoop-SSIS Container!</title>
		<link>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/ms-dos-lebt-%e2%80%93-im-foreachloop-ssis-container-2/</link>
		<comments>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/ms-dos-lebt-%e2%80%93-im-foreachloop-ssis-container-2/#comments</comments>
		<pubDate>Thu, 29 Apr 2010 21:32:12 +0000</pubDate>
		<dc:creator>Chris Jacob</dc:creator>
				<category><![CDATA[Integration Services 2005]]></category>
		<category><![CDATA[Integration Services 2008]]></category>
		<category><![CDATA[SQL Server 2005]]></category>
		<category><![CDATA[SQL Server 2008]]></category>

		<guid isPermaLink="false">http://www.ixto.de/blog/?p=91</guid>
		<description><![CDATA[Wer hätte gedacht dass man heutzutage noch über die Vermächtnisse einer 20 Jahre alten Software stolpern kann – und das in den Integration Services!
Aber beginnen wir von vorne: immer wenn die Bearbeitung mehrere gleichartiger Dateien mittels SSIS gefragt ist, kommt früher oder später der ForEach-Loop-Container zum Einsatz. Damit lässt sich z.B. recht komfortabel über Dateien [...]]]></description>
			<content:encoded><![CDATA[<p>Wer hätte gedacht dass man heutzutage noch über die Vermächtnisse einer 20 Jahre alten Software stolpern kann – und das in den Integration Services!</p>
<p>Aber beginnen wir von vorne: immer wenn die Bearbeitung mehrere gleichartiger Dateien mittels SSIS gefragt ist, kommt früher oder später der ForEach-Loop-Container zum Einsatz. Damit lässt sich z.B. recht komfortabel über Dateien mit einer bestimmten Namensstruktur innerhalb eines Ordners iterieren. So könnte ein mögliches Importszenario vorsehen, dass mehrere Exceldateien in einem Ordner ausgelesen werden sollen – den ForEach-Loop-Container könnte man dann wie folgt konfigurieren:</p>
<p><span id="more-91"></span><br />
<br\><br />
Soweit so gut, der Rest des Paketes könnte dann wie folgt aussehen. Innerhalb des Loops wird eine Variable mit dem aktuellen Dateipfad ausgelesen um alle gefundenen Dateien nach Beendigung des Containers in einer MessageBox anzuzeigen:<br />
<br\><br />
<img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/042910_2132_MSDOSlebti2.png" alt="" /><br />
<br\><br />
Ich habe bisher den Inhalt des betroffenen Ordners unterschlagen, hier ist er:<br />
<br\><br />
<img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/042910_2132_MSDOSlebti3.png" alt="" /><br />
<br\><br />
Und siehe da: unser Paket findet zwei Exceldateien – wunderbar! Oder doch nicht? Störend an dieser Stelle könnte die Excel-Datei sein, die im neuen 2007er Format gespeichert wurde. Diese hat die Endung .xlsx! Eigentlich ganz praktisch, denn die beiden Exceltypen unterscheiden sich grundlegend und erfordern ggf. eine differenzierte Behandlung innerhalb der SSIS. Weitere prüfende Blicke in die Konfiguration des ForEach-Loops lassen letztlich nur einen Schluss zu: egal wie man es dreht und wendet, der Ausdruck im &#8220;Files&#8221;-Feld des Editors lässt sich nicht dahingehend ändern dass er nur .xls Dateien erwischt (was man eigentlich erwarten würde) – der Ausdruck verhält sich also wie *.xls*!<br />
<br\><br />
An dieser Stelle wirken die Books Online einmal mehr erhellend und decken auf, dass es sich quasi um erwünschtes Verhalten handelt – der Ausdruck verhält sich nämlich wie beim dir-Befehl in Windows (siehe: <a href="http://msdn.microsoft.com/en-us/library/ms187670.aspx">http://msdn.microsoft.com/en-us/library/ms187670.aspx</a>). Dieser wiederum ist aus Kompatibilitätsgründen so gestrickt, dass er nur drei Zeichen der Dateinamenerweiterung betrachtet – die gute alte 8.3-Dateinamenbeschränkung aus MS-DOS Zeiten schlägt hier also wieder einmal zu!<br />
<br\><br />
Eine Differenzierung nach Dateierweiterung muss also später geschehen, die könnte dann z.B. so aussehen: innerhalb des ForEach-Loop-Containers fügen wir eine Rangfolgeeinschränkung (zu deutsch: einen Pfeil) ein und binden daran eine SSIS-Expression. Diese prüft, ob die letzten drei Buchstaben des ausgelesenen Dateinamens auch tatsächlich x, l und s sind:<br />
<br\><br />
<img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/042910_2132_MSDOSlebti4.png" alt="" /><br />
<br\><br />
<img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/042910_2132_MSDOSlebti5.png" alt="" /><br />
<br\><br />
So modifiziert, verrichtet das Paket ordnungsgemäß seinen Dienst und findet tatsächlich nur eine Excel-Datei:<br />
<br\><br />
<img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/042910_2132_MSDOSlebti6.png" alt="" /><br />
<br\><br />
Da kann man sich natürlich schon fragen ob es sich um ein Bug oder ein Feature handelt, aber genau wegen diesen Ecken und Kanten lieben wir die Integration Services so sehr – man entdeckt eben immer wieder etwas Neues!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/ms-dos-lebt-%e2%80%93-im-foreachloop-ssis-container-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Codeplex Perlen &#8211; heute: Enhanced SSIS Execute Package Task</title>
		<link>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/codeplex-perlen-heute-enhanced-ssis-execute-package-task/</link>
		<comments>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/codeplex-perlen-heute-enhanced-ssis-execute-package-task/#comments</comments>
		<pubDate>Wed, 31 Mar 2010 13:33:26 +0000</pubDate>
		<dc:creator>Chris Jacob</dc:creator>
				<category><![CDATA[Integration Services 2005]]></category>
		<category><![CDATA[Integration Services 2008]]></category>
		<category><![CDATA[SQL Server 2005]]></category>
		<category><![CDATA[SQL Server 2008]]></category>

		<guid isPermaLink="false">http://www.ixto.de/blog/?p=81</guid>
		<description><![CDATA[Divide et impera – das von Machiavelli vor gut 500 Jahren geprägte Prinzip des Teilen und Herrschens ist heutzutage eine allseits beliebte Technik zur Problemlösung. Auch in den Integration Services finden sich eine Menge Möglichkeiten, um große Aufgaben in kleinere, übersichtliche Schritte zu unterteilen. Beispiele sind unterschiedliche Datenflüsse für unterschiedliche Aufgaben, die Strukturierung mittels Sequenzcontainern [...]]]></description>
			<content:encoded><![CDATA[<p>Divide et impera – das von Machiavelli vor gut 500 Jahren geprägte Prinzip des Teilen und Herrschens ist heutzutage eine allseits beliebte Technik zur Problemlösung. Auch in den Integration Services finden sich eine Menge Möglichkeiten, um große Aufgaben in kleinere, übersichtliche Schritte zu unterteilen. Beispiele sind unterschiedliche Datenflüsse für unterschiedliche Aufgaben, die Strukturierung mittels Sequenzcontainern und nicht zuletzt die Möglichkeit, aus einem SSIS Paket andere SSIS Pakete aufzurufen.</p>
<p><span id="more-81"></span><br />
<br\><br />
Doch mit steigender Komplexität der Pakete (Verbindungsmanager, Paketvariablen) und ausgiebiger Nutzung der Paketkonfiguration gelangt man mit den SSIS-Bordmitteln einmal mehr an die Grenzen des Wartbaren. Die Integration Services bieten von Haus aus die Möglichkeit, andere SSIS Pakete aufzurufen, doch die Übergabe von Variablen und Verbindungsmanagern an das &#8220;Kindpaket&#8221; gestaltet sich sehr aufwändig (viel Expression-Hacking). Die Möglichkeit, Rückgabewerte vom Kindpaket zu empfangen fehlt gar gänzlich.<br />
<br\><br />
In diese Nische stürzt sich ein Codeplex-Projekt, welches ich im Folgenden kurz vorstellen möchte: der &#8220;Enhanced SSIS Execute Package Task&#8221;. Unter <a href="http://ssisexec.codeplex.com/">http://ssisexec.codeplex.com/</a> ist das noch recht unbekannte Projekt zu finden (derzeit für SSIS 2005 und SSIS 2008 R2 – siehe &#8220;Downloads&#8221;). Hilfreich bei der Installation (es wird eine dll und ein schmales readme geliefert) ist folgender Eintrag in den Books Online: <a href="http://msdn.microsoft.com/en-us/library/ms403356.aspx">http://msdn.microsoft.com/en-us/library/ms403356.aspx</a> Hat man die Komponente erfolgreich installiert lässt sie sich über einen Rechtsklick in die Toolbox -&gt; Choose Items in die Liste der Kontrollfluss Tasks aufnehmen.<br />
<br\><br />
<img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/enhanced_1.png" alt="" /><br />
<br\><br />
Die Komponente bietet eine grafische Oberfläche zum Mappen der Variablen bzw. Verbindungsmanagern zwischen den Paketen. Diese können entweder im Dateisystem oder in der MSDB serverseitig gespeichert sein. Somit lassen sich recht schnell und komfortabel Konfigurationen zwischen Paketen übergeben. Wie das aussehen könnte sieht man in folgendem screenshot: Hier wird von einem Steuerpaket, welches den Pfad zu einer Exceldatei via Paketkonfiguration erhält, ein Kindpaket aufgerufen wobei der Verbindungsmanager zwischen den Paketen übergeben wird.<br />
<br\><br />
<img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/enhanced_2.png" alt="" /><br />
<br\><br />
<strong>Hinweis:</strong> Das Projekt hat derzeit noch den <strong>Alpha-Status</strong> – vom Einsatz in einer Produktivumgebung rate ich also dringend ab! Laut Aussage des Authors scheint es aber stabil zu laufen und dieser freut sich mit Sicherheit über ausgiebiges feedback in Form von Kommentaren oder Anmerkungen. In diesem Sinne: happy testing!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/codeplex-perlen-heute-enhanced-ssis-execute-package-task/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Excel und führende Nullen vs. SSIS</title>
		<link>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/excel-und-fuhrende-nullen-vs-ssis/</link>
		<comments>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/excel-und-fuhrende-nullen-vs-ssis/#comments</comments>
		<pubDate>Sat, 27 Feb 2010 08:03:47 +0000</pubDate>
		<dc:creator>Chris Jacob</dc:creator>
				<category><![CDATA[Integration Services 2005]]></category>
		<category><![CDATA[Integration Services 2008]]></category>
		<category><![CDATA[SQL Server 2005]]></category>
		<category><![CDATA[SQL Server 2008]]></category>

		<guid isPermaLink="false">http://www.ixto.de/blog/?p=79</guid>
		<description><![CDATA[Wer kennt sie nicht: die Problematik der führenden Nullen in Excel. Um beispielsweise Postleitzahlen mit führenden Nullen korrekt darzustellen, bedarf es in aller Regel Einiges an Formatierungsaufwand, da Excel Zahlen gerne als numerischen Wert interpretiert. Befüllt man nun mit den SSIS ein Excel-Ziel, gehen führende Nullen beim ersten Öffnen der Excel-Mappe verloren, ganz gleich ob [...]]]></description>
			<content:encoded><![CDATA[<p>Wer kennt sie nicht: die Problematik der führenden Nullen in Excel. Um beispielsweise Postleitzahlen mit führenden Nullen korrekt darzustellen, bedarf es in aller Regel Einiges an Formatierungsaufwand, da Excel Zahlen gerne als numerischen Wert interpretiert. Befüllt man nun mit den SSIS ein Excel-Ziel, gehen führende Nullen beim ersten Öffnen der Excel-Mappe verloren, ganz gleich ob man z.B. eine Spalte PLZ als Text oder Zahl durchleitet.</p>
<p><span id="more-79"></span><br />
<br\><br />
Abhilfe schafft hier ein kleiner Trick, der in den screenshots unten zu sehen ist. Mittels des Tasks &#8220;Abgeleitete Spalte&#8221; erstellt man eine neue Textspalte, deren Inhalt die Spalte mit den führenden Nullen eingebettet in Hochkommata und ein vorangestelltes Gleichheitszeichen ist (Escape-Sequenz beachten!).<br />
<br\><br />
<img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/030110_0803_Excelundfhr1.png" alt="" /><br />
<br\><br />
<img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/030110_0803_Excelundfhr2.png" alt="" /><br />
<br\><br />
Als Resultat interpretiert Excel fortan den Wert in der Spalte als Formel und die führende Null wird korrekt dargestellt.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/excel-und-fuhrende-nullen-vs-ssis/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Range-Lookups mit den Integration Services – Teil III</title>
		<link>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/range-lookups-mit-den-integration-services-%e2%80%93-teil-iii/</link>
		<comments>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/range-lookups-mit-den-integration-services-%e2%80%93-teil-iii/#comments</comments>
		<pubDate>Thu, 26 Nov 2009 13:41:58 +0000</pubDate>
		<dc:creator>Markus Schechner</dc:creator>
				<category><![CDATA[Integration Services 2005]]></category>
		<category><![CDATA[Integration Services 2008]]></category>
		<category><![CDATA[SQL Server 2005]]></category>
		<category><![CDATA[SQL Server 2008]]></category>
		<category><![CDATA[Binary Search]]></category>
		<category><![CDATA[Caching]]></category>
		<category><![CDATA[fortlaufende Werte]]></category>
		<category><![CDATA[Lookup]]></category>
		<category><![CDATA[SSIS]]></category>

		<guid isPermaLink="false">http://www.ixto.de/blog/sql-server-2005/integration-services-2005/range-lookups-mit-den-integration-services-%e2%80%93-teil-iii/</guid>
		<description><![CDATA[In den vorangegangenen beiden Teilen dieses Artikels (Teil I, Teil II) haben wir uns mit den Möglichkeiten des Range-Lookups beschäftigt, die ohne weitere Programmierung oder zusätzliche Komponenten mit reinen SSIS-Boardmitteln realisiert werden können. Nun wollen wir uns mit einer dritten Variante beschäftigen, um in unserem Beispielszenario die Kunden nach Ihrem Einkommen in die bereits bekannten [...]]]></description>
			<content:encoded><![CDATA[<p>In den vorangegangenen beiden Teilen dieses Artikels (<a href="http://www.ixto.de/blog/sql-server-2005/integration-services-2005/range-lookups-mit-den-integration-services-%e2%80%93-teil-i-2/">Teil I</a>, <a href="http://www.ixto.de/blog/sql-server-2005/integration-services-2005/range-lookups-mit-den-integration-services-%e2%80%93-teil-ii/">Teil II</a>) haben wir uns mit den Möglichkeiten des Range-Lookups beschäftigt, die ohne weitere Programmierung oder zusätzliche Komponenten mit reinen SSIS-Boardmitteln realisiert werden können. Nun wollen wir uns mit einer dritten Variante beschäftigen, um in unserem Beispielszenario die Kunden nach Ihrem Einkommen in die bereits bekannten Einkommensgruppen einzuteilen:</p>
<p><span id="more-74"></span><br />
<br\><br />
<img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/112609_1341_RangeLookup1.png" alt="" /></p>
<p><span style="text-decoration: underline;"><strong>Lösung mittels Script Task – Wunderwaffe &#8220;Binary Search&#8221;<br />
</strong></span><br />
<br\><br />
Wenn einen die mitgelieferten Standardkomponenten der Integration Services mal wieder nicht so richtig ans Ziel bringen, dann gibt es ja zum Glück immer noch den Script Task, den wir auch dazu nutzen können, um einen eigenen Lookup zu realisieren. Und genau das werden wir jetzt tun. Script Transformation in unseren Datenfluss gezogen und schon kann es mit der Definition der Eingabespalten beginnen. In unserem Fall reicht das YearlyIncome aus:</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/112609_1341_RangeLookup2.png" alt="" /><br />
<br\><br />
Danach müssen wir dann noch die zugehörige Ausgabespalte definieren, die dann später unsere Gruppen-ID aufnehmen soll:</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/112609_1341_RangeLookup3.png" alt="" /><br />
<br\><br />
Da ich die möglichen Gruppen direkt im Script-Task aus der Datenbank lesen möchte (es gibt auch andere, durchaus sauberere Wege), benötige ich auch noch einen Verbindungsmanager (der Einfachheit halber mit dem .NET Provider für SQL Server):</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/112609_1341_RangeLookup4.png" alt="" /><br />
<br\><br />
Nun kann ich mir noch die Programmiersprache auswählen (ich nehm mal C#) und endlich mit der eigentlichen Arbeit beginnen.<br />
<br\><br />
Als erstes benötige ich einen gefüllten Cache, der die möglichen Ausprägungen meiner Gehaltsgruppen aufnehmen kann. Diesen definiere ich als globale generische Liste einer eigens dafür gedachten Klasse Namens &#8220;NumericRangeItem&#8221;:</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/112609_1341_RangeLookup5.png" alt="" /><br />
<br\><br />
Im PreExecute übernehme ich dann den Freigegebenen Verbindungsmanager und fülle einmalig den besagten Cache:</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/112609_1341_RangeLookup6.png" alt="" /><br />
<br\><br />
Im FillCache() werden dann die oben gezeigten Zeilen aus der Datenbank gelesen und für jede Zeile ein neues NumericRangeItem in den Cache übernommen. Das NumericRangeItem ist dabei im wesentlichen ein Key-Value-Pair, wobei IncomeFrom als Key und die zugehörige IncomeGroupID als Value übernommen wird :</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/112609_1341_RangeLookup7.png" alt="" /><br />
<br\><br />
Besonderheit ist, dass es die Schnittstelle IComparable implementiert. Dazu muss die Klasse also eine Methode namens CompareTo enthalten, die aber auch ziemlich simpel ist.</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/112609_1341_RangeLookup8.png" alt="" /><br />
<br\><br />
Damit wird lediglich festgelegt, dass beim Vergleich zweier NumericRangeItems die Schlüssel (also die Einkommensgrenze) miteinander verglichen werden.<br />
<br\><br />
Ergebnis ist, dass ich nach dem PreExecute alle Einkommensgrenzen als generische Liste vergleichbarer Elemente im Speicher habe. Belohnt werde ich für diesen Aufwand dann damit, dass eine solche Liste eine Methode namens BinarySearch() zur Verfügung stellt, die wir dann für den eigentlichen Lookup nutzen können:</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/112609_1341_RangeLookup9.png" alt="" /><br />
<br\><br />
Für jede Zeile die ein Einkommen enthält wird die Funktion GetID aufgerufen, die einen numerischen Wert übergeben bekommt und das dazu passende NumericRangeItem zurückgibt. Der zu suchende Wert wird dabei selbst in einem NumericRangeItem verpackt, damit der Vergleich funktioniert. Und dann kann BinarySearch() die eigentliche Arbeit übernehmen. Dabei wird immer in die Mitte der Wertmenge geschaut und überprüft ob der dort vorhandene Wert größer oder kleiner als der Vergleichswert ist und dann mit der jeweils passenden Hälfte weitergearbeitet. Dadurch kann man mit BinarySerach auch in großen Datenmengen sehr schnell suchen (im schlimmsten Fall werden log<sub>2</sub>(<em>N</em>) + 1 Iterationen benötigt =&gt; zum Durchsuchen von 1 Mio Datensätzen werden höchstens 20 Versuche gebraucht). Was das Ganze für unser Szenario aber erst nutzbar macht, ist, dass auch für Werte, die nicht gefunden werden können ein Ergebnis geliefert wird.<br />
<br\><br />
Mathematisch ist dieser negative Rückgabewert das Komplement des nächstgrößeren Index. Da wir über die Untergrenzen suchen (IncomeFrom) erhalten wir also mit if (iScore &lt; 0) iScore = (short)(~iScore &#8211; 1); genau das Ergebnis, dass wir für unseren RangeLookup brauchen.<br />
<br\><br />
Das Ergebnis sieht dann wie zu hoffen war folgendermaßen aus:</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/112609_1341_RangeLookup10.png" alt="" /><br />
<br\><br />
Dieses Bild haben wir so ähnlich bereits im Teil I gesehen, nur dass das Ergebnis diesmal in nicht mal einem Sechstel der Zeit berechnet war. Bei größeren Datenmengen verstärkt sich dieser Effekt sogar noch weiter. In unserem kleinen Beispiel benötigt der Script Task noch fast die Hälfte für das PreExecute, in dem ja auch der Cache gefüllt werden muss. Wenn wir aber nicht mehr 18k sondern ein paar Millionen Datensätze durch die Pipeline schicken, fällt dieser Overhead nicht mehr ins Gewicht.<br />
<br\><br />
Mit der Wunderwaffe BinarySearch brauchen wir also in Zukunft keine Angst mehr vor Range-Lookups bei großen Datenmengen zu haben.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/range-lookups-mit-den-integration-services-%e2%80%93-teil-iii/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Range-Lookups mit den Integration Services – Teil II</title>
		<link>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/range-lookups-mit-den-integration-services-%e2%80%93-teil-ii/</link>
		<comments>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/range-lookups-mit-den-integration-services-%e2%80%93-teil-ii/#comments</comments>
		<pubDate>Fri, 30 Oct 2009 13:01:22 +0000</pubDate>
		<dc:creator>Markus Schechner</dc:creator>
				<category><![CDATA[Integration Services 2005]]></category>
		<category><![CDATA[Integration Services 2008]]></category>
		<category><![CDATA[SQL Server 2005]]></category>
		<category><![CDATA[SQL Server 2008]]></category>
		<category><![CDATA[Caching]]></category>
		<category><![CDATA[Common Table Expression]]></category>
		<category><![CDATA[CTE]]></category>
		<category><![CDATA[fortlaufende Werte]]></category>
		<category><![CDATA[Lookup]]></category>
		<category><![CDATA[SSIS]]></category>

		<guid isPermaLink="false">http://www.ixto.de/blog/sql-server-2005/integration-services-2005/range-lookups-mit-den-integration-services-%e2%80%93-teil-ii/</guid>
		<description><![CDATA[Wie im Teil I versprochen wollen wir uns nun mit einer weiteren Möglichkeit für Range-Lookups mit den Integration Services auseinandersetzen.
Dazu zur Erinnerung noch einmal unsere Quelltabelle, aus der wir die zugehörigen IDs ermitteln wollen:



Lösung mittels Lookup – Mit Caching


Wie wir festgestellt haben, verliert der Lookup bei ausgeschaltetem Caching enorm an Geschwindigkeit. Alternativ könnte man also [...]]]></description>
			<content:encoded><![CDATA[<p>Wie im <a href="http://www.ixto.de/blog/sql-server-2005/integration-services-2005/range-lookups-mit-den-integration-services-%e2%80%93-teil-i-2/trackback/">Teil I</a> versprochen wollen wir uns nun mit einer weiteren Möglichkeit für Range-Lookups mit den Integration Services auseinandersetzen.</p>
<p>Dazu zur Erinnerung noch einmal unsere Quelltabelle, aus der wir die zugehörigen IDs ermitteln wollen:</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/102709_1200_RangeLookup1.png" alt="" /></p>
<p><span id="more-73"></span><br />
<br\><br />
<span style="text-decoration: underline;"><strong>Lösung mittels Lookup – Mit Caching<br />
</strong></span><br />
<br\><br />
Wie wir festgestellt haben, verliert der Lookup bei ausgeschaltetem Caching enorm an Geschwindigkeit. Alternativ könnte man also versuchen, den Range-Lookup mit aktiviertem Caching zu realisieren. Da der Lookup dann aber nur genaue Übereinstimmungen als Treffer wertet, muss für jeden möglichen Wert eine Zeile in der Lookup Tabelle existieren. Angenommen die Einkommen sind im beschriebenen Fall als volle Eurobeträge gespeichert, ließe sich das mit folgender rekursiven Common Table Expression (CTE) realisieren:<br />
<br\><br />
<span style="font-family: Courier New; font-size: 10pt;"><span style="color: blue;">WITH</span> IncomeGroups<span style="color: gray;">(</span>IncomeGroupID<span style="color: gray;">,</span>Income<span style="color: gray;">,</span> IncomeTo<span style="color: gray;">)<br />
</span></span></p>
<p><span style="font-family: Courier New; color: blue; font-size: 10pt;">AS<br />
</span></p>
<p><span style="font-family: Courier New; color: gray; font-size: 10pt;">(<br />
</span></p>
<p><span style="font-family: Courier New; font-size: 10pt;"><span style="color: blue;">SELECT</span>    [IncomeGroupID]<span style="color: gray;">,</span>[IncomeFrom] <span style="color: blue;">as</span> Income<span style="color: gray;">,</span> [IncomeTo]<br />
</span></p>
<p><span style="font-family: Courier New; font-size: 10pt;"><span style="color: blue;">FROM</span>    dbo<span style="color: gray;">.</span>_MATCH_IncomeGroup<br />
</span></p>
<p><span style="font-family: Courier New; font-size: 10pt;"><span style="color: blue;">WHERE</span>    IncomeGroupID <span style="color: gray;">&lt;</span> 7<br />
</span></p>
<p><span style="font-family: Courier New; font-size: 10pt;"><span style="color: blue;">UNION</span><br />
<span style="color: gray;">ALL<br />
</span></span></p>
<p><span style="font-family: Courier New; font-size: 10pt;"><span style="color: blue;">SELECT</span> [IncomeGroupID]<span style="color: gray;">,</span> Income <span style="color: gray;">+</span> 1<span style="color: gray;">,</span> [IncomeTo]<br />
</span></p>
<p><span style="font-family: Courier New; font-size: 10pt;"><span style="color: blue;">FROM</span> IncomeGroups<br />
</span></p>
<p><span style="font-family: Courier New; font-size: 10pt;"><span style="color: blue;">WHERE</span> Income <span style="color: gray;">+</span> 1 <span style="color: gray;">&lt;=</span> IncomeTo<br />
</span></p>
<p><span style="font-family: Courier New; color: gray; font-size: 10pt;">)<br />
</span></p>
<p><span style="font-family: Courier New; font-size: 10pt;"><span style="color: blue;">SELECT</span> Income<span style="color: gray;">,</span> IncomeGroupID<br />
</span></p>
<p><span style="font-family: Courier New; font-size: 10pt;"><span style="color: blue;">FROM</span> IncomeGroups<br />
</span></p>
<p><span style="font-family: Courier New; font-size: 10pt;"><span style="color: blue;">ORDER</span><br />
<span style="color: blue;">BY</span> 1<span style="color: gray;">,</span>2<br />
</span></p>
<p><span style="font-family: Courier New; font-size: 10pt;"><span style="color: blue;">OPTION </span><span style="color: gray;">(</span>MAXRECURSION 0<span style="color: gray;">)</span></span><br />
<br\><br />
Dieses Statement liefert im Management das folgende Ergebnis:</p>
<p><img src="http://blog.ixtoprod.s15244651.onlinehome-server.info/blogbilder/102709_1200_RangeLookup2.png" alt="" /><br />
<br\><br />
Dies könnte als Quelle für den Lookup Task verwendet werden der dann wie jeder andere gewöhnliche Lookup konfiguriert werden kann.<br />
<br\><br />
Anschließend müsste dann die oberste Grenze, die in der Quelle bewusst ausgelassen wurde (denn 999.999.999 Datensätze wollte ich nun wirklich nicht erzeugen) per Abgeleitete Spalte (derived column) gesetzt werden.<br />
<br\><br />
Für Situationen in denen sehr viele Datensätze verarbeitet werden müssen, könnte dies eine gute Alternative zu der Variante ohne Caching sein. Es wird aber auch schnell klar, dass auch dieses Verfahren seine Grenzen hat. Für eine Aufteilung in Altersklassen von Kunden, für die es nur sehr wenige Ausprägungen in den Quelldaten gibt, ist dies wahrscheinlich die beste Lösung. Wenn es aber darum geht Firmenumsätze, die in die Milliarden gehen, in Gruppen einzuteilen, wird sowohl die Abfragezeit der Rekursion, als auch der benötigte Speicherplatz den Rahmen sprengen. Spätestens aber wenn die Grenzen nicht mehr mit ganzen Zahlen definierbar sind, ist dann wirklich Schluss.<br />
<br\><br />
Um auch für diese Situationen gerüstet zu sein, wird sich der nächste und vermutlich letzte Teil dieses Artikels mit der dritten Alternative, dem Range-Lookup mittels Script Task auseinandersetzen.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.ixto.de/blog/sql-server-2005/integration-services-2005/range-lookups-mit-den-integration-services-%e2%80%93-teil-ii/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

