it-swarm.com.de

Fügen Sie schnell 2 Millionen Zeilen in SQL Server ein

Ich muss ungefähr 2 Millionen Zeilen aus einer Textdatei einfügen.

Und mit dem Einfügen muss ich einige Stammtabellen erstellen. 

Was ist der beste und schnellste Weg, um einen so großen Datensatz in SQL Server einzufügen?

57
Wadhawan Vishal

Sie können es mit SqlBulkCopy class versuchen.

Ermöglicht das effiziente Massenladen einer SQL Server-Tabelle mit Daten aus eine andere Quelle.

Es gibt einen coolen blog post darüber, wie man ihn benutzen kann.

44
Soner Gönül
  1. Ich denke, es ist besser, wenn Sie Daten von Textdateien in DataSet lesen 

  2. Testen Sie SqlBulkCopy - Masseneinfügung in SQL von C # App aus

    // connect to SQL
    using (SqlConnection connection = 
            new SqlConnection(connString))
    {
        // make sure to enable triggers
        // more on triggers in next post
        SqlBulkCopy bulkCopy = 
            new SqlBulkCopy
            (
            connection, 
            SqlBulkCopyOptions.TableLock | 
            SqlBulkCopyOptions.FireTriggers | 
            SqlBulkCopyOptions.UseInternalTransaction,
            null
            );
    
        // set the destination table name
        bulkCopy.DestinationTableName = this.tableName;
        connection.Open();
    
        // write the data in the "dataTable"
        bulkCopy.WriteToServer(dataTable);
        connection.Close();
    }
    // reset
    this.dataTable.Clear();
    

oder 

nachdem Sie Schritt 1 oben ausgeführt haben

  1. Erstellen Sie XML aus DataSet 
  2. Übergeben Sie XML an die Datenbank und führen Sie Masseneinfügungen durch 

sie können diesen Artikel auf Einzelheiten überprüfen: Masseneinfügung von Daten mithilfe von C # DataTable und SQL Server OpenXML-Funktion

Aber es wurde nicht mit 2 Millionen Datensätzen getestet, es wird aber Speicherplatz auf der Maschine verbrauchen, da Sie 2 Millionen Datensätze laden und einfügen müssen.

57
Pranay Rana

Lösung für SqlBulkCopy:

Ich habe den StreamReader zum Konvertieren und Verarbeiten der Textdatei verwendet. Das Ergebnis war eine Liste meines Objekts. 

Ich habe eine Klasse erstellt, als Datatable oder einen List<T> und eine Puffergröße (CommitBatchSize). Es konvertiert die Liste mit einer Erweiterung (in der zweiten Klasse) in eine Datentabelle.

Es funktioniert sehr schnell. Auf meinem PC kann ich in weniger als 10 Sekunden mehr als 10 Millionen komplizierte Datensätze einfügen.

Hier ist die Klasse:

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DAL
{

public class BulkUploadToSql<T>
{
    public IList<T> InternalStore { get; set; }
    public string TableName { get; set; }
    public int CommitBatchSize { get; set; }=1000;
    public string ConnectionString { get; set; }

    public void Commit()
    {
        if (InternalStore.Count>0)
        {
            DataTable dt;
            int numberOfPages = (InternalStore.Count / CommitBatchSize)  + (InternalStore.Count % CommitBatchSize == 0 ? 0 : 1);
            for (int pageIndex = 0; pageIndex < numberOfPages; pageIndex++)
                {
                    dt= InternalStore.Skip(pageIndex * CommitBatchSize).Take(CommitBatchSize).ToDataTable();
                BulkInsert(dt);
                }
        } 
    }

    public void BulkInsert(DataTable dt)
    {
        using (SqlConnection connection = new SqlConnection(ConnectionString))
        {
            // make sure to enable triggers
            // more on triggers in next post
            SqlBulkCopy bulkCopy =
                new SqlBulkCopy
                (
                connection,
                SqlBulkCopyOptions.TableLock |
                SqlBulkCopyOptions.FireTriggers |
                SqlBulkCopyOptions.UseInternalTransaction,
                null
                );

            // set the destination table name
            bulkCopy.DestinationTableName = TableName;
            connection.Open();

            // write the data in the "dataTable"
            bulkCopy.WriteToServer(dt);
            connection.Close();
        }
        // reset
        //this.dataTable.Clear();
    }

}

public static class BulkUploadToSqlHelper
{
    public static DataTable ToDataTable<T>(this IEnumerable<T> data)
    {
        PropertyDescriptorCollection properties =
            TypeDescriptor.GetProperties(typeof(T));
        DataTable table = new DataTable();
        foreach (PropertyDescriptor prop in properties)
            table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType);
        foreach (T item in data)
        {
            DataRow row = table.NewRow();
            foreach (PropertyDescriptor prop in properties)
                row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
            table.Rows.Add(row);
        }
        return table;
    }
}

}

