it-swarm.com.de

Bulk Insert in Oracle-Datenbank: Was ist besser: FOR Cursor-Schleife oder ein einfaches Select?

Was wäre eine bessere Option für das Einfügen von Massen in eine Oracle-Datenbank? Eine FOR-Cursor-Schleife

DECLARE
   CURSOR C1 IS SELECT * FROM FOO;
BEGIN
   FOR C1_REC IN C1 LOOP
   INSERT INTO BAR(A,
                B,
                C)
          VALUES(C1.A,
                 C1.B,
                 C1.C);
   END LOOP;
END

oder eine einfache Auswahl wie:

INSERT INTO BAR(A,
                B,
                C)
        (SELECT A,
                B,
                C
        FROM FOO);

Irgendein bestimmter Grund wäre besser?

24
Sathyajith Bhat

Ich würde die Auswahloption empfehlen, da Cursor länger dauern.
Die Verwendung von Select ist für jeden, der Ihre Abfrage ändern muss, viel einfacher zu verstehen

28
Josh Mein

Die allgemeine Faustregel lautet: Wenn Sie dies mit einer einzelnen SQL-Anweisung anstelle von PL/SQL tun können, sollten Sie dies tun. Es wird normalerweise effizienter sein.

Wenn Sie jedoch (aus irgendeinem Grund) mehr prozedurale Logik hinzufügen müssen, müssen Sie möglicherweise PL/SQL verwenden. Sie sollten jedoch Massenoperationen anstelle der zeilenweisen Verarbeitung verwenden. (Hinweis: In Oracle 10g und höher verwendet die FOR-Schleife automatisch BULK COLLECT, um 100 Zeilen gleichzeitig abzurufen; die Einfügeanweisung wird jedoch immer noch Zeile für Zeile ausgeführt.).

z.B.

DECLARE
   TYPE tA IS TABLE OF FOO.A%TYPE INDEX BY PLS_INTEGER;
   TYPE tB IS TABLE OF FOO.B%TYPE INDEX BY PLS_INTEGER;
   TYPE tC IS TABLE OF FOO.C%TYPE INDEX BY PLS_INTEGER;
   rA tA;
   rB tB;
   rC tC;
BEGIN
   SELECT * BULK COLLECT INTO rA, rB, rC FROM FOO;
   -- (do some procedural logic on the data?)
   FORALL i IN rA.FIRST..rA.LAST
      INSERT INTO BAR(A,
                      B,
                      C)
      VALUES(rA(i),
             rB(i),
             rC(i));
END;

Das Vorstehende hat den Vorteil, dass Kontextwechsel zwischen SQL und PL/SQL minimiert werden. Oracle 11g bietet auch eine bessere Unterstützung für Datensatztabellen, sodass Sie nicht für jede Spalte eine separate PL/SQL-Tabelle benötigen.

Wenn das Datenvolumen sehr groß ist, ist es auch möglich, den Code zu ändern, um die Daten in Batches zu verarbeiten.

21
Jeffrey Kemp

Wenn Ihr Rollback-Segment/Rückgängig-Segment die Größe der Transaktion aufnehmen kann, ist Option 2 besser. Option 1 ist nützlich, wenn Sie nicht über die erforderliche Rollback-Kapazität verfügen und die große Einfügung in kleinere Commits aufteilen müssen, damit Sie nicht zu kleine Rollback-/Rückgängig-Segment-Fehler erhalten. 

5
MichaelN

Ein einfaches Einfügen/Auswählen wie bei der zweiten Option ist bei weitem vorzuziehen. Für jede Einfügung in der ersten Option benötigen Sie einen Kontextwechsel von pl/sql zu sql. Führen Sie jedes mit trace/tkprof aus und überprüfen Sie die Ergebnisse.

Wenn, wie Michael erwähnt, Ihr Rollback die Anweisung nicht verarbeiten kann, müssen Sie von Ihrem Datenbankadministrator mehr Informationen erhalten. Festplatten sind billig, während Teilergebnisse, die sich aus dem Einfügen Ihrer Daten in mehreren Durchgängen ergeben, möglicherweise recht teuer sind. (Mit einem Insert ist fast kein Undo verbunden.)

5
Scott Swank

