2009-10-21 5 views
5

Ich habe ein paar große Tabellen (188m und 144m Zeilen) Ich muss aus Ansichten zu füllen, aber jede Ansicht enthält ein paar hundert Millionen Zeilen (zusammenziehen pseudo-dimensional modellierte Daten in eine flache Form). Die Schlüssel auf jeder Tabelle sind über 50 zusammengesetzte Bytes von Spalten. Wenn die Daten in Tabellen wären, könnte ich immer daran denken, sp_rename zu verwenden, um die andere neue Tabelle zu erstellen, aber das ist nicht wirklich eine Option.Batch-Commit bei großer INSERT-Operation in nativem SQL?

Wenn ich eine einzelne INSERT-Operation, verwendet der Prozess eine große Menge an Transaktionsprotokoll Speicherplatz, typischerweise feilte es und eine Menge Ärger mit den DBAs. (Und ja, das ist wahrscheinlich ein Job, den die Datenbankadministratoren handhaben/entwerfen/entwerfen sollten)

Ich kann SSIS verwenden und die Daten mit Batch-Commits in die Zieltabelle streamen (aber dazu müssen die Daten über das Netzwerk übertragen werden) , da wir keine SSIS-Pakete auf dem Server ausführen dürfen).

Irgendwelche anderen Dinge als den Prozess in mehrere INSERT-Operationen unter Verwendung einer Art Schlüssel aufzuteilen, um die Zeilen in verschiedene Stapel zu verteilen und eine Schleife zu machen?

+1

Wenn Tabellenpartitionierung eine Option ist, können Sie die Einfügungen durch den Paritionswert teilen. Könnte es schneller machen, die resultierenden Teilmengen zusammenzufassen. –

Antwort

4

Sie könnten Ihre Daten partitionieren und Ihre Daten in eine Cursorschleife einfügen. Das wäre fast das Gleiche wie SSIS-Batch-Insert. Aber läuft auf deinem Server.

create cursor .... 
select YEAR(DateCol), MONTH(DateCol) from whatever 

while .... 
    insert into yourtable(...) 
    select * from whatever 
    where YEAR(DateCol) = year and MONTH(DateCol) = month 
end 
+0

Dies war letztlich die Strategie, die ich verwendet habe. –

1

Sie könnten den BCP-Befehl verwenden, um die Daten zu laden und die Chargengröße Parameter verwenden

http://msdn.microsoft.com/en-us/library/ms162802.aspx

zweistufiger Prozess

  • BCP OUT-Daten von Ansichten in Textdateien
  • BCP IN Daten von Textdateien in Tabellen mit Batch-Größe Parameter
+0

13 Sekunden! * schüttelt winzige Faust * –

+0

Wie hast du den Sekundenanteil bekommen? Es zeigt mir nur Minuten –

+1

@Raj: über den relativen Zeitstempel bewegen, um tatsächliche Zeitstempel mit Sekunden zu erhalten –

1

Dies sieht aus wie ein Job für gute ol 'BCP.

2

Es gibt keine Pixie Staub, das wissen Sie.

Ohne genaue Informationen über das tatsächlich übertragene Schema zu erhalten, wäre eine generische Lösung genau so, wie Sie es beschreiben: Unterteilen Sie die Verarbeitung in mehrere Einfügungen und behalten Sie den/die Schlüssel im Auge. Dies ist eine Art von Pseudo-Code T-SQL:

create table currentKeys (table sysname not null primary key, key sql_variant not null); 
go 

declare @keysInserted table (key sql_variant); 
declare @key sql_variant; 
begin transaction 
do while (1=1) 
begin 
    select @key = key from currentKeys where table = '<target>'; 
    insert into <target> (...) 
    output inserted.key into @keysInserted (key) 
    select top (<batchsize>) ... from <source> 
    where key > @key 
    order by key; 

    if (0 = @@rowcount) 
     break; 

    update currentKeys 
    set key = (select max(key) from @keysInserted) 
    where table = '<target>'; 
    commit; 
    delete from @keysInserted; 
    set @key = null; 
    begin transaction; 
end 
commit 

Es wäre komplizierter, wenn man für die parallelen Reihen zulassen möge und die Schlüssel partitionieren.

+3

Eigentlich sind zermahlene silberne Kugeln sehr gute Feenstaub –

+1

ty mr Kelley, das werde ich bei meinem nächsten Projekt im Hinterkopf behalten;) –

5

