{"id":253,"date":"2014-12-31T18:20:55","date_gmt":"2014-12-31T17:20:55","guid":{"rendered":"http:\/\/www.csopro.de\/biblog\/?p=253"},"modified":"2014-12-31T18:23:00","modified_gmt":"2014-12-31T17:23:00","slug":"null-werte-bei-lookups","status":"publish","type":"post","link":"https:\/\/www.csopro.de\/biblog\/2014\/12\/null-werte-bei-lookups\/","title":{"rendered":"NULL-Werte bei Lookups"},"content":{"rendered":"<p>Lookups kommen bei SSIS-Data Flows ziemlich h\u00e4ufig vor. Zum Beispiel m\u00fcssen bei der Verarbeitung von Fakten-S\u00e4tzen enthaltene Dimensions-Referenzen umgesetzt werden. Zum Beispiel k\u00f6nnte es sein, dass Deutschland im Quellsystem \u2018D\u2019 ist, im DWH aber mit 49 abgebildet wird. <\/p>\n<p>Das ist mit Lookups nat\u00fcrlich ganz einfach.<\/p>\n<p>Nun kommt es aber vor, dass nicht alle Fakten eine Referenz auf ein Land haben m\u00fcssen, also NULL sind. Diese sollen dann auch NULL bleiben. <\/p>\n<p>Wie kann eine solche Aufgabe gel\u00f6st werden?<\/p>\n<p>Die Standard-L\u00f6sung wird so aussehen:<\/p>\n<p>Vor dem Lookup wird \u00fcber einen Conditional Split \u00fcberpr\u00fcft, ob das Land leer ist: <\/p>\n<ul>\n<li>Wenn ja, dann wird der Zielwert auch auf NULL gesetzt <br \/>(Daf\u00fcr muss nichts gemacht werden, da dies beim folgenden UNION ALL automatisch erfolgt)\n<li>Wenn nein, dann wird der Lookup durchgef\u00fchrt<\/li>\n<\/ul>\n<p>Danach werden beide Pfade \u00fcber einen UNION wieder zusammengef\u00fchrt:<\/p>\n<p><a href=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2014\/12\/image4.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2014\/12\/image_thumb4.png\" width=\"244\" height=\"242\"><\/a><\/p>\n<p>In meinem Beispiel sieht die Lookup-Quelle so aus:<\/p>\n<blockquote>\n<p>SELECT N&#8217;D&#8216; as Country, 49 as nummer<br \/>UNION ALL<br \/>SELECT N&#8217;A&#8216; as Country, 43 as nummer <br \/>UNION ALL<br \/>SELECT N&#8217;CH&#8216; as Country, 41 as nummer<\/p>\n<\/blockquote>\n<p>Diese Vorgehensweise hat mehrere Nachteile:<\/p>\n<ul>\n<li>\u00dcber das UNION ALL wird immer ein neuer Buffer angelegt, was insbesondere bei gro\u00dfen Data Flows unn\u00f6tig Speicher belegt (UNION ALL ist semi-blocking)<\/li>\n<li>Die L\u00f6sung ist einigerma\u00dfen komplex<\/li>\n<li>Falls eine Sortierung vorliegt, geht die Sortierung durch das UNION ALL verloren. Dies sollte nicht durch den Einsatz eines MERGE verhindert werden, da dies die Performance sehr negativ beeinflussen kann (vielleicht schreibe ich dazu mal einen eigenen Blog-Eintrag)<\/li>\n<\/ul>\n<p>Deswegen hatten wir nach einer besseren L\u00f6sung gesucht:<\/p>\n<p>Der erste Ansatz war folgender: Wir k\u00f6nnen beim Lookup Nicht-Treffer ignorieren und danach \u00fcberpr\u00fcfen, ob wir ein Mapping \u00fcbersehen haben und dann selbst einen Fehler schmei\u00dfen:<\/p>\n<p><a href=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2014\/12\/image5.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2014\/12\/image_thumb5.png\" width=\"644\" height=\"331\"><\/a>Der Conditional Split enth\u00e4lt folgende Bedingung:<\/p>\n<blockquote>\n<p>!(ISNULL(Country)) &amp;&amp; ISNULL(nummer)<\/p>\n<\/blockquote>\n<p>In der Skriptkompopnente wird ein Fehler mit folgendem Code erzeugt:<\/p>\n<blockquote>\n<p>public override void Input0_ProcessInputRow(Input0Buffer Row)<br \/>{<br \/>&nbsp;&nbsp;&nbsp; bool p=false;<br \/>&nbsp;&nbsp;&nbsp; ComponentMetaData.FireError(10, &#8222;Testkomponente&#8220;, &#8222;Country was not null, but could not be mapped to a country number&#8220;, &#8222;&#8220;, 0, out p);<br \/>}<\/p>\n<\/blockquote>\n<p>Dieser Ansatz hat folgenden Vorteil:<\/p>\n<ul>\n<li>Alle Komponenten sind synchron, ben\u00f6tigen also keine zus\u00e4tzlichen Buffer.<\/li>\n<\/ul>\n<p>&#8211; aber auch folgende Nachteile:<\/p>\n<ul>\n<li>Die L\u00f6sung ist noch komplexer<\/li>\n<li>Die L\u00f6sung ist un\u00fcbersichtlich und schlechter wartbar, da ohne die Skriptkomponente der Lookup falsch programmiert w\u00e4re. Das muss aber der wartende Mitarbeiter wissen. Au\u00dferdem ist ein Error Handling \u201cFehler ignorieren\u201d irref\u00fchrend.<\/li>\n<li>Skript-Komponenten haben selbst keinen Fehler-Output. Damit kann eine Skript-Komponente leider nicht mit der vorhin vorgestellten Quarant\u00e4ne-Idee automatisch korrigiert werden.<\/li>\n<\/ul>\n<p>Die <strong>beste L\u00f6sung<\/strong> ist somit eine ganz einfache:<\/p>\n<p>Wir&nbsp; stellen den Lookup wieder auf Fehler bei einem Nicht-Treffer. Wir erg\u00e4nzen die Quelle des Lookups um NULL-Werte, also in meinem Beispiel<\/p>\n<blockquote>\n<p>SELECT N&#8217;D&#8216; as Country, 49 as nummer<br \/>UNION ALL<br \/>SELECT N&#8217;A&#8216; as Country, 43 as nummer <br \/>UNION ALL<br \/>SELECT N&#8217;CH&#8216; as Country, 41 as nummer<br \/>UNION ALL<br \/>SELECT NULL, NULL<\/p>\n<\/blockquote>\n<p>bzw. allgemein<\/p>\n<blockquote>\n<p>SELECT * FROM &lt;lookup-tabelle&gt;<br \/>UNION ALL<br \/>SELECT NULL, NULL<\/p>\n<\/blockquote>\n<p>Dann sieht der Data Flow ganz einfach aus:<\/p>\n<p><a href=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2014\/12\/image6.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2014\/12\/image_thumb6.png\" width=\"216\" height=\"174\"><\/a><\/p>\n<p>Dies hat nat\u00fcrlich etliche Vorteile:<\/p>\n<ul>\n<li>Die L\u00f6sung ist super-einfach.<\/li>\n<li>Die L\u00f6sung ist nat\u00fcrlich synchron und somit sehr schnell.<\/li>\n<\/ul>\n<p>Allerdings funktioniert diese L\u00f6sung nur, wenn Full Cache eingestellt ist:<\/p>\n<p><a href=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2014\/12\/image7.png\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" title=\"image\" style=\"border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px\" border=\"0\" alt=\"image\" src=\"https:\/\/www.csopro.de\/biblog\/wp-content\/uploads\/2014\/12\/image_thumb7.png\" width=\"644\" height=\"304\"><\/a><\/p>\n<p>Dies ist aber eh die meistens verwendete (da performanteste) Variante.<\/p>\n<p>Die Ursache, warum die anderen Cache-Einstellungen nicht funktionieren, liegt darin, dass im SQL NULLs anders verwendet werden (sie k\u00f6nnen nicht mit = abgepr\u00fcft werden). In den anderen Cache-Einstellungen w\u00fcrden SQLs wie SELECT * FROM &lt;lookuptabelle&gt; where Country = NULL ausgef\u00fchrt.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Lookups kommen bei SSIS-Data Flows ziemlich h\u00e4ufig vor. Zum Beispiel m\u00fcssen bei der Verarbeitung von Fakten-S\u00e4tzen enthaltene Dimensions-Referenzen umgesetzt werden. Zum Beispiel k\u00f6nnte es sein, dass Deutschland im Quellsystem \u2018D\u2019 ist, im DWH aber mit 49 abgebildet wird. Das ist mit Lookups nat\u00fcrlich ganz einfach. Nun kommt es aber vor, dass nicht alle Fakten eine &hellip; <a href=\"https:\/\/www.csopro.de\/biblog\/2014\/12\/null-werte-bei-lookups\/\" class=\"more-link\"><span class=\"screen-reader-text\">NULL-Werte bei Lookups<\/span> weiterlesen <span class=\"meta-nav\">&rarr;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[10],"tags":[18,16],"class_list":["post-253","post","type-post","status-publish","format-standard","hentry","category-integrationservices","tag-error-handling","tag-lookup"],"_links":{"self":[{"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/posts\/253","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/comments?post=253"}],"version-history":[{"count":3,"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/posts\/253\/revisions"}],"predecessor-version":[{"id":264,"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/posts\/253\/revisions\/264"}],"wp:attachment":[{"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/media?parent=253"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/categories?post=253"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.csopro.de\/biblog\/wp-json\/wp\/v2\/tags?post=253"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}