Ich denke, dass in dieser Frage eine wichtige Information fehlt.

Wie viele Datensätze werden Sie einfügen?

  1. Wenn von 1 bis cca. 10.000 dann sollten Sie die SQL-Anweisung verwenden (Wie sie sagten, ist es leicht zu verstehen und leicht zu schreiben).
  2. Wenn von cca. 10.000 to cca. 100.000 dann sollten Sie den Cursor verwenden, aber Sie sollten eine Logik hinzufügen, um alle 10.000 Datensätze zu bestätigen. 
  3. Wenn von cca. 100.000 bis Millionen dann sollten Sie Bulk Collect verwenden, um eine bessere Leistung zu erzielen.
3
sulica

Wie Sie den anderen Antworten entnehmen können, stehen Ihnen viele Optionen zur Verfügung. Wenn Sie nur <10.000 Zeilen machen, sollten Sie die zweite Option wählen. 

Kurz gesagt für ca. 10k den ganzen Weg ein <100k zu sagen. Es ist eine Art Grauzone. Viele alte Geezer bellen in großen Rollback-Segmenten. Aber ehrlich gesagt, haben Hardware und Software erstaunliche Fortschritte gemacht, wenn Sie Option 2 für viele Datensätze nutzen können, wenn Sie den Code nur ein paar Mal ausführen. Andernfalls sollten Sie wahrscheinlich alle 1k-10k-Zeilen festschreiben. Hier ist ein Ausschnitt, den ich benutze. Ich mag es, weil es kurz ist und ich keinen Cursor angeben muss. Darüber hinaus bietet es die Vorteile von Sammelgut und für alles.

begin
    for r in (select rownum rn, t.* from foo t) loop
        insert into bar (A,B,C) values (r.A,r.B,r.C);
        if mod(rn,1000)=0 then
            commit;
        end if;
    end;
    commit;
end;

Ich habe dieses link von der Oracle-Site gefunden, das die Optionen detaillierter veranschaulicht.

2

Ich mache auch kein tägliches komplettes Nachladen von Daten. Sagen Sie zum Beispiel, ich lade meine Denver-Site. Es gibt andere Strategien für Echtzeit-Deltas.

Ich verwende eine create table SQL, da ich festgestellt habe, dass sie fast so schnell ist wie ein Massenladen Zum Beispiel wird eine create table -Anweisung verwendet, um die Daten zu inszenieren und die Spalten in den richtigen Datentyp umzuwandeln:

CREATE TABLE sales_dataTemp als select Cast (Spalte1 als Datum) als SALES_QUARTER, Cast (Umsatz als Nummer) als SALES_IN_MILLIONS, .... FROM TABELLE 1;

diese temporäre Tabelle spiegelt die Struktur meiner Zieltabelle genau wider. Diese Liste ist nach Site partitioniert . Ich tausche dann die Partition mit der DENVER-Partition aus und habe einen neuen Datensatz.

0
Hughsmg

Sie können verwenden:

Sammeln Sie zusammen mit FOR ALL den Namen Bulk binding

Weil der PL/SQL-Operator forall für einfache Tabelleneinfügungen 30x schneller ist. 

BULK_COLLECT und Oracle FORALL zusammen werden diese beiden Funktionen als Bulk Binding bezeichnet. Massenbindungen sind eine PL/SQL-Technik, bei der anstelle mehrerer einzelner SELECT-, INSERT-, UPDATE- oder DELETE-Anweisungen alle Daten auf einmal abgerufen oder in einer Tabelle gespeichert werden. Dadurch wird der Kontextwechsel vermieden, den Sie erhalten, wenn die PL/SQL-Engine an die SQL-Engine übergeben werden muss, dann wieder an die PL/SQL-Engine usw., wenn Sie einzeln auf die Zeilen zugreifen. Um Massenbindungen mit den Anweisungen INSERT, UPDATE und DELETE auszuführen, schließen Sie die SQL-Anweisung in eine FORALL-Anweisung von PL/SQL ein. Um Massenbindungen mit SELECT-Anweisungen auszuführen, schließen Sie die BULK COLLECT-Klausel in die SELECT-Anweisung ein, anstatt INTO zu verwenden.

Es verbessert die Leistung.

0
user2001117