Die Ver­mei­dung von Schlüs­sel­ver­let­zun­gen und Dublet­ten ist bei der Bewirt­schaf­tung kon­so­li­dier­ter Data Warehou­ses all­täg­li­che Grund­an­for­de­rung. Es exis­tiert eine ganze Reihe von Soft­ware-Tools, wie bei­spiels­weise DWau­to­ma­tic oder IBM Change Data Cap­ture, die diese Fil­te­run­gen unter­stüt­zen. Im Fall der allei­ni­gen Ver­wen­dung eines ETL-Tools bleibt es jedoch der Map­ping-Logik über­las­sen, die Dublet­ten­fil­te­rung zu über­neh­men.
In die­sem Arti­kel beleuch­ten wir eine per­for­mante Umset­zungs­mög­lich­keit am Bei­spiel des erfolg­rei­chen ETL-Tools Infor­ma­tica Power­Cen­ter. Im Fol­gen­den wol­len wir immer von im Data Ware­house übli­chen Sta­ging-Tabel­len als Daten­quelle ausgehen.

Dublet­ten

Die Ent­ste­hungs­mög­lich­kei­ten von Dublet­ten sind zahl­reich. Gehen wir hier davon aus, dass die Daten-Quelle die Aktua­li­tät ihrer Sätze mit einem Zeit­stem­pel QUELL_TS mar­kiert. Jede Aktua­li­sie­rung eines Daten­sat­zes erzeugt so einen neuen Satz mit glei­chem Schlüs­sel aber neuem, höhe­rem QUELL_TS. Durch Nach­lie­fe­run­gen nach Ver­bin­dungs­pro­ble­men o.ä. ist nicht aus­ge­schlos­sen, dass neuere QUELL_TS ältere „über­ho­len“ und vor ihnen in der Anlie­fe­rungs­ta­belle ankom­men.
Wird die Anlie­fe­rung der­sel­ben fach­li­chen Daten – bei­spiels­weise wegen Ver­bin­dungs­pro­ble­men – wie­der­holt, so ent­ste­hen fach­lich iden­ti­sche Sätze. Allen wei­te­ren Dublet­ten­ent­ste­hungs­mög­lich­kei­ten – vor allem durch schlechte Qua­li­tät der ange­lie­fer­ten Daten – begeg­nen wir mit der Annahme bzw. Defi­ni­tion, dass die neu­este Zeile die beste Infor­ma­tion ent­hält. Gehen wir hier­bei davon aus, dass ein fort­lau­fen­der Zei­len­iden­ti­fi­ka­tor STAGE_ROW_ID vor­han­den ist. Pro fach­li­chem Schlüs­sel – nen­nen wir ihn KEY1 – wol­len wir also nur die Zeile mit der höchs­ten STAGE_ROW_ID von denen mit dem höchs­ten QUELL_TS.

Per­for­mance-Rand­be­din­gun­gen

In ETL-Tools sind Aggre­ga­to­ren und Joi­ner kos­ten­in­ten­sive Trans­for­ma­tio­nen, da sie Caches für Ihre Berech­nun­gen anle­gen und vor­hal­ten müs­sen. Man kann sich jedoch die zei­len­weise Ver­ar­bei­tung von Daten­zei­len zunutze machen und Infor­ma­tio­nen von einer Vor­gän­ger­zeile an einen Nach­fol­ger wei­ter­rei­chen. Dies ermög­licht einer­seits die per­for­man­teste Art zur Wech­sel­wir­kung von Daten­zei­len mit­ein­an­der, ist ande­rer­seits aber auf eine defi­nierte Sor­tie­rung ange­wie­sen und so auf einen ein­sei­ti­gen Daten­fluss beschränkt. Die pas­sende Zei­len-Rei­hen­folge und damit Sor­tie­rung ist also die Grund­vor­aus­set­zung für per­for­mante Dublettenentfernung.

Zei­len-Rei­hen­folge