Hat die Ansicht irgendeine Art von eindeutigen Bezeichner/Kandidatenschlüssel? Wenn ja, könnten Sie diese Zeilen in eine Arbeitstabelle mit auswählen:

SELECT key_columns INTO dbo.temp FROM dbo.HugeView; 

(Wenn es Sinn macht, vielleicht setzen diese Tabelle in einer anderen Datenbank, vielleicht mit Modell SIMPLE Recovery, von der Protokollaktivität zu verhindern stören Ihre primäre Datenbank sollte ohnehin viel weniger Protokoll generieren, und Sie können den Speicherplatz in der anderen Datenbank freigeben, bevor Sie fortfahren, für den Fall, dass das Problem darin besteht, dass nicht genügend Festplattenspeicher vorhanden ist.)

Dann können Sie etwas tun, 10.000 Zeilen zu einer Zeit eingeführt wird, und das Protokoll zwischen Sicherung:

SET NOCOUNT ON; 

DECLARE 
    @batchsize INT, 
    @ctr INT, 
    @rc INT; 

SELECT 
    @batchsize = 10000, 
    @ctr = 0; 

WHILE 1 = 1 
BEGIN 
    WITH x AS 
    (
     SELECT key_column, rn = ROW_NUMBER() OVER (ORDER BY key_column) 
     FROM dbo.temp 
    ) 
    INSERT dbo.PrimaryTable(a, b, c, etc.) 
     SELECT v.a, v.b, v.c, etc. 
     FROM x 
     INNER JOIN dbo.HugeView AS v 
     ON v.key_column = x.key_column 
     WHERE x.rn > @batchsize * @ctr 
     AND x.rn <= @batchsize * (@ctr + 1); 

    IF @@ROWCOUNT = 0 
     BREAK; 

    BACKUP LOG PrimaryDB TO DISK = 'C:\db.bak' WITH INIT; 

    SET @ctr = @ctr + 1; 
END 

Das ist alles aus der Spitze von meinem Kopf, so schneiden nicht/einfügen/ausführen, aber ich denke, die allgemeine Idee ist da.

Beachten Sie, dass Sie bei regelmäßigen Datenbank- und Protokollsicherungen wahrscheinlich eine vollständige Datenbank verwenden, um die Protokollkette erneut zu starten.

+0

Das ist lustig, denn bevor ich das meiste dieses Systems gebaut habe auf dem Server und nutzte immer noch meine Box, um zu entwickeln, hatte ich bereits Kategorien von Tabellen für Recovery-Ziele aufgeteilt und in separate Schemas in der Hoffnung, dass die DBAs mir mehrere Datenbanken geben würde, aber es sollte nicht sein ... –

+0

Und leider sind die Schlüssel alle über 50 Bytes von zusammengesetzten Spalten, denke mal, es ist Zeit, es zu erledigen ... –

+1

@AaronBertrand Ich weiß, das ist alt, aber ich habe es gerade in einem Test verwendet. Die Theorie funktioniert gut, Sie vermissen gerade 'SET @ ctr = @ctr + 1' in der Schleife, um den Chargennummernzähler zu erhöhen. Ich hoffe, es macht dir nichts aus - ich habe es bearbeitet (nachdem ich die Ergebnisse getestet habe). – Bridge

2

Ich weiß, das ein alter Thread, aber ich habe eine generische Version von Arthur Cursor Lösung:

--Split a batch up into chunks using a cursor. 
--This method can be used for most any large table with some modifications 
--It could also be refined further with an @Day variable (for example) 

DECLARE @Year INT 
DECLARE @Month INT 

DECLARE BatchingCursor CURSOR FOR 
SELECT DISTINCT YEAR(<SomeDateField>),MONTH(<SomeDateField>) 
FROM <Sometable>; 


OPEN BatchingCursor; 
FETCH NEXT FROM BatchingCursor INTO @Year, @Month; 
WHILE @@FETCH_STATUS = 0 
BEGIN 

--All logic goes in here 
--Any select statements from <Sometable> need to be suffixed with: 
--WHERE Year(<SomeDateField>)[email protected] AND Month(<SomeDateField>)[email protected] 


    FETCH NEXT FROM BatchingCursor INTO @Year, @Month; 
END; 
CLOSE BatchingCursor; 
DEALLOCATE BatchingCursor; 
GO 

Dies löste das Problem auf Lasten unserer großen Tabellen.

Verwandte Themen