Windows Azure Cloud Storage ermöglicht es Ihnen bereits ab 0,10€ pro GB/Monat die Vorteile der Cloud zu nutzen.
Willkommen bei dotnet-snippets.de! Snippet hinzufügen Login Registrieren
Snippets in der Datenbank: 1551 | Anzahl registrierter User: 1841 | Besucher online: 71
Hauptmenü
Home
Top Ten
Zufälliger Snippet
FAQs
.NET Community
dotnet-forum.de
dotnet-kicks.de
Social

RSS Feeds
Rss Alle Snippets
Rss C#
Rss VB.NET
Rss C++
Rss ASP.NET
Partner
Member of Microsoft Community Leader/Insider Program (CLIP)

CSV und XML-Datei Datenbank-Import (incl. valid-check)


Autor: knoyxz
Sprache: C#
Bewertung:
noch nicht bewertet
Anzahl der Aufrufe: 22783
  
Kick it on dotnet-kicks.de  

Beschreibung:

Hallo :-)

vor kurzem stand ich vor der Aufgabe bei einem MS SQL Server 2005,
mehrere tausend Datensätze aus einer CSV-Datei zu importieren.
Eine Funktion mehrere Datensätze auf einmal zu übertragen, wie bei mySQL,
gibt es nicht (erst ab Version 2008)...


Einzelne Insert-Befehle per Schleife auszuführen ist zwar möglich,
aber viel zu langsam (50.000 Datensätze >= 7 Min. im LAN)!


Hatte es dann zunächst wie folgt versucht (50k Datensätze <=10 Sek):
1) CSV-Datei von Sonderzeichen bereinigen und
2) als neue CSV speichern
3) Datenwerte gegen Schema prüfen
4) CSV als XML speichern
5) XML in DataTable und letztendlich
6) DataTable an Datenbank schicken

Dies funktionierte zwar, dazu sind (bei den Anforderungen siehe unten)
jedoch etwa 200-300 Zeilen Code und zwei Temp-Dateien/viel Speicher nötig.

Hier ein noch schnellerer und effektiverer Weg (50k <= 5 Sek) :-)
(Schritte 2 und 4 entfallen, im 5ten wahlweise xml/csv verarbeiten)

Alternativ könnte der "Bulk-Insert"-Befehl genutzt werden.
Dieser hätte u.a. folgende Nachteile:
- Bulk-Insert-Recht können nur global, nicht für einzelne DBs vergeben werden
- es funktioniert nicht mit Remote-Server (CSV wird nicht gefunden)
- CSV-Datei muss ggf. ebenfalls aufbereitet werden
- keine Möglichkeit zu überprüfen ob Daten kompatibel sind (valid-check)

So, für alle Experimentierfreudigen und "Hilfebedürftigen" ^_^, hier nun der Code:

EDIT: hab den Code etwas umgebaut:
- Aufruf per Parameter (QuellDatei, ZielTabelle, Verbindungsinfo)
- ganz unten seht ihr eine zweite Methode die ein Beispiel-Aufruf beinhaltet
- es kann nun sowohl eine XML als auch CSV-Datei importiert werden
- CSV/XML wird per OpenFileDialog ausgesucht
- InfoMeldung über Erfolg/Fehler (incl. Zeitmesser & verarbeitete Zeilen)
- Zuverlässigkeit gesteigert durch hinzufügen von einigen if-Abfragen und einer try-Klausel


Abgelegt unter: CSV, XML, Datei, Import, importieren, SQL, Log, Schema, DataAdapter, DataSet, DataTable, SQL-Server, MS SQL-Server 2005, Bulk-Insert.



C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//using System.Data;
//using System.Data.SqlClient;
//using System.IO;

