Wie führe ich ein großes Skript mit vielen Einfügungen aus, ohne dass mir der Speicher ausgeht?


28

Frage:

Ich habe ein Skript mit rund 45.000 Einfügungen aus ausgewählten Anweisungen. Wenn ich versuche, es auszuführen, erhalte ich eine Fehlermeldung, die besagt, dass mir der Speicher ausgeht. Wie kann ich dieses Skript ausführen?

Kontext:

  1. Einige neue Datenfelder wurden hinzugefügt, damit eine App mit einer anderen App, die der Client verwendet, gut funktioniert.
  2. Vom Client wurde eine Tabelle mit Daten abgerufen, die die aktuellen Datenelemente den Werten für diese neuen Felder zuordnet.
  3. Konvertierte Tabelle zum Einfügen von Anweisungen.
  4. Wenn ich nur einige der Anweisungen ausführe, funktioniert dies, das gesamte Skript jedoch nicht.
  5. Nein, es gibt keine Tippfehler.

Wenn es einen anderen Weg gibt, wie ich diese Daten laden soll, züchtige ich mich und lass es mich wissen.


Ähnliche Frage auf SO: ( stackoverflow.com/questions/222442/… ) Nicht sicher, ob die Antwort hilft
Jumpdart

Antworten:


17

Die maximale Stapelgröße für SQL Server 2005 beträgt 65.536 * Netzwerkpaketgröße (Network Packet Size, NPS), wobei NPS normalerweise 4 KB beträgt. Das klappt auf 256 MB. Das würde bedeuten, dass Ihre Einfügeanweisungen durchschnittlich 5,8 KB groß sind. Das scheint nicht richtig zu sein, aber vielleicht gibt es fremde Räume oder etwas Ungewöhnliches.

Mein erster Vorschlag wäre, nach jeder INSERT-Anweisung eine "GO" -Anweisung einzufügen. Dadurch wird Ihr einzelner Stapel mit 45.000 INSERT-Anweisungen in 45.000 separate Stapel aufgeteilt. Dies sollte leichter zu verdauen sein. Seien Sie vorsichtig, wenn einer dieser Einschübe versagt, kann es schwierig sein, den Schuldigen zu finden. Vielleicht möchten Sie sich mit einer Transaktion schützen. Sie können diese Anweisungen schnell hinzufügen, wenn Ihr Editor über eine gute Such- und Ersetzungsfunktion (mit der Sie nach Rückgabezeichen wie \ r \ n suchen und diese ersetzen können) oder eine Makrofunktion verfügt.

Der zweite Vorschlag besteht darin, einen Assistenten zu verwenden, um die Daten direkt aus Excel zu importieren. Der Assistent erstellt hinter den Kulissen ein kleines SSIS-Paket für Sie und führt es dann aus. Es wird dieses Problem nicht haben.


2
Ein GOnach jeder Aussage? Nun, ich denke, wenn Sie sie mit einem anderen Skript generieren, das in Ordnung ist. Ansonsten würde ich nur alle 1000 INSERTs eine setzen. Warum nicht alle Zeilen in eine temporäre Tabelle oder eine Tabellenvariable laden und sie dann von dort auf einmal in die Zieltabelle laden, um die Transaktion atomar zu machen und die Größe der Transaktion zu minimieren?
Nick Chammas

Eine 1000 ist genauso gut wie eine 1, aber schwerer zu zählen. Um ehrlich zu sein, könnte er mit nur einer GO-Aussage davonkommen, auf halber Strecke, in der Nähe der Aussage 21.500. Ich mag das GO-Update, weil es keine komplizierte Bearbeitung des aktuellen Skripts oder das Zählen von INSERT-Anweisungen erfordert (die möglicherweise nicht direkt Zeilennummern zugeordnet sind).
strait

2
Sicherlich ist auch eine schlechte Annäherung von 1000 Aussagen gut genug. :)
Nick Chammas

1
Das Hinzufügen der GOs war eine schnelle und einfache Lösung. Das 25-MB-Skript läuft in etwas weniger als 9 Minuten ohne Probleme. Wollte es als Skript haben, um es in unserem Standard-Patch-Bereitstellungsprozess zu belassen, wenn es ausgeht.
Spaghetticowboy

14

BULK INSERToder bcpscheinen angemessenere Optionen als 45.000 Einfügeanweisungen.

Wenn Sie bei den Einfügeanweisungen bleiben müssen, würde ich ein paar Optionen in Betracht ziehen:

A: Verwenden Sie Transaktionen und verpacken Sie Stapel mit jeweils 100 oder 500 oder 1000 Anweisungen, um die Auswirkungen auf das Protokoll und den Stapel zu minimieren. z.B

BEGIN TRANSACTION;
INSERT dbo.table(a, ...) SELECT 1, ...
INSERT dbo.table(a, ...) SELECT 2, ...
...
INSERT dbo.table(a, ...) SELECT 500, ...
COMMIT TRANSACTION;
GO

BEGIN TRANSACTION;
INSERT dbo.table(a, ...) SELECT 1, ...
INSERT dbo.table(a, ...) SELECT 2, ...
...
INSERT dbo.table(a, ...) SELECT 500, ...
COMMIT TRANSACTION;
GO

B: Verwenden Sie anstelle einzelner insert-Anweisungen jeweils UNION ALL100 oder 500 Anweisungen, z