Da in Power­Cen­ter eine Zeile immer nur Infor­ma­tio­nen von ihrem Vor­gän­ger (und damit ggfs. auch wei­te­ren Vor­gän­gern) erhal­ten kann, ist es nötig, die zu erhal­tende Zeile inner­halb einer Dublet­ten-Gruppe an die erste Stelle zu sor­tie­ren. Die nach­fol­gen­den Zei­len kön­nen dann erken­nen, dass ihr Vor­gän­ger einen iden­ti­schen Schlüs­sel hat und sich selbst so als zu ent­fer­nende Dublette erken­nen.
Die Sor­tier­rei­hen­folge der Schlüs­sel­at­tri­bute ist uner­heb­lich, da es auf die Rei­hen­folge der Ele­mente inner­halb einer Schlüs­sel­gruppe ankommt, aber nicht auf die Rei­hen­folge der Grup­pen zuein­an­der. Es muss nur sicher­ge­stellt sein, dass die Zei­len eines Schlüs­sel­at­tri­buts immer in einer ein­zi­gen Gruppe zusam­men­ste­hen. Anders ist es bei QUELL_TS und STAGE_ROW_ID: Hier ist jeweils eine abstei­gende (des­cen­ding) Sor­tie­rung nötig, um den neu­es­ten Satz mit jeweils höchs­tem Wert zu erhalten.

Sor­tie­rung in Infor­ma­tica PowerCenter

Rela­tio­nale Daten­ban­ken sind grund­sätz­lich gut für Sor­tie­run­gen aus­ge­legt und so ist es mög­lich, die Daten schon bei der Selek­tion sor­tiert abzu­ru­fen. In Power­Cen­ter geschieht diese Ein­stel­lung im Source Qua­li­fier Objekt im Rei­ter „Pro­per­ties“. Hier kann in das Feld „Num­ber of Sor­ted Ports“ die Anzahl der Attri­bute ein­ge­tra­gen wer­den über die sor­tiert wer­den soll. Über Aus­wahl und Sor­tier­rei­hen­folge der Attri­bute ent­schei­det dabei ihre Rei­hen­folge von oben nach unten im Source Qua­li­fier. Das Attri­but über das als ers­tes sor­tiert wer­den soll, wird also als obers­ter Port durch den Source Qua­li­fier geführt.
Die Umset­zung die­ser Ein­stel­lun­gen von Power­Cen­ter ist dann denk­bar ein­fach: es wird ein „order by …“ gefolgt von einer Liste der betref­fen­den Attri­but­na­men an das Selek­ti­ons-State­ment ange­hängt. Ohne wei­tere Spe­zi­fi­zie­rung erfolgt die Sor­tie­rung auf der Daten­bank dann nach der dor­ti­gen Stan­dard-Ein­stel­lung – im Nor­mal­fall also auf­stei­gend. Die Mit­gabe einer auf­stei­gend (ascen­ding) oder abstei­gend (des­cen­ding) Angabe ist nicht mög­lich, was das ganze für uns unbrauch­bar macht: Wir sind ja gerade auf eine abstei­gende Sor­tie­rung ange­wie­sen, um unsere zu erhal­ten­den Sätze an den Anfang einer Dublet­ten-Gruppe zu plat­zie­ren.
Mit einem Trick kön­nen wir aber doch die von uns gewünschte, pas­sende „order by“-Formulierung in das State­ment ein­schleu­sen. Dazu nut­zen wir den „Source Fil­ter“ im sel­ben Rei­ter, in den sonst „where“-Bedingungen des State­ments ein­ge­tra­gen wer­den. Hier machen wir uns zu Nutze, das Power­Cen­ter genau das tut: es schreibt, was im Source Fil­ter steht im SQL-State­ment hin­ter das „where“. Auf die­sen even­tu­el­len Fil­ter folgt im SQL dann ja aber auch direkt das „order by“. So kann man hier nach dem Fil­ter direkt mit der selbst­ge­schrie­be­nen „order by“-Formulierung fort­fah­ren. Wie auch in unse­rem ein­fa­chen Bei­spiel braucht man ja nicht immer einen wirk­li­chen Fil­ter. Damit das State­ment valide wird, benö­tigt man also ggfs. eine Pseudo-Fil­ter-For­mu­lie­rung. Hier genügt z.B. ein „1 = 1“. In unse­rem Bei­spiel wür­den wir also fol­gen­des in den Source Qua­li­fier schreiben:

1=1
order by
KEY1,
QUELL_TS desc,
STAGE_ROW_ID desc

