Pergunta

Estou exportando dados programaticamente do Excel para o SQL Server 2005 usando o sqlbulkcopy. Funciona muito bem, o único problema que tenho é que ele não preserva a sequência de linhas que tenho no arquivo do Excel. Não tenho uma coluna para encomendar, só quero que os registros sejam inseridos na mesma ordem em que aparecem na planilha do Excel.

Não posso modificar o arquivo do Excel e tenho que trabalhar com o que tenho. A classificação por qualquer uma das colunas existentes quebrará a sequência.

Por favor ajude.

PS acabou inserindo a coluna de identificação da planilha, parece que não há como manter o pedido durante a exportação/importação

Foi útil?

Solução

Não acho que a ordem da linha seja especificada ou garantida pelo SQL, a menos que você use uma cláusula "Ordem by".

De um post de Bill Vaughn (http://betav.com/blog/billva/2008/08/sql_server_indexing_tips_and_t.html):

Usando o pedido por: Mesmo quando uma tabela possui um índice em cluster (que armazena os dados em ordem física), o SQL Server não garante que as linhas serão retornadas nessa (ou em particular) ordem, a menos que um pedido por cláusula seja usado.

Outro link com informações:

http://sqlblogcasts.com/blogs/simons/archive/2007/08/21/what-is-the-position-of-a-row--.aspx

Outras dicas

Depois de muitas pesquisas, parece evidente que não há como manter a ordem de linha com o comando de inserção em massa escrito, pois é apresentado pela Microsoft. Você precisa adicionar uma coluna de ID diretamente ao arquivo de importação, usar um shell ou outro script externo ou não. Parece que seria um recurso necessário (e fácil) para a Microsoft adicionar, mas depois de mais de uma década de nada deles, isso não vai acontecer.

No entanto, eu precisava preservar a ordem de registro real no arquivo de importação após a importação, pois os registros mais altos substituiriam os mais baixos se uma coluna definida tivesse o mesmo valor.

Então eu segui uma rota diferente. Minhas restrições foram:

  • Não pude alterar o arquivo de origem. (E estabeleça um precedente ruim!)
  • Eu não poderia usar um script externo. Muito complicado. Tinha que ser uma solução simples baseada em T-SQL, sem execuções de CMD. Isso precisava entrar em um único procedimento para que pudesse ser automatizado.

Gostei da lógica de usar o PowerShell para criar instruções de inserção ordenadas para cada linha e depois executando no SQL. Estava essencialmente na fila de cada registro para inserção individual, em vez de inserção em massa. Sim, funcionaria, mas também seria muito lento. Costumo ter arquivos com 500k+ linhas neles. Eu precisava de algo rápido.

Então eu encontrei XML. Granel Faça o upload do arquivo diretamente em uma única variável XML. Isso reteria a ordem dos registros, pois cada um é adicionado ao XML. Em seguida, analise a variável XML e insira os resultados em uma tabela, adicionando uma coluna de identidade ao mesmo tempo.

Supõe -se que o arquivo de importação seja um arquivo de texto padrão, com cada registro terminando em um feed de linha (char (13)+char (10))

Minha abordagem tem 2 etapas:

  1. Execute a instrução Import SQL (usando o OpenRowset), encapsulando cada registro com as tags XML. Capture os resultados em uma variável XML.

  2. Analise a variável pelas tags XML em uma tabela, adicionando uma coluna Incrementing [ID].

    ---------------------------------
    Declare @X xml;
    ---------------------------------
    SELECT @X=Cast('<X>'+Replace([BulkColumn],Char(13)+Char(10),'</X><X>')+'</X>' as XML)
    FROM OPENROWSET (BULK N'\\FileServer\ImportFolder\ImportFile_20170120.csv',SINGLE_CLOB) T
    ---------------------------------
    SELECT [Record].[X].query('.').value('.','varchar(max)') [Record]
    ,ROW_NUMBER() OVER (ORDER BY (SELECT 100)) [ID]
    --Into #TEMP 
    FROM @X.nodes('X') [Record](X);
    ---------------------------------
    
    • As tags XML substituem cada alimentação de linha.

    • Se o arquivo terminar com um feed de linha, isso fará com que uma linha em branco seja adicionada no final. Basta excluir a última linha.

Eu escrevi isso no meu procedimento usando SQL dinâmico para poder passar no nome do arquivo e definir o ID para começar em 1 ou 0 (caso haja uma linha de cabeçalho).

Consegui executar isso em um arquivo de 300 mil registros em cerca de 5 segundos.

Você também pode definir uma coluna de identidade em sua tabela que incrementos automáticos durante a carga de dados. Dessa forma, você pode classificá -lo mais tarde, quando deseja os registros na mesma ordem novamente.

Se você puder salvar a planilha do Excel como um CSV, é muito fácil gerar uma lista de instruções de inserção com qualquer linguagem de script que será executada exatamente na mesma ordem que a planilha. Aqui está um exemplo rápido no Groovy, mas qualquer linguagem de script o fará com a mesma facilidade, se não for mais fácil:

def file1 = new File('c:\\temp\\yourSpreadsheet.csv')
def file2 = new File('c:\\temp\\yourInsertScript.sql')

def reader = new FileReader(file1)
def writer = new FileWriter(file2)

reader.transformLine(writer) { line ->
    fields =  line.split(',')

    text = """INSERT INTO table1 (col1, col2, col3) VALUES ('${fields[0]}', '${fields[1]}', '${fields[2]}');"""

}

Você pode executar o seu "YourInserStScript.SQL" no seu banco de dados e seu pedido será o mesmo que sua planilha.

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