2016-04-13 45 views
5

Ho un'origine dati di file flat ingannevole. I dati sono raggruppati, in questo modo:
Come caricare i dati raggruppati con SSIS

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

Ma io voglio che sia questo formato quando è caricato al database:

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

Chiunque ha incontrato un tale problema prima? Hai un'idea per affrontarlo?
L'unica idea che ho ora è usare il cursore, ma è troppo lento.
Grazie!

+0

se non si dispone di un'altra colonna come id per aiutare a capire a quale paese appartengono le città è impossibile per DBMS ottenere ciò di cui si ha bisogno. perché non vanno in ordine. –

risposta

3

la risposta da parte cha funzionerà, ma qui è un altro in caso di necessità di fallo con SSIS hout tabelle temporanee/di staging:

È possibile eseguire il flusso di dati tramite una Script Transformation che utilizza una variabile a livello di flusso di dati. Quando ogni riga arriva nello script controlla il valore della colonna Paese.

Se ha un valore non vuoto, inserire la variabile con quel valore e passarla nel flusso di dati.

Se Paese ha un valore vuoto, sovrascriverlo con il valore della variabile, che sarà l'ultimo valore del Paese non vuoto ottenuto.

EDIT: ho guardato il vostro messaggio di errore e ho imparato qualcosa di nuovo su Script Components (lo strumento del flusso di dati, al contrario di attività di script, lo strumento di controllo di flusso):

La raccolta di ReadWriteVariables è disponibile solo nel metodo PostExecute per massimizzare le prestazioni e ridurre al minimo il rischio di conflitti di blocco . Pertanto non è possibile incrementare direttamente il valore di una variabile di pacchetto mentre si elabora ogni riga di dati.Aumentare invece il valore di una variabile locale e impostare il valore della variabile del pacchetto sul valore della variabile locale nel metodo PostExecute dopo che tutti i dati sono stati elaborati. È inoltre possibile utilizzare la proprietà VariableDispenser per ovviare a questa limitazione, come descritto più avanti in questo argomento. Tuttavia, scrivere direttamente su un pacchetto con la variabile mentre ogni riga viene elaborata avrà un impatto negativo sulle prestazioni e aumenterà il rischio di conflitti di blocco.

che viene da this MSDN article, che ha anche maggiori informazioni sul dispenser variabile work-around, se si vuole seguire questa strada, ma a quanto pare ti indurre in errore precedenza quando ho detto è possibile impostare il valore della variabile package nella sceneggiatura. È necessario utilizzare una variabile locale per lo script e quindi modificarla nel gestore eventi Post-esecuzione. Non posso dire dall'articolo se ciò significa che non sarai in grado di leggere la variabile nello script, e se questo è il caso, allora il Variable Dispenser sarebbe l'unica opzione. O suppongo che potresti creare un'altra variabile a cui lo script avrà accesso in sola lettura e impostare il suo valore su un'espressione in modo che abbia sempre il valore della variabile di lettura-scrittura. Potrebbe funzionare.

+0

Grazie amico. Ho provato ad usare il cursore, che penso sia simile alla tua idea. Ma in realtà ho 20k + righe, quindi correre fila per fila è troppo lento ...... –

+0

Ok, ho provato il tuo metodo, ma ho sempre ricevuto il messaggio di errore "La collezione di variabili bloccate per accesso in lettura e scrittura non è disponibile al di fuori di PostExecute ". Ma sto mettendo l'assegnazione variabile in PostExecute ....... segno, non so cosa sia andato storto. Grazie –

+0

Forse non sei autorizzato a leggere la variabile ReadWrite anche nello script. Ho modificato la mia risposta con alcune nuove informazioni che ho appreso. –

3

Sì, è possibile. In primo luogo è necessario caricare i dati in una tabella con una colonna IDENTITY:

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

Il risultato sarà:

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

E ora, con un po 'di CTE ricorsiva magia è possibile popolare i dettagli mancanti:

;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) 

Risultato:

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

Aggiornamento:

Come Tab Alemanni suggerito sotto lo stesso risultato può essere raggiunto senza la query ricorsiva:

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 

A proposito, il file di formato per i dati di input è questo (se volete provare gli script salvare i dati di input come c: \ import.txt e il file di formato qui sotto come 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

Grazie cha. Ho provato la tua idea, ma nel mio caso non ha funzionato. Perché in realtà ho 20k + righe nel mio tavolo. Quindi ho ricevuto un errore che diceva qualcosa come "la CTE ricorre più di 100 volte" –

+0

È possibile utilizzare un self-join al posto del CTE ricorsivo per evitare il limite di ricorsione. –

+0

Grazie a @TabAlleman. Ho aggiornato la mia risposta per includere una versione senza ricorsione – cha