Die anfangs genannte „Num­ber of Sor­ted Ports“ muss dann natür­lich auf 0 ste­hen, damit Power­Cen­ter nicht wie­der ein wei­te­res „order by“ anfügt.
Abhän­gig von der ver­wen­de­ten Quell-Daten­bank und der Kom­ple­xi­tät der Quell­da­ten­se­lek­tion kann es güns­ti­ger sein, auf die Sor­tie­rung auf der Daten­bank zu ver­zich­ten und die Daten erst im Map­ping von einem Sor­ter-Objekt sor­tie­ren zu las­sen. Dies kann bei not­wen­di­gen fach­li­chen Fil­tern oder kom­ple­xe­ren Delta-Daten-Selek­tio­nen pas­sie­ren, wenn die Daten­bank dabei nicht in der Lage ist, vor­han­dene Indi­ces sinn­voll zu nut­zen. Beim Sor­ter-Objekt in Power­Cen­ter wird die Sor­tier­rei­hen­folge auch über die Rei­hen­folge der Ports von oben nach unten bestimmt. „Ascen­ding“ oder „Des­cen­ding“ kann hier­bei dann ganz ein­fach aus­ge­wählt wer­den.
Mit die­sen zwei Vor­ge­hens­wei­sen kön­nen wir nur also jede von uns gewünscht Sor­tie­rung erzeu­gen. In den fol­gen­den Bild-Bei­spie­len sind Power­Cen­ter-Sor­ter ver­wen­det, weil so schnel­ler ersicht­lich ist, wie genau sor­tiert wird.

Dublet­ten­er­ken­nung von ein­zel­nen Schlüsseln

Zur Erken­nung von Dublet­ten eines Schlüs­sels wird nach die­sem sor­tiert. Eine Zeile kann sich so als Dublette erken­nen, indem sie ihr Schlüs­sel­at­tri­but – in unse­rem Bei­spiel KEY1 – mit dem ihrer Vor­gän­ger­zeile ver­gleicht. Sind diese gleich, kann sie sich als Dublette mar­kie­ren und aus­ge­fil­tert wer­den.
Die erste Zeile einer Dublet­ten­gruppe erkennt sich selbst also nicht als Dublette und ist die­je­nige einer sol­chen Gruppe, die bestehen bleibt. Anhand der pas­sen­den Sor­tie­rung haben wir dafür gesorgt, dass die­je­nige Zeile als erste ver­ar­bei­tet wird, die wir im Falle von Dublet­ten behal­ten wol­len: die­je­nige mit höchs­tem QUELL_TS und unter denen ggfs. die mit der höchs­ten Zei­len­num­mer STAGE_ROW_ID.
Für den Ver­gleich mit der Vor­gän­ger­zeile machen wir uns zu Nutze, dass in Power­Cen­ter-Expres­si­ons die Varia­blen-Fel­der von oben nach unten berech­net wer­den. Wenn man bei einer Berech­nung also eine andere Varia­ble ver­wen­det, die wei­ter unten in der Expres­sion steht, dann hat diese zu dem Zeit­punkt noch den „alten“ Wert der Vor­gän­ger­zeile.
In unse­rem Bei­spiel ver­wen­den wir eine Varia­ble V_FLAG_DUBLETTE, die das Schlüs­sel­at­tri­but KEY1 mit dem Wert einer Varia­ble V_KEY1_PREV ver­gleicht, in der der Vor­gän­ger­zei­len­wert gespei­chert ist:

IIF( KEY1 = V_KEY1_PREV, 1, 0 )

Die Varia­ble V_KEY1_PREV nimmt ein­fach nur den Wert des durch­ge­führ­ten Ports KEY1 auf. Ent­schei­dend ist nur, dass V_FLAG_DUBLETTE in der Expres­sion über V_KEY1_PREV steht, damit zum Berech­nungs­zeit­punkt von V_FLAG_DUBLETTE in V_KEY1_PREV noch der Wert von KEY1 der Vor­gän­ger­zeile steht. Erst nach Berech­nung von V_FLAG_DUBLETTE wird in V_KEY1_PREV der Wert der aktu­el­len Zeile hin­ter­legt, um ihn erst in der nach­fol­gen­den Zeile dann zu benut­zen. In der Abbil­dung „Dublet­ten­er­ken­nung von ein­zel­nen Schlüs­seln“ ist das Expres­sion-Objekt mit den pas­sen­den Feld-Rei­hen­fol­gen und ‑Inhal­ten gezeigt.

Performante Dublettenentfernung mit Informatica PowerCenter Bild1
Abbil­dung 1 Dublet­ten­er­ken­nung von ein­zel­nen Schlüsseln

In unse­rem Bei­spiel füh­ren wir die Dublet­ten-Infor­ma­tion aus V_FLAG_DUBLETTE in einem Out­put-Port FLAG_DUBLETTE aus der Expres­sion hin­aus. In einem nach­fol­gen­den Fil­ter wer­den dann nur Zei­len durch­ge­las­sen, die sich nicht als Dublet­ten mar­kiert haben, für die also FLAG_DUBLETTE = 0 gilt.

