Der Import von CSV-Dateien in eine Datenbank stellt ein paar Tücken bereit. Nicht immer hat man die volle Kontrolle über die CSV-Datei. Besonders wenn die Daten von einem Fremdanbieter kommen, der nicht so auf die Qualität seiner Daten achtet. Da kann es schon mal vorkommen, dass die Spaltenanzahl in den Zeilen abweicht oder durch falsches Escaping von Trennzeichen(Delimeter) erst gar nicht richtig erkannt werden.
In meinem Beispiel verwende ich eine Datei mit Komma als Spaltentrennzeichen:
1,Wert1,01.01.2001
2,Wert2,12.06.1999
3,Wert3
4,Wert4,31.01.2002
5,Wert5,01.01.2011
6,Wert6
7,Wert7
8,Wert8,24.12.1953
9,Wert9,15.09.1988
10,Wert10
Würde man diese Datei mit den Standardeinstellungen importieren, käme die folgende Zuordnung heraus:

Um dieses Problem zu umgehen, gibt es einen Workaround: Man importiert die CSV-Datei komplett als eine Spalte und extrahiert sich anschließend per Transformationstask die vorhandene Spalten.
Hierfür löscht man im Connection-Manager für die CSV-Datei alle erkannten Spalten und legt eine einzelne neue Spalte an. Da in CSV-Dateien durchaus auch größere Daten vorkommen können (z.B. Beschreibungstexte), wähle ich Textdatenstrom als Datentyp. Daher muss später im Skript-Task auch noch eine Konvertierung vorgenommen werden.

Daraus ergibt sich folgende Zuordnung:

Jetzt kann man einen Datenflusstask anlegen, der wie folgt aussehen könnte:

Im Transformationstask muss dann die zuvor festgelegte Spalte aus dem Verbindungsmanager als Input definiert werden:

Des weiteren müssen die Ausgabespalten für den Transformationstask definiert werden:

Das Skript für den Transformationstask sieht wie folgt aus:
1: public class ScriptMain : UserComponent
2: {
3:
4: private char[] columnDelimiter = new char[] { ',' };
5:
6: public override void Eingabe0_ProcessInputRow(Eingabe0Buffer Row)
7: {
8: string[] columnValues = null;
9:
10: var blobLen = Convert.ToInt32(Row.Line.Length);
11: byte[] blobBytes = new byte[blobLen];
12: blobBytes = Row.Line.GetBlobData(0, blobLen);
13:
14: string line = System.Text.Encoding.Unicode.GetString(blobBytes);
15: columnValues = line.Split(columnDelimiter);
16:
17: // Test Correct number of rows
18: if (columnValues.Length == 3)
19: {
20: // Row is OK, output values
21: Row.Column1 = columnValues.GetValue(0).ToString();
22: Row.Column2 = columnValues.GetValue(1).ToString();
23: Row.Column3 = columnValues.GetValue(2).ToString();
24: }
25: else
26: {
27: //Row is not complete - Handle error
28: Row.Column1_IsNull = true;
29: Row.Column2_IsNull = true;
30: Row.Column3_IsNull = true;
31: }
32: }
33: }
In der columnDelimeter-Variable wird das Spaltentrennzeichen definiert. Da ich für den Eingabeparameter Line den Datentyp Textdatenstrom gewählt habe ist Row.Line vom Typ Microsoft.SqlServer.Dts.Pipeline.BlobColumn. Dieser wird in den Zeilen 10 –14 in einen String umgewandelt und kann anschließend per Split-Funktion geteilt werden. Ist die korrekte Anzahl an Spalten vorhanden (in meinem Fall 3), werden die Daten an die Ausgabeparameter übergeben. Andernfalls werden NULL-Werte zurückgegeben.
Die Daten importiert in eine Datenbank liefern folgendes Ergebnis:

Alle Zeilen, die nicht die korrekte Anzahl an Spalten haben werden ignoriert und es werden NULL-Werte in die Datenbank geschrieben. Dieses Verhalten kann man natürlich im Skript-Task nach seinen Wünschen anpassen.