private void CSV_XML_nach_Datenbank(String dateiPfad, String ZielTabelle, SqlConnection con)
{
//knoxyz 14.11.2008
    try
    {
        DateTime startzeit = System.DateTime.Now;

        //stellt Vermittler zwischen DatenQuelle(sqlServer) und DatenZiel(DataSet/DataTable) dar
        SqlDataAdapter da = new SqlDataAdapter();

        //enthält Tabellen
        DataSet ds = new DataSet();

        SqlCommand cmd = new SqlCommand();  //der sqlCommand nimmt SQL-Befehle entgegen
        cmd.Connection = con;               //generell festlegen, für welche Datenbank er diese Befehle ausführen soll

        //ggf Verbindung herstellen
        if (con.State != ConnectionState.Open) con.Open();

        String schemaPfad = Application.StartupPath + @"\Schema_" + ZielTabelle + ".xsd";

        //(Optional) Per TableMapping können Tabellen einen Namen gegeben werden. Alte Einträge entfernen
        da.TableMappings.Clear();

        //Welche Datenbankt
        cmd.CommandText = "SELECT * FROM " + ZielTabelle;


        da.SelectCommand = cmd;
        da.TableMappings.Add("Table", ZielTabelle);
        ds.Clear();
        if (File.Exists(schemaPfad) == false) //Fall keine Schemadatei vorhanden ist, jetzt eine erstellen
        {                                    //sofern Änderungen an der Datenbank gemacht wurden, die Schema.xsd einfach löschen
            da.Fill(ds); //Tabellen des DataAdapter in ein DataSet schreiben
            da.FillSchema(ds, SchemaType.Source); //Schema(Aufbau) der SQL-Tabellen auch im DataSet übernehmen
            ds.WriteXmlSchema(schemaPfad); //Schema lokal speichern
        }
        //ds.WriteXml(schemaPfad + ".xml");//Schema und Daten lokal speichern (BackUp)


        //Ziel-SQL-Tabelle
        //  anders als hier, schicke ich die Daten erst an eine Temp-Tabelle, die den gleichen Aufbau wie die eigentliche Ziel-Tabelle hat,
        //  und frage per Exist-Befehl anhand verschiedener Kriterien ab, ob Datensätze evtl. in der Ziel-Tab bereits vorhanden sind.
        DataTable dt = new DataTable("Artikelstamm");

        //um zu überprüfen ob die CVS-Daten zur SQL-Tabelle passen, hier das Schema der Datenbank übergeben
        dt.ReadXmlSchema(schemaPfad);

        //festlegen wo die CSV zu finden ist, die wir an die Datenbank schicken wollen

        //Wurde eine eine 'xml-Datei' ausgesucht..
        if (dateiPfad.EndsWith(".xml", StringComparison.OrdinalIgnoreCase))
        {
            dt.ReadXml(dateiPfad);
        }
        else
        {
            if (//...oder CSV/Textdatei?
                (dateiPfad.EndsWith(".csv", StringComparison.OrdinalIgnoreCase))
                || (dateiPfad.EndsWith(".txt", StringComparison.OrdinalIgnoreCase))
               )
            {
                StreamReader DateiLeser = new StreamReader(dateiPfad, System.Text.Encoding.Default);

                //Die nachfolgende Replace-Aufrufe entfernen Sonderzeichen, die mögliche Probleme verursachen könnten
                //Spaltenüberschriften auslesen und ggf. Punkte mit Unterstrich ersetzen
                String Zeileninhalt = DateiLeser.ReadLine().Replace(".", "_").Replace("\t", ";");
                DateiLeser.ReadLine(); //Leerzeile falls Daten erst in 3ter Zeile beginnen


                char trennzeichen = ';';
                //Der Inhalt der CSV-Datei wird Zeile für Zeile ausgelesen und in eine DataTable geschrieben
                while (DateiLeser.EndOfStream == false)
                {
                    Zeileninhalt = DateiLeser.ReadLine().Replace("'", "").Replace(";", "").Replace("=", "").Replace("\"", "");
                    Zeileninhalt = Zeileninhalt.Replace("\t", ";").Substring(0, Zeileninhalt.Length - 1);
                    //Zeileninhalt = Zeileninhalt.Replace("\t", ";"); //je nach CSV-Aufbau, diese oder Zeile darüber benutzen

                    //Inhalt einer Zeilen in Teilstrings zerlegen.
                    String[] csvZeilenInhalt = Zeileninhalt.Split(trennzeichen);

                    dt.Rows.Add(csvZeilenInhalt);
                }
                DateiLeser.Close();
            }
            else { MessageBox.Show("ungültige Datei ausgesucht", "falscher Dateityp"); }
        }
        if (dt.Rows.Count > 0) //wurden Daten aus der Quelldatei in DataTable kopiert?
        {
            //Jetzt liegt der Inhalt der CSV-Datei, auf Datentypen geprüft und mit Schema,
            // im DataTable und kann in einem Rutsch (als Objekt) an den Datenbank-Server geschickt werden :-)
            SqlBulkCopy bcopy = new SqlBulkCopy(con);
            bcopy.DestinationTableName = ZielTabelle;
            bcopy.WriteToServer(dt);
            //dt.WriteXml(dateiPfad + ".xml"); //csv als xml speichern

            String dauer = new DateTime(System.DateTime.Now.Ticks - startzeit.Ticks).ToString("mm 'Minuten und' ss 'Sekunden'");
            MessageBox.Show(dt.Rows.Count + " Zeilen wurden in " + dauer + " verarbeitet :-)",
                            "Import war erfolgreich :-)");
            dt.Clear();
            //try
            //{
            //(optional) Log-Datei der Datenbank verkleinern
            //cmd.CommandText = "DBCC SHRINKDATABASE(N'" + con.Database + "' )";
            //cmd.ExecuteNonQuery();
            //}
            //catch (Exception e) { MessageBox.Show(e.Message); }
        }
    }
    catch (Exception importFehler)
    {
        MessageBox.Show(importFehler.Message, "Import-Fehler");
    }
}