Dublet­ten­er­ken­nung in Gesamtdaten-Lieferungen

In Fäl­len, in denen nur die neuste Gesamt­lie­fe­rung von Daten­sät­zen her­an­ge­zo­gen wer­den soll, muss es einen ein­deu­ti­gen, über die Lie­fe­rung kon­stan­ten Iden­ti­fi­ka­tor geben. Gehen wir also davon aus, dass eine Gesamt­lie­fe­rung mit ein­heit­li­chem QUELL_TS gelie­fert wird. Hier­bei sor­tie­ren wir dann zunächst abstei­gend nach die­sem QUELL_TS, sodass die zu berück­sich­ti­gen­den Sätze als ers­tes in das Map­ping geführt wer­den. Sobald ein QUELL_TS nicht dem sei­nes Vor­gän­gers ent­spricht, ist die­ser Satz und alle fol­gen­den ver­al­tet und wer­den ver­wor­fen.
In unse­rem Bei­spiel las­sen wir in V_FLAG_QTS_KLEINER dar­auf hor­chen und wenn der Über­gang ein­mal gefun­den wurde, die Infor­ma­tion in alle fol­gen­den Zei­len weitertragen:

IIF( QUELL_TS < V_QUELL_TS_PREV or V_FLAG_QTS_KLEINER = 1, 1, 0 )

Die Aus­gabe zum Weg­fil­tern FLAG_DUBLETTE setzt sich dann aus der Dublet­ten­er­ken­nung ein­zel­ner Schlüs­sel V_FLAG_DUBLETTE und der Grup­pen­er­ken­nung V_FLAG_QDZ_KLEINER zusammen:

IIF( V_FLAG_DUBLETTE = 1 or V_FLAG_QTS_KLEINER = 1, 1, 0 )

In der Abbil­dung „Dublet­ten­er­ken­nung in Gesamt­da­ten-Lie­fe­run­gen“ wird das Vor­ge­hen bild­lich zusammengefasst.

Performante Dublettenentfernung mit Informatica PowerCenter Bild2
Abbil­dung 2 Dublet­ten­er­ken­nung in Gesamtdaten-Lieferungen

Dublet­ten­er­ken­nung bei Gruppen-Schlüsseln

Wenn für einen Grup­pen­schlüs­sel – wir nen­nen ihn hier GRUPPEN_KEY – nur die neus­ten Ele­mente berück­sich­tigt wer­den sol­len, ver­wen­den wir eine Sor­tie­rung nach dem GRUPPEN_KEY, dann dem QUELL_TS abstei­gend und danach den Ein­zel­schlüs­sel-Attri­bu­ten – hier KEY1.
Die Dublet­ten­er­ken­nung für ein­zelne Sätze geht über den gesam­ten Schlüs­sel, wird also zu:

IIF( GRUPPEN_KEY = V_GRUPPEN_KEY_PREV and
KEY1 = V_KEY1_PREV, 1, 0 )

Die Prü­fung in V_FLAG_QTS_KLEINER fin­det jetzt nur noch inner­halb eines GRUPPEN_KEY statt:

IIF( GRUPPEN_KEY = V_GRUPPEN_KEY_PREV and
( QUELL_TS < V_QUELL_TS_PREV
or
V_FLAG_QTS_KLEINER = 1
)
1, 0 )

Die Abbil­dung „Dublet­ten­er­ken­nung bei Grup­pen-Schlüs­seln“ fasst das Vor­ge­hen zusammen.

Performante Dublettenentfernung mit Informatica PowerCenter Bild3
Abbil­dung 3 Dublet­ten­er­ken­nung bei Gruppen-Schlüsseln

Fazit

Durch die zei­len­weise Ver­ar­bei­tung ist in Infor­ma­tica Power­Cen­ter eine Dublet­ten-Erken­nung und Fil­te­rung bei Sor­tie­rung nach dem ent­spre­chen­den Schlüs­sel per­for­mant mög­lich. Wenn es wei­tere sor­tier­bare Fel­der zur Iden­ti­fi­ka­tion des zu erhal­ten­den Sat­zes einer Dublet­ten-Gruppe gibt, kann er durch die pas­sende Sor­tie­rung als der Satz aus­ge­wählt wer­den, der nicht aus­ge­fil­tert wird. Zudem ist das Prin­zip auch auf kom­ple­xere Grup­pen-Aus­wahl-Regeln erweiterbar.