INSERT dbo.table(a, ...)
SELECT 1, ...
UNION ALL SELECT 2, ...
...
UNION ALL SELECT 500, ...
GO

INSERT dbo.table(a, ...)
SELECT 501, ...
UNION ALL SELECT 502, ...
...
UNION ALL SELECT 1000, ...
GO

Ich habe die Fehlerbehandlung der Kürze halber weggelassen, aber der Punkt ist, dass ich niemals versuchen würde, einen einzelnen Stapel von 45.000 einzelnen Anweisungen an SQL Server zu senden.


1
Schade, dass das OP keine Tabellenwertkonstruktoren verwenden kann , eine Funktion von 2008+. Er müsste die Beilagen immer noch in Gruppen von 1000 Zeilen stapeln. Dies ist das Maximum, das Sie mit einem TVC zusammen gruppieren können.
Nick Chammas

Das würde mein erster Vorschlag sein, bis ich das Versions-Tag sah.
Aaron Bertrand

2
@ NickChammas - Die Leistung dieser verschlechtert sich nicht linear mit der Anzahl der Werte Klauseln BTW . Ich habe ein Verbindungselement mit einem Repro zum Einfügen von 1000 Zeilen mit 10 VARCHAR(800)Spalten im Jahr 2008 mit einer Kompilierungszeit von 12,5 Minuten auf meiner 2008-Entwicklungsinstanz eingereicht, da es viele unnötige Arbeiten zum Vergleichen von Werten ausführt, anstatt sie nur einzufügen (führt viel aus) schneller, wenn parametriert und keine Werte zu sehen). Obwohl 2012 stark verbessert, ist das nichtlineare Muster immer noch vorhanden und sollte in der Version danach behoben werden.
Martin Smith

9

Ich bin nicht sicher, warum Sie den Fehler "out of memory" erhalten, aber es gibt einen einfacheren Ansatz.

Wenn Sie die Daten aus der Tabelle in ein getrenntes Format (z. B. csv) exportieren können, können Sie die Daten mit dem Datenimport-Assistenten in SSMS für Sie einfügen:

SSMS-Datenimporttask.


Das ist hilfreich, aber ich habe keinen Zugriff auf die Client-Datenbanken. Ich muss Patches und Datenladungen in Skripten vorbereiten
spaghetticowboy

0

Erstellen Sie mit mehreren SqlBulkCopy eine temporäre Tabelle. Fügen Sie neue Daten in die temporäre Tabelle ein und fügen Sie die Daten in der temporären Tabelle in die vorhandene Tabelle ein. Beispiel mit der C # SqlBulkCopy.WriteToServer-Methode (DataTable) . Ich hoffe es hilft


0

Ja, das können wir, ich habe es mit einem BCP- Ansatz (Bulk Copy Program) versucht , um ein OutOfMemory- Problem zu vermeiden .

Hinweis : Auf SQL Server 2014 ausprobiert.

In BCP müssen wir zuerst die Quellendatenbankdaten in eine BCP- Datei (im lokalen Verzeichnisordner) exportieren und dann diese BCP- Datei in die Zieldatenbank importieren .

Bildbeschreibung hier eingeben

Nachfolgend sind die Schritte für die Kuchenwanderung aufgeführt:

Hinweis:

a) Stellen Sie sicher, dass in der Zieldatenbank eine leere Tabelle vorhanden ist

b) Stellen Sie sicher, dass der Ordner Temp im Laufwerk C vorhanden ist

  1. Erstellen Sie eine bat-Datei mit dem Namen Export_Data.bat mit dem folgenden Befehl:

    bcp.exe [Source_DataBase_Name].[dbo].[TableName] OUT "C:\Temp\TableName.bcp" -S "Computer Name" -U "SQL Server UserName" -P "SQL Server Password" -n -q 

    Pause

  2. Führen Sie diese bat-Datei aus. Dadurch wird eine bcp- Datei im Temp- Ordner generiert

  3. Erstellen Sie dann eine weitere bat-Datei mit dem Namen Import_Data.bat mit dem folgenden Befehl:

    bcp.exe [Destination_DataBase_Name].[dbo].[TableName] IN "C:\Temp\TableName.bcp" -S "Computer Name" -U "SQL Server UserName" -P "SQL Server Password" -n -q 

    Pause

Und es geht los!


Fehlermeldung "Für die Eingabe-, Ausgabe- oder Formatierungsoptionen ist ein gültiger Tabellenname erforderlich." beim Versuch, Daten zu exportieren.
Sen Jacob

1
Könnten Sie den Befehl, den Sie ausprobiert haben, mit allen Attributwerten einfügen. Befolgen Sie bitte das folgende Beispiel: bcp.exe ExportDB.dbo.AddressCountry OUT "C: \ Temp \ AddressCountry.bcp" -S "IN-L20054" -U "sa" -P "sa" -n -q In dieser [ExportDB -> Quell-DB, AddressCountry-> Tabelle in Quell-DB, IN-L20054 -> Computername, "sa" ist der Benutzername / Kennwort der
Datenbank

Ich habe es jetzt nicht. Am Ende habe ich die Funktion zum Importieren von Daten in SSMS verwendet. Anschließend wurde die Ziel-DB (Version 14.0) über eine MS OLE DB-Verbindung mit der Quell-DB (Version 15.0) verbunden, und es war ziemlich schnell, mehrere Millionen Datenzeilen zu importieren. Vielen Dank!
Sen Jacob
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.