//Hier folgt ein Beispiel wie die Methode aufgerufen werden könnte
private void BeispielZumAufrufDerImportMethode_Click(object sender, EventArgs e)
{   //Vorbereitung

    String zielTabelle = "Artikelstamm";

    String dateiPfad = String.Empty;
    OpenFileDialog dateiAuswaehlen = new OpenFileDialog();
    dateiAuswaehlen.Filter = "csv-Datei (*.txt)|*.txt"
                             + "|xml-Datei (*.xml)|*.xml"
                             + "|alle Dateien (*.*)|*.*";
    dateiAuswaehlen.Title = zielTabelle;

    if (dateiAuswaehlen.ShowDialog() == DialogResult.OK) dateiPfad = dateiAuswaehlen.FileName;


    //Verbindungsaufbau definieren
    SqlConnectionStringBuilder conBuilder = new SqlConnectionStringBuilder();
    conBuilder.DataSource = "(local)";        //kann auch eine IP oder Name sein, dann ohne Klammern
    conBuilder.InitialCatalog = "knoxyz";
    conBuilder.ConnectTimeout = 10;
    conBuilder.IntegratedSecurity = true; //oder User & Password
    //conBuilder.UserID = "knoxyz";
    //conBuilder.Password = "xyz";

    SqlConnection con = new SqlConnection(conBuilder.ConnectionString);
    //Prüfen ob Parameter Werte haben, falls nicht kein Aufruf
    if ((dateiPfad != String.Empty) && (zielTabelle.Trim() != "") && (con.ConnectionString != ""))
        CSV_XML_nach_Datenbank(dateiPfad, zielTabelle, con);
    else MessageBox.Show("ParameterWerte für DB-Import stimmen nicht");
}
Sie haben Fragen zu diesem Snippet oder brauchen Hilfe bei der .NET Entwicklung?
Freundliche und kompetente Entwickler helfen Ihnen gern weiter im Forum für .NET Entwicklung.



Kommentare:
(Zum Schreiben von Kommentaren bitte anmelden.)

knoyxz schrieb am:  13.11.2008 20:12:11

Gibt es eine Möglichkeit sein "Snippet" zu bearbeiten?

Diese usings werden benötigt:
using System.Data;
using System.Data.SqlClient;
using System.IO;
Jan Welker schrieb am:  13.11.2008 20:19:09

Hallo,
ja das geht, mit dem blauen Notizblock unter dem Snippet.
Jan
knoyxz schrieb am:  13.11.2008 20:36:00

Dankeschön Jan :-)
Günther Foidl schrieb am:  13.11.2008 22:13:47

Der ConnectionString kann auch mit dem SqlConnectionStringBuilder erstellt werden.
knoyxz schrieb am:  13.11.2008 23:02:18

Gute Einwand den SqlConnectionStringBuilder zu benutzen,
da dieser, noch vor dem Versuch eine Verbindung aufzubauen,
überprüft, ob nur gültige Werte für die Verbindungseigenschaften angegeben wurden.
(Bsp: für TimeOut keine Buchstaben zugelassen, ect)

Habe es im Code entsprechend angepasst.


Diese Snippets könnten für Sie interessant sein:
[C#] Eventlog Eintrag finden
[C#] CSV Dateien schreiben
[C#] Generischer Converter für CSV Dateien
[VB.NET] ASP.Net Gridview to Excel
[C#] CSV Datei in DataTable einlesen
[C#] DataTable in CSV Datei speichern
[C#] CSV Datei in Datatable einlesen
[C#] arbeiten mit einem struct-Typ. Auflisten, zählen
[C#] Objekt in XML speichern (Serialisieren)
[C#] Treeview in XML schreiben
[ASP.net] XML - HTML Transformation
[C#] XML Encoding eines XmlDocument ändern
[C#] XMLDocument in XDocument konvertieren
[C#] Rss Feed in XMLDocument laden
[VB.NET] XML Datei in DataSet einlesen
[VB.NET] Objekt mit dem XmlSerializer serialisieren
[VB.NET] XML Datei mit dem XmlSerializer deserialisieren
[C#] Image zu Base64 konvertieren und zurück
[C#] Generische XML-Serialisierung
[C#] Währungskurse in Datenbank speichern
[VB.NET] Einfaches Erstellen einer XML Datei in .Net
[C#] XML in DataTable laden
[C#] Intellisense Unterstützung für XML Dateien für LINQ
[C#] user.config und generische Listen
[C#] Dataset verschlüsseln
[C#] Xml Datei entschlüsseln
[ASP.net] XML Daten über einen Internet Proxy abfragen
[C#] XML-Programmkonfiguration / -Steuerung
[C#] Formatieren von Sonderzeichen für XML
[C#] Binärdatei in XML File speichern
[C#] Binärdatei aus XML Datei auslesen und abspeichern
[C#] leere Knoten aus XML Document entfernen
[C#] XML Kommentare entfernen
[C#] Austauch von kritischen Zeichen in einem String...
[C#] Excel-Export ohne Excel (auch für Web)
[C#] Ini-Datei-Klasse
[C#] TreeView Export To Xml OR Import from XMl
[C#] Autom. Laden & Speichern von Position und Größe eines Forms
[VB.NET] Bild als XML Datei Speichern
[C#] Ein Object serialisieren
[C#] Ein Object deserialisieren
[C#] Konvertiert Code nach Example für XML-Kommentar
[C#] XMLIO - einfachstes (De)serialisieren von/zu XML-Dateien
[C#] Generisch XML De-/ Serialisieren
[C#] XML generieren mit Linq to XML
[C#] 3 arten der Serialisierung bzw Deserialisierung
[C#] Spracherkennung
[C#] Wunderground Wettervorhersage
[C#] Mit LINQtoXML XML Dateien erzeugen bzw. abfragen
[C#] ini Datei anlegen - mit Dictionary
[C#] Typen mit dem XmlSerializer (de-)serialisieren
[VB.NET] Datei im Explorer anzeigen und markieren
[C#] Datei anlegen
[C#] Aktuelles Verzeichnis auslesen
[VB.NET] CRC32 Hash einer Datei berechnen
[VB.NET] Den MD5 Hash einer Datei ermitteln
[VB.NET] Datei mittels Windows Dialog löschen
[VB.NET] Datei mittels Windows Dialog verschieben
[VB.NET] Datei mittels Windows Dialog kopieren
[VB.NET] Den SHA1 Hash einer Datei ermitteln
[VB.NET] Prüfen ob Datei gerade benutzt wird
[C#] Dateien in die Zwischenablage kopieren
[ASP.net] Textdatei zum Download anbieten
[C#] MD5-Hash von Dateien ermitteln
[C#] Arraylist in Textdatei schreiben
[C#] Textdatei in Arraylist speichern
[C#] SHA-1 Hash einer Datei erzeugen
[VB.NET] Datei Paketweise Kopieren
[ASP.net] Datei nach Bildupload prüfen
[C#] Dateien suchen mit LINQ
[C#] Datei/Ordner durch Erweiterungsmethoden abfragen
[C#] Datei-Eigenschaften zeigen (Eigenschaften-Fenster)
[C#] Create Nice File Size in C#
[VB.NET] Programmpfad mittels Erweiterung ermitteln in VB
[VB.NET] Dateien nach Datum sortieren
[C#] Datei umbenennen
[C#] Datei im Windows-Explorer anzeigen und markieren
[VB.NET] File Owner / File Besitzer
[VB.NET] wie in der CMD Pfad zu Datei ermitteln anhand des EXE-namens
[C#] Anzahl der Dateien in einem Ordner ermitteln
[VB.NET] Dateigröße als formatierter String
[VB.NET] Alle Dateien eines Ordners auflisten
[VB.NET] Den kurzen Pfad einer Datei ermitteln (8.3 Format)
[VB.NET] Klasse für Dateisuche
[C#] Erzeugen eines zufälligen Strings
[VB.NET] Dummy Datei erstellen
[C#] Datei in Papierkorb verschieben
[C#] Zeilen einer Datei zählen
[C#] Schreibschutz einer Datei setzen / entfernen
[ASP.net] Eine Datei zum Download anbieten
[VB.NET] Prüfen ob Dateiattribut vorhanden ist
[VB.NET] Bestimmtes Dateiattribut entfernen
[VB.NET] Datei ein bestimmtes Attribut geben
[VB.NET] Prüfen ob Datei eine EXE Datei ist
[C#] Prüfen, ob eine Datei ausfürbar ist (.exe, .bat, etc.)
[C#] Alle Dateien eines Ordners auflisten - C# Version -
[C#] eindeutigen Dateinamen ermitteln
[VB.NET] Datei-Inhalt anzeigen
[C#] Dateien und Ordner rekursiv löschen
[C#] Stream in Datei umleiten
[C#] Ordnergröße ermitteln
[C#] Automatisches Umbenennen eines Pfades
[C#] Kapazität mit Einheit darstellen
[C#] LogWriter klasse
[C#] MIMETypeManager
[C#] externes Programm aufrufen (andere Version) mit Parameter
[C#] Herausfinden welches Programm ein bestimmtes Dokument öffnet
[VB.NET] Gibt das Bytearray einer Datei zurück
[C#] Datei ein bestimmtes Attribut geben/setzen
[VB.NET] Klasse für SimpleFileVerification (SFV)
[C#] Laden und speichern von komprimierten Binärdaten
[C#] Datei einlesen und als ArrayList jede Zeile zurückliefern
[C#] Parallele Dateisuche über mehrere Laufwerke
[C#] Byte Größenangaben als String formatieren (KB, MB, GB, ...)
[C#] Textdatei einlesen auf verschiedene Arten
[C#] Datei Teilweise einlesen (gestückelt)
[C#] Bestimmte Zeile einer Datei löschen
[C#] File Container
[C#] PDF Dateien erstellen
[C#] Ist Datei beschreibbar
[C#] Sichern einer MS-SQLServer Datenbank aus C#
[C#] Datei mi GZip packen
[C#] Dummy Files erzeugen. ( Mit Inhalt )
[C#] Byte-Array in Struktur kopieren
[C#] Text in Textdateien suchen
[VB.NET] Prüfen, ob eine Datei überschrieben werden kann
[C#] Schutz vor SQL-Injektion
[C#] Größe einer SQL-Server Datenbank ermitteln
[VB.NET] Größe einer SQL-Server Datenbank ermitteln
[VB.NET] Einfache Ähnlichkeitssuche (SQL)
[VB.NET] SQL INSERT-Stringbuilder Function
[VB.NET] Mini-Sql-Command-Generator
[C#] Text Resourcen aus Assembly auslesen
[C#] T-SQL-Stored-Proc Generieren
[VB.NET] DataGridView Databinding mit LINQtoSQL
[VB.NET] Master-Detail Ansicht auf SQL Server-Daten mit LINQ-to-SQL
[C#] SQL Server Daten in Exel-Datei exportieren mit LINQ
[C#] Hole alle Datenbanken eines SQL Servers
[C#] SimpleDb
[C#] SqlDataSource.FilterExpression auf Bitflag filtern
[VB.NET] SQLCMD Parameter debuggen
[C#] Mit LINQ to SQL auf eine Datenbank zugreifen
[C#] Trivialer Datenbankzugriff
[C#] Fire-and-forget SQL-query (in 3 Zeilen)
[C#] Datenbankverbindung herstellen
[ASP.net] Message in Logfile schreiben
[C#] MethodStopwatch - timemeasuring class for methods
[C#] Datenupdate mittels OleDbCommandBuilder
[ASP.net] Dataset nur zum Teil im Gridview anzeigen
[VB.NET] Convert DataGridViewRow nach DataRow
[VB.NET] Convert DataGridViewRow nach Objekt-Typ
[C#] RSS Feed to Dataset
[C#] Dataset mit Stored Procedure füllen
[C#] XMLDocument in DataSet konvertieren
[C#] Connection-String Dialog öffnen
[C#] DataSet to Object Array Extension Method
[C#] Data Access Layer: Datenzugriff mit Enterprise Library 2.0
[C#] Excel: DataTable mittels OLEDB in Excel-Dokument exportieren
[C#] Methode CheckMaxLength() als Ereignis für DataTable
[C#] Pivot-Tabelle erstellen
[C#] doppelte Zeilen aus DataTable löschen
[C#] Prüfen ob eine Spalte in einer Datatable existiert
[C#] Zufällige Row aus DataTable
[C#] LINQ-Ergebnis in DataTable kopieren
[VB.NET] DataTable: schnelle Erstellung eines DataRow-Objekts
[C#] Verfügbare SQL-Server abfragen

schlecht sehr gut
1 2 3 4 5 6 7 8 9 10
Nur angemeldete User können Snippets bewerten.