Como executo um script grande com muitas inserções sem ficar sem memória?

28

Questão:

Eu tenho um script com cerca de 45 mil inserções de instruções select. Quando tento executá-lo, recebo uma mensagem de erro informando que fiquei sem memória. Como posso executar esse script?

Contexto:

  1. Foram adicionados alguns novos campos de dados para que um aplicativo funcione bem com outro aplicativo que o cliente usa.
  2. Obteve uma planilha de dados do cliente cheia de dados que mapeou os itens de dados atuais para valores para esses novos campos.
  3. Planilha convertida para inserir instruções.
  4. Se eu executar apenas algumas das instruções, ele funciona, mas o script inteiro não.
  5. Não. Não há erros de digitação.

Se houver uma maneira diferente de carregar esses dados, sinta-se à vontade para me castigar e me avisar.

spaghetticowboy
fonte
Pergunta semelhante no SO: ( stackoverflow.com/questions/222442/... ) Não tenho certeza se a resposta ajuda
jumpdart

Respostas:

17

O tamanho máximo do lote para o SQL Server 2005 é 65.536 * NPS (Network Packet Size), em que o NPS geralmente é de 4KB. Isso funciona com 256 MB. Isso significaria que suas instruções de inserção teriam uma média de 5,8 KB cada. Isso não parece certo, mas talvez haja espaços estranhos ou algo incomum lá.

Minha primeira sugestão seria colocar uma instrução "GO" após cada instrução INSERT. Isso dividirá seu lote único de 45.000 instruções INSERT em 45.000 lotes separados. Isso deve ser mais fácil de digerir. Cuidado, se uma dessas inserções falhar, você pode ter dificuldade em encontrar o culpado. Você pode querer se proteger com uma transação. Você pode adicionar essas instruções rapidamente se o seu editor tiver uma boa pesquisa e substituição (que permitirá pesquisar e substituir caracteres de retorno como \ r \ n) ou um recurso de macro.

A segunda sugestão é usar um Assistente para importar os dados diretamente do Excel. O assistente cria um pequeno pacote SSIS para você, nos bastidores, e depois o executa. Não terá esse problema.

darin strait
fonte
2
A GO after every statement? Well, I guess if you're generating them using another script that's OK. Otherwise, I'd just put one after every 1000 INSERTs. With regards to making the transaction atomic and minimizing the size of the transaction, why not load all the rows into a temp table or table variable and then load them in one shot from there to the target table?
Nick Chammas
Um 1000 é tão bom quanto 1, mas mais difícil de contar. Para ser honesto, ele pode se safar com apenas uma declaração do GO, na metade do caminho, perto da declaração 21.500. Gosto da correção GO, porque não requer edição complicada do script atual ou contagem de instruções INSERT (que podem não ser mapeadas diretamente para os números de linha).
darin strait
2
Certamente, mesmo uma aproximação incorreta de 1000 declarações é boa o suficiente. :)
Nick Chammas
1
Adicionar os GOs foi uma solução rápida e fácil. O script de 25mb é executado em pouco menos de 9min sem problemas. Queria tê-lo como um script para mantê-lo dentro do processo de implantação de patches padrão para quando ele sair.
Spaghetticowboy
14

BULK INSERTou bcpparecer opções mais apropriadas do que 45.000 instruções de inserção.

Se você precisar seguir as instruções de inserção, considerarei algumas opções:

R: Use transações e agrupe lotes de 100 ou 500 ou 1000 instruções em cada uma delas para minimizar o impacto no log e no lote. por exemplo

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: Em vez de instruções de inserção individuais, use UNION ALLpara 100 ou 500 instruções de cada vez, por exemplo

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

Eu deixei o tratamento de erros por questões de brevidade, mas o ponto é que nunca tentaria enviar um único lote de 45.000 instruções individuais para o SQL Server.

Aaron Bertrand
fonte
1
Pena que o OP não pode usar construtores de valor de tabela , um recurso de 2008+. Ele ainda precisará agrupar as inserções em grupos de 1000 linhas, que é o máximo que você pode agrupar com um TVC.
21412 Nick Chammas
Essa seria minha primeira sugestão até eu ver a tag da versão.
Aaron Bertrand
2
@NickChammas - O desempenho desses degrada não linearmente com o número de cláusulas BTW . Enviei um item de conexão com uma reprodução de inserir 1000 linhas com 10 VARCHAR(800)colunas em 2008 com um tempo de compilação de 12,5 minutos na minha instância de desenvolvimento de 2008, pois faz muito trabalho desnecessário na comparação de valores em vez de apenas inseri-los (executa muito mais rápido quando parametrizado e sem valores a serem observados). Embora tenha melhorado muito em 2012, o padrão não linear ainda existe e deve ser corrigido na versão posterior.
Martin Smith
9

Não sei por que você está recebendo o erro de falta de memória, mas existe uma abordagem mais fácil.

Se você pode exportar os dados da planilha para um formato delimitado (por exemplo, csv), pode usar o assistente de importação de dados no SSMS para inserir os dados para você:

Tarefa de importação de dados do SSMS.

datagod
fonte
isso é útil, mas eu não tenho acesso aos bancos de dados de clientes. Eu tenho que preparar patches e dataloads em scripts #
spaghetticowboy
0

Usando vários SqlBulkCopy, crie uma tabela temporária. Insira novos dados na tabela temporária e mescle os dados na tabela temporária na existente. Exemplo usando o método C # SqlBulkCopy.WriteToServer (DataTable) . Espero que ajude

Hung Vu
fonte
0

Sim, nós poderíamos fazer isso, tentei com uma abordagem BCP (Bulk Copy Program) para evitar um problema de OutOfMemory .

Nota : Tentei no SQL Server 2014.

No BCP, primeiro precisamos exportar os dados do banco de dados de origem para o arquivo bcp (na pasta do diretório local) e depois importar esse arquivo bcp para o banco de dados de destino.

insira a descrição da imagem aqui

Abaixo estão os passos do bolo:

Nota:

a) Verifique se a tabela vazia está presente no banco de dados de destino

b) Verifique se a pasta Temp está presente na unidade C

  1. Crie um arquivo bat chamado Export_Data.bat com o comando mostrado abaixo:

    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 

    pausa

  2. Execute esse arquivo bat, como resultado, um arquivo bcp será gerado na pasta Temp

  3. Em seguida, crie um outro arquivo bat chamado Import_Data.bat com o seguinte comando:

    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 

    Pausa

E aqui vamos nós!

Kms
fonte
Obtendo o erro "É necessário um nome de tabela válido para as opções de entrada, saída ou formatação". quando tentou exportar dados.
Sen Jacob
1
Você pode colar o comando que tentou com todo o valor do atributo.Por favor, siga o exemplo abaixo: bcp.exe ExportDB.dbo.AddressCountry OUT "C: \ Temp \ AddressCountry.bcp" -S "IN-L20054" -U "sa" -P "sa" -n -q Em que [ExportDB -> DB de origem, AddressCountry-> Tabela presente no DB de origem, IN-L20054 -> Nome da máquina, "sa" é o nome de usuário / senha do DB]
2009
Eu não tenho isso agora. Acabei usando o recurso de importação de dados no SSMS. Em seguida, conectou o banco de dados de destino (v14.0) ao banco de dados de origem (v.15.0) usando a conexão MS OLE DB e foi muito rápido importar milhões de linhas de dados. Obrigado!
Sen Jacob