2014-12-05 19 views
9

così ho dovuto risolvere per un po 'di tempo un problema di perplessità con BULK INSERT. I file provengono da una scatola Linux e quando li guardo in modalità esadecimale/notepad ++ sembrano avere solo un linefeed (0A) come terminatore di riga. Conservo le istruzioni di inserimento in blocco in una tabella che in seguito seleziona un lavoro e esegue l'istruzione nella tabella per caricare i dati in una tabella di staging.Inserimento di massa - Terminatore di riga per il file UNIX + Terminatore di riga " l"

Il caso particolare che mi lascia perplessi è una tabella con 7 colonne. Il file di dati ha solo le prime 4 colonne, il resto deve essere lasciato NULL.

Tipicamente essi simile a questa:

BULK INSERT STAGING_TABLE FROM 'FILE_LOCATION' 
WITH  ( 
DATAFILETYPE = 'widechar' 
, FIELDTERMINATOR = ',' 
, ROWTERMINATOR = 'something_here' 
); 

La fila terminatore è stata la più grande fonte di miei problemi.

Quando provo a utilizzare "\ n" l'inserto di massa non riesce su un errore di troncamento, sembra trattare il file come una stringa lunga e delimita le colonne correttamente solo fino a quando non esaurisce le colonne (quindi errore di troncamento) .

Quando utilizzo "0x0a", l'inserimento di massa non riesce sull'errore "fine del file inaspettato". C'era una riga vuota alla fine del file, ma anche quando l'ho rimosso ha gettato ancora lo stesso errore, quindi non sono sicuro di cosa c'è che non va.

L'UNICO finora che ha lavorato per ottenere i dati effettivamente nella tabella era "\ l". Qualcuno sa cosa significa? Ho cercato in lungo e in largo ma non sembra esserci documentazione su di esso. Quello o io ho cercato completamente nel posto sbagliato.

La cosa strana con \ l come rowterminator è che anche se viene caricato correttamente, non rispetta ancora il rowterminator ... Le righe vengono semplicemente caricate in tutte e 7 le colonne e divise in intervalli apparentemente casuali.

Qualcuno ha qualche idea? Dovrei chiarire un po 'di più?

risposta

7

Il problema riscontrato non è dovuto al Terminatore di riga. Ho il sospetto che, insieme con la fine del file di errore, è anche visto qualcosa di simile al seguente:

Msg 4864, Level 16, State 1, Line 1
Bulk load data conversion error (type mismatch or invalid character for the specified codepage) for row 1, column 4 ({column_name}).

Mentre quello che ho detto al di sotto della linea è ancora valido per quanto riguarda la ROWTERMINATOR, il vero problema è indicato dalla sua dichiarazione di :

[the] table that has 7 columns. The data file only has the first 4 columns, the rest should be left NULL.

Questo è il problema. Quando si utilizza BULK INSERT, il file di dati deve avere lo stesso numero di campi della tabella in cui viene inserito. In caso contrario, è necessario utilizzare l'opzione FORMATFILE ='format_file_path', nel qual caso è necessario creare un Format File e specificare la posizione.

Ho pensato che si potrebbe ottenere via con la più facile OPENROWSET(BULK...) in modo da poter eseguire le seguenti operazioni:

INSERT INTO STAGING_TABLE 
    SELECT * 
    FROM OPENROWSET(BULK 'FILE_LOCATION' ...); 

Ma che non permette di specificare un ROWTERMINATOR senza utilizzare un file di formato. Quindi è necessario il file di formato in entrambi i casi.

