2016-04-13 15 views
5

Ich habe eine knifflige Flat-File-Datenquelle. Die Daten werden gruppiert, wie folgt aus:
So laden Sie gruppierte Daten mit SSIS

Country City 
U.S.  New York 
      Washington 
      Baltimore 
Canada  Toronto 
      Vancouver 

Aber ich will es dieses Format sein, wenn es in die Datenbank geladen wird:

Country City 
U.S.  New York 
U.S.  Washington 
U.S.  Baltimore 
Canada  Toronto 
Canada  Vancouver 

Wer vor ein solches Problem hat getroffen? Hast du eine Idee, damit umzugehen?
Die einzige Idee, die ich jetzt habe, ist den Cursor zu benutzen, aber der ist einfach zu langsam.
Vielen Dank!

+0

Wenn Sie keine andere Spalte wie ID haben, um herauszufinden, zu welchem ​​Land die Städte gehören, ist es für DBMS unmöglich zu bekommen, was Sie brauchen. weil sie nicht in Reihenfolge gehen. –

Antwort

3

die Antwort von cha funktionieren wird, aber hier ist eine andere den Fall, dass Sie Mach es in SSIS wit hout temporäre/Staging-Tabellen:

Sie können Ihren Datenfluss über eine Script Transformation, die eine DataFlow-Level-Variable verwendet, ausführen. Wenn jede Zeile kommt, überprüft das Skript den Wert der Spalte Land.

Wenn es einen nicht leeren Wert hat, füllen Sie die Variable mit diesem Wert und übergeben Sie sie im Datenfluss.

Wenn Land einen leeren Wert hat, überschreiben Sie es mit dem Wert der Variablen, die der letzte nicht leere Landwert ist, den Sie erhalten haben.

EDIT: Ich sah Ihre Fehlermeldung auf und lernte etwas Neues über Script Components (Datenfluss Werkzeug, im Gegensatz zu Skript-Tasks, das Kontrollfluss-Tool):

Die Sammlung von Readwrite ist nur verfügbar, in der PostExecute-Methode, um die Leistung zu maximieren und das Risiko von Sperren Konflikte zu minimieren. Daher können Sie den Wert einer Paketvariablen nicht direkt erhöhen, während Sie jede Datenzeile verarbeiten.Erhöhen Sie stattdessen den Wert einer lokalen Variablen und setzen Sie den Wert der Variablen des Pakets auf den Wert der lokalen Variablen in der PostExecute-Methode , nachdem alle Daten verarbeitet wurden. Sie können auch die VariableDispenser -Eigenschaft verwenden, um diese Einschränkung zu umgehen, wie später in diesem Thema beschrieben. Das direkte Schreiben auf eine Paketvariable , wenn jede Zeile verarbeitet wird, wirkt sich jedoch negativ auf die Leistung aus und erhöht das Risiko von Sperrkonflikten.

Das von this MSDN article kommt, die auch weitere Informationen über die Variable Dispenser Work-around hat, wenn Sie diesen Weg gehen wollen, aber anscheinend ich täuschen Sie oben, wenn ich sagte, Sie den Wert der Paketvariablen einstellen im Skript. Sie müssen eine Variable verwenden, die für das Skript lokal ist, und sie dann im Post-Execute-Ereignishandler ändern. Ich kann aus dem Artikel nicht sagen, ob das bedeutet, dass Sie die Variable im Skript nicht lesen können, und wenn das der Fall ist, dann wäre der Variable Dispenser die einzige Option. Oder ich nehme an, Sie könnten eine andere Variable erstellen, auf die das Skript schreibgeschützten Zugriff hat, und seinen Wert auf einen Ausdruck setzen, so dass es immer den Wert der Variable zum Lesen/Schreiben hat. Das könnte funktionieren.

+0

Danke Kumpel. Ich habe versucht, Cursor zu verwenden, was Ihrer Idee ähnlich ist. Aber ich habe tatsächlich 20k + Zeilen, so dass Zeile für Zeile läuft einfach zu langsam ...... –

+0

Ok, ich habe versucht, Ihre Methode, aber ich bekam immer die Fehlermeldung "Die Sammlung von Variablen für Lese-und Schreibzugriff gesperrt ist außerhalb von PostExecute nicht verfügbar ". Aber ich setze die Variablenbelegung in PostExecute ....... Zeichen, weiß nicht, was schief gelaufen ist. Danke –

+0

Vielleicht dürfen Sie auch nicht die Variable ReadWrite im Skript lesen. Ich habe meine Antwort mit einigen neuen Informationen bearbeitet, die ich gelernt habe. –

3

Ja, es ist möglich. Zuerst müssen Sie die Daten in eine Tabelle mit einer Identity-Spalte laden:

-- drop table #t 
CREATE TABLE #t (id INTEGER IDENTITY PRIMARY KEY, 
Country VARCHAR(20), 
City VARCHAR(20)) 

INSERT INTO #t(Country, City) 
SELECT a.Country, a.City 
FROM OPENROWSET(BULK 'c:\import.txt', 
    FORMATFILE = 'c:\format.fmt', 
    FIRSTROW = 2) AS a; 

select * from #t 

Das Ergebnis wird:

id   Country    City 
----------- -------------------- -------------------- 
1   U.S.     New York 
2        Washington 
3        Baltimore 
4   Canada    Toronto 
5        Vancouver 

Und jetzt mit einem wenig rekursive CTE Magie können Sie die fehlenden Details füllen:

;WITH a as(
    SELECT Country 
      ,City 
      ,ID 
    FROM #t WHERE ID = 1 
    UNION ALL 
    SELECT COALESCE(NULLIF(LTrim(#t.Country), ''),a.Country) 
      ,#t.City 
      ,#t.ID 
    FROM a INNER JOIN #t ON a.ID+1 = #t.ID 
    ) 
SELECT * FROM a 
OPTION (MAXRECURSION 0) 

Ergebnis:

Country    City     ID 
-------------------- -------------------- ----------- 
U.S.     New York    1 
U.S.     Washington   2 
U.S.     Baltimore   3 
Canada    Toronto    4 
Canada    Vancouver   5 
Update-

:

Wie Tab Alleman unter dem gleichen Ergebnis legte nahe, ohne die rekursive Abfrage erreicht werden:

SELECT ID 
    , COALESCE(NULLIF(LTrim(a.Country), ''), (SELECT TOP 1 Country FROM #t t WHERE t.ID < a.ID AND LTrim(t.Country) <> '' ORDER BY t.ID DESC)) 
    , City 
FROM #t a 

BTW, die Formatdatei für Ihre Eingangsdaten ist dies (wenn Sie wollen versuchen, die Skripte speichern die Eingabedaten als c: \ import.txt und die Formatdatei im folgenden als c: \ format.fmt):

9.0 
    2 
    1  SQLCHAR  0  11  ""  1  Country  SQL_Latin1_General_CP1_CI_AS 
    2  SQLCHAR  0  100  "\r\n" 2  City   SQL_Latin1_General_CP1_CI_AS 
+0

Danke cha. Ich habe deine Idee ausprobiert, aber in meinem Fall hat es nicht funktioniert. Weil ich tatsächlich 20k + Zeilen in meiner Tabelle habe. So habe ich einen Fehler wie "der CTE wiederholt für mehr als 100 Mal" –

+0

Sie können einen Self-Join statt der rekursiven CTE verwenden, um die Rekursion zu vermeiden. –

+0

Danke @TabAlleman. Ich habe meine Antwort aktualisiert, um eine Version ohne Rekursion zu enthalten – cha

Verwandte Themen