Pergunta

Eu tenho um par de tabelas grandes (188m e 144m linhas) que eu preciso para preencher a partir de pontos de vista, mas cada vista contém algumas centenas de milhões de linhas (puxando dados em conjunto pseudo-dimensionalmente modelados em uma forma plana). As teclas em cada mesa são mais de 50 bytes compósitos de colunas. Se os dados foi em tabelas, eu sempre poderia pensar em usar sp_rename para fazer a outra nova tabela, mas isso não é realmente uma opção.

Se eu fizer uma única operação INSERT, o processo utiliza uma enorme quantidade de espaço de log de transações, typicalyl arquivando-o e levando um monte de problemas com os DBAs. (E sim, este é provavelmente um trabalho dos DBAs devem manusear / design / arquiteto)

Eu posso usar o SSIS e transmitir os dados na tabela de destino com commits lote (mas isso requer que os dados sejam transmitidos através da rede, uma vez que não estão autorizados a executar pacotes SSIS no servidor).

Todas as outras coisas do que para dividir o processo em múltiplas operações de INSERT usando algum tipo de chave para distribuir as linhas em diferentes lotes e fazer um loop?

Foi útil?

Solução

Você pode particionar seus dados e inserir os seus dados em um loop cursor. Isso seria quase o mesmo que SSIS batchinserting. Mas é executado em seu servidor.

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

Outras dicas

Será que a visão tem qualquer tipo de chave identificador / candidato único? Se assim for, você pode selecionar as linhas em uma tabela trabalhando usando:

SELECT key_columns INTO dbo.temp FROM dbo.HugeView;

(Se isso faz sentido, talvez colocar essa tabela em um banco de dados diferente, talvez com o modelo de recuperação simples, para evitar que a atividade de log interfira com o seu banco de dados primário. Isso deve gerar muito menos log de qualquer maneira, e você pode liberar o espaço no outro banco de dados antes de continuar, caso o problema é que você tem espaço em disco insuficiente ao redor.)

Em seguida, você pode fazer algo como isto, a inserção de 10.000 linhas de cada vez, e fazer backup do log entre:

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

Isso é tudo em cima da minha cabeça, por isso não cortar / colar / run, mas acho que a ideia geral é lá.

Note que, se estiver a tomar banco de dados regular e log backups você provavelmente vai querer tomar um completo para iniciar a sua cadeia de log de novo.

Eu sei que esta é uma discussão antiga, mas eu fiz uma versão genérica de solução cursor de 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

Isso resolveu o problema em cargas dos nossos grandes tabelas.

Não há pó de pirlimpimpim, você sabe disso.

Sem saber detalhes sobre o esquema real que está sendo transferido, uma solução genérica seria exatamente como você descrevê-lo: processamento de dividir em várias inserções e manter o controle da chave (s). Esta é uma espécie de pseudo-código 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

Ele iria ficar mais complicado se você deseja permitir para lotes paralelos e particionar as chaves.

Você pode usar o comando BCP para carregar os dados e use o parâmetro Tamanho de lote

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

processo de duas etapas

  • dados BCP para fora de Visualizações em arquivos de texto
  • dados BCP in desde arquivos de texto em tabelas com tamanho de lote parâmetro

Este parece como um trabalho para uma boa ol' BCP .

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top