Domanda

Ho un paio di tabelle di grandi dimensioni (righe 188m e 144m) che devo popolare dalle viste, ma ogni vista contiene alcune centinaia di milioni di righe (che riuniscono i dati modellati pseudo-dimensionalmente in una forma piatta). Le chiavi su ogni tabella sono oltre 50 byte compositi di colonne. Se i dati fossero nelle tabelle, potrei sempre pensare all'utilizzo di sp_rename per creare l'altra nuova tabella, ma questa non è davvero un'opzione.

Se eseguo una singola operazione INSERT, il processo utilizza un'enorme quantità di spazio nel registro delle transazioni, in genere lo archivia e richiede un sacco di problemi con i DBA. (E sì, questo è probabilmente un lavoro che i DBA dovrebbero gestire / progettare / architetto)

Posso usare SSIS e trasmettere i dati nella tabella di destinazione con commit in batch (ma ciò richiede che i dati vengano trasmessi sulla rete, poiché non ci è permesso eseguire pacchetti SSIS sul server).

Nient'altro che dividere il processo in più operazioni INSERT usando una sorta di chiave per distribuire le righe in diversi batch e fare un ciclo?

È stato utile?

Soluzione

È possibile partizionare i dati e inserirli in un loop di cursore. Sarebbe quasi lo stesso dell'inserzione batch SSIS. Ma funziona sul tuo 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

Altri suggerimenti

La vista ha QUALSIASI tipo di identificatore univoco / chiave candidata? In tal caso, puoi selezionare quelle righe in una tabella di lavoro usando:

SELECT key_columns INTO dbo.temp FROM dbo.HugeView;

(Se ha senso, magari inserire questa tabella in un database diverso, magari con il modello di recupero SEMPLICE, per evitare che l'attività di registro interferisca con il database primario. Ciò dovrebbe generare comunque molto meno registro e puoi liberare il spazio nell'altro database prima di riprendere, nel caso in cui il problema sia che lo spazio su disco è inadeguato.)

Quindi puoi fare qualcosa del genere, inserendo 10.000 righe alla volta e eseguendo il backup del login tra:

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

Questo è tutto dalla parte superiore della mia testa, quindi non tagliare / incollare / correre, ma penso che l'idea generale sia lì.

Tieni presente che se stai eseguendo backup regolari di database e log probabilmente vorrai prenderne uno pieno per ricominciare da capo la catena di log.

So che questo è un vecchio thread, ma ho creato una versione generica della soluzione del cursore di Arthur:

--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>)=@Year AND Month(<SomeDateField>)=@Month   


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

Questo ha risolto il problema su molti dei nostri tavoli di grandi dimensioni.

Non c'è polvere di folletto, lo sai.

Senza conoscere i dettagli dello schema reale da trasferire, una soluzione generica sarebbe esattamente come la descrivi: dividi l'elaborazione in più inserti e tieni traccia delle chiavi. Questo è una specie di pseudo-codice 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

Sarebbe più complicato se si desidera consentire batch paralleli e partizionare le chiavi.

È possibile utilizzare il comando BCP per caricare i dati e utilizzare il parametro Dimensione batch

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

Processo in due passaggi

  • Dati BCP OUT da Viste in file di testo
  • Dati BCP IN da file di testo in tabelle con parametro dimensione batch

Sembra un buon lavoro per il BCP .

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top