O, si può solo importare in una diversa tabella di gestione temporanea che ha solo 4 colonne, e poi o:

  • discarica che nel vostro STAGING_TABLE corrente, o

  • fare un ALTER TABLE aggiungere le 3 colonne mancanti (è più efficiente aggiungere solo 3 campi NULLable piuttosto che trasferire i dati da una tabella a un'altra :-).

O, come detto da @PhilipKelley in un commento su questa risposta, è possibile creare una visualizzazione con solo quei quattro campi e che hanno essere la destinazione/target. E se stavi facendo i passi appropriati per abilitare l'operazione a essere minimamente loggato, la pagina MSDN per Prerequisites for Minimal Logging in Bulk Import non dice in un modo o nell'altro quale sarà l'effetto se usi una vista.


Molto probabilmente il \l stata appena interpretato come quei due caratteri letterali, quindi non rispettando la rowterminator quando si è tentato di esso.

Il 0x0A funzionerà come l'ho provato e si comporta come previsto.La vostra dichiarazione dovrebbe essere simile alla seguente:

BULK INSERT STAGING_TABLE 
FROM 'FILE_LOCATION' 
WITH ( 
     DATAFILETYPE = 'widechar', 
     FIELDTERMINATOR = ',', 
     ROWTERMINATOR = '0x0A' 
); 

Ho provato sia con che senza un carattere 0x0A alla fine della linea finale e sia lavorato lo stesso.

Ho poi rimosso una delle virgole da una delle linee, lasciando con meno l'intero insieme di campi, e cioè quando ho ottenuto il seguente errore:

Msg 4832, Level 16, State 1, Line 2 
    Bulk load: An unexpected end of file was encountered in the data file. 
Msg 7399, Level 16, State 1, Line 2 
    The OLE DB provider "BULK" for linked server "(null)" reported an error. The 
       provider did not give any information about the error. 
Msg 7330, Level 16, State 2, Line 2 
    Cannot fetch a row from OLE DB provider "BULK" for linked server "(null)". 

Assicurarsi che tutti le righe nel file di dati hanno il numero richiesto di separatori di campo (, in questo caso). Hai detto di avere 4 colonne nel file in modo che dovessero essere 3 virgole per riga.

+1

Grazie, i suggerimenti alternativi che hai fatto hanno finito per essere in grado di risolvere il mio problema. È stato sicuramente il fatto che ho colonne diverse tra il file di dati e la tabella di staging. Sono stato confuso da questo però perché ci sono molti altri processi strutturati in questo stesso esatto modo di funzionare correttamente nonostante abbiano anche colonne non corrispondenti. In ogni caso tutto ha funzionato. Grazie! –

+1

@RazzleDazzle Sono contento che abbia funzionato! Per quanto riguarda gli altri processi simili che funzionano, sei sicuro che a) stanno usando 'BULK INSERT' e non qualcosa come' OPENROWSET (BULK ...) 'o' BCP.EXE', e b) se stanno usando ' BULK INSERT', che non usano anche un file di formattazione? Non vedo come sia possibile che possano lavorare con 'BULK INSERT' pur avendo un diverso numero di colonne e nessun file di formato, a meno che quelle colonne non siano IDENTITY o qualcosa del genere (che non può essere inserito in). PS, quale vera soluzione hai finito con? Semplicemente curioso :) –

+0

@RazzleDazzle Solo FYI: ho appena testato di nuovo con una colonna in più nella tabella di destinazione. Ho provato sia come 'DATETIME NOT NULL DEFAULT (GETDATE())' che come 'INT NOT NULL IDENTITY (1, 1)' ed entrambi non sono riusciti a causa della mancata corrispondenza nel numero di colonne. Quindi non si è sicuri di come quegli altri processi funzionino senza un file di formattazione o usando qualcosa di diverso da "BULK INSERT". –

0

Vorrei commentare per chiedere questi, ma la mia reputazione non è abbastanza alta.

Credo che "\ l" sia "linefeed", in modo da creare mesh con la visualizzazione di 0A nella codifica del file.

La mia prima domanda sarebbe: in che tipo di codifica dei caratteri ci sono i file di dati? E qual è il tipo di dati sulle colonne della tua tabella?

Direi che questo sarà un problema di codifica dei caratteri. Vedo che il tuo DATAFILETYPE è 'widechar' Hai confermato che il tuo file sorgente è Unicode? E quando inserisci dati e li rimuovi, sembra che la codifica dei caratteri venga preservata?

+0

Il '\ l' è solo" backslash l ". Non è una sequenza di escape interpretata. L'OP è corretto in quel 0x0A == \ n == avanzamento riga. Ma il problema è una mancata corrispondenza del numero del campo tra il file di origine e la tabella di destinazione. –

0

This sembra indicare che l'utilizzo di un carattere di fine riga come terminatore di riga, '\n', verrà automaticamente tradotto in '\r\n'. Dice che si applica solo a bcp, ma chiaramente sta succedendo qualcos'altro.

Esempio C nella parte inferiore della pagina dice di usare questo SQL dinamico per fine riga Unix:

DECLARE @bulk_cmd varchar(1000); 
SET @bulk_cmd = 'BULK INSERT AdventureWorks2012.Sales.SalesOrderDetail 
FROM ''<drive>:\<path>\<filename>'' 
WITH (ROWTERMINATOR = '''+CHAR(10)+''')'; 
EXEC(@bulk_cmd); 

che fa sembrare come se fosse un problema noto.

Se si sta recuperando il file da un sito FTP/SFTP, è possibile trasferire il file in modalità ASCII?In alternativa, è possibile eseguire il file attraverso uno dei numerosi line ending changers come unix2dos o todos?

So che SSIS consente di specificare solo le nuove righe per i terminatori di riga, così come l'importazione/esportazione guidata. Se questa è un'opzione, potresti guardarla. Devi definire le tue colonne nei file di dati in modo molto preciso ed è molto noioso per i file con molte colonne, ma generalmente ottieni molte altre opzioni come identificatori di campi quotati, ecc.

E non ho idea di cosa controlli il carattere \l rappresenta. Non sembra essere documentato da nessuna parte.

+0

Sono assolutamente d'accordo con l'uso di FTP/SFTP/FTPS per specificare la modalità ASCII e fare la conversione. Sfortunatamente, questo da solo non aiuta in quanto il problema non è con il Terminatore di riga: il numero di campi è diverso tra il file di origine e la tabella di destinazione e tuttavia non è specificato alcun file di formato per gestirlo. –

Problemi correlati