Hier ein Beispiel, wenn ich eine Liste meines benutzerdefinierten Objekts List<PuckDetection> (ListDetections) einfügen möchte:

var objBulk = new BulkUploadToSql<PuckDetection>()
{
        InternalStore = ListDetections,
        TableName= "PuckDetections",
        CommitBatchSize=1000,
        ConnectionString="ENTER YOU CONNECTION STRING"
};
objBulk.Commit();

Die BulkInsert-Klasse kann geändert werden, um bei Bedarf eine Spaltenzuordnung hinzuzufügen. Beispiel: Sie haben einen Identitätsschlüssel als erste Spalte (dies setzt voraus, dass die Spaltennamen in der Datentabelle der Datenbank entsprechen).

//ADD COLUMN MAPPING
foreach (DataColumn col in dt.Columns)
{
        bulkCopy.ColumnMappings.Add(col.ColumnName, col.ColumnName);
}
13
Amir

Ich bin vor kurzem auf dieses Szenario (weit über 7 Millionen Zeilen) gestoßen und habe die Verwendung von sqlcmd via Powershell (nach dem Analysieren von Rohdaten in SQL-Einfügeanweisungen) in Segmenten von 5.000 gleichzeitig (SQL kann nicht 7 Millionen Zeilen in einem Sammelauftrag verarbeiten oder sogar 500.000 Zeilen für diese Angelegenheit, sofern sie nicht in kleinere 5-KB-Teile zerlegt werden. Sie können dann jedes 5-KB-Skript nacheinander ausführen.) Ich musste den neuen Sequenzbefehl in SQL Server 2012 Enterprise einsetzen. Ich konnte keinen programmatischen Weg finden, um mit diesem Sequenzbefehl sieben Millionen Datenzeilen schnell und effizient einzufügen.

Zweitens müssen Sie beim Einfügen einer Million Zeilen oder mehr Daten in einer Sitzung die CPU- und Speicherauslastung (meistens Speicher) während des Einfügevorgangs beachten. SQL beansprucht Speicher/CPU mit einem Job dieser Größenordnung, ohne diese Prozesse freizugeben. Unnötig zu sagen, wenn Sie nicht genügend Rechenleistung oder Speicher auf Ihrem Server haben, können Sie ihn in kurzer Zeit ziemlich leicht zum Absturz bringen (was ich auf die harte Tour herausfand). Wenn Sie zu einem Punkt kommen, an dem Ihr Speicherverbrauch über 70-75% liegt, starten Sie den Server einfach neu und die Prozesse werden normal freigegeben. 

Ich musste eine Reihe von Test- und Fehlertests durchführen, um die Grenzen für meinen Server zu ermitteln (angesichts der begrenzten CPU-/Arbeitsspeicher-Ressourcen), bevor ich einen endgültigen Ausführungsplan erhalten konnte. Ich würde vorschlagen, dass Sie dasselbe in einer Testumgebung tun, bevor Sie diese in der Produktion einsetzen.

3
Techie Joe

Ich benutze das bcp-Dienstprogramm. (Bulk Copy-Programm) Ich lade jeden Monat etwa 1,5 Millionen Textdatensätze. Jeder Textdatensatz ist 800 Zeichen lang. Auf meinem Server dauert es etwa 30 Sekunden, um die 1,5 Millionen Textdatensätze in ein Verzeichnis einzufügen SQL Server-Tabelle.

Die Anweisungen für bcp finden Sie unter http://msdn.Microsoft.com/en-us/library/ms162802.aspx

3
Bill Edmett

Ich habe es mit dieser Methode versucht, und die Ausführungszeit für das Einfügen von Datenbanken wurde erheblich reduziert.

List<string> toinsert = new List<string>();
StringBuilder insertCmd = new StringBuilder("INSERT INTO tabblename (col1, col2, col3) VALUES ");

foreach (traverse your loop here)
{
      toinsert.Add(string.Format("( '{0}', '{1}', '{2}' )", "Val1", "Val2", "Val3"));
}
if (toinsert.Count != 0)
{
      insertCmd.Append(string.Join(",", toinsert));
      insertCmd.Append(";");
}
using (MySqlCommand myCmd = new MySqlCommand(insertCmd.ToString(), SQLconnectionObject))
{
      myCmd.CommandType = CommandType.Text;
      myCmd.ExecuteNonQuery();
}

* SQL-Verbindungsobjekt erstellen und dort ersetzen, wo ich SQLconnectionObject geschrieben habe.

1
Amey Vartak