Olá Pessoal!
Bom dia!
Hoje eu estou bem empolgado com essa stored procedure que vou apresentar a vocês, porque ela realmente me deu um pouco de trabalho para desenvolver. Com certeza, vocês já ouviram falar de arquivos CSV (Comma-separated values), aqueles arquivos de texto onde as informações são organizadas utilizando um delimitador, geralmente vírgula (,) ou ponto e vírgula (;).
Pois bem, semana passada surgiu uma necessidade onde eu trabalho de importar um arquivo CSV para dentro do banco de dados, onde as colunas teriam que ser criadas de acordo com o arquivo CSV. Para resolver isso, eu criei uma stored procedure que lê o arquivo CSV, identifica as colunas, e cria uma tabela dinamicamente para armazenar os dados vindos do CSV.
Na procedure, eu coloquei ainda um parâmetro opcional para informar se a primeira linha do arquivo é o cabeçalho do CSV. Neste caso, a primeira linha não será exibida no resultado final e o título das colunas é alterado dinamicamente para o nome contido naquela coluna da linha do cabeçalho.
Antes de ir ao código, essa procedure utiliza duas outras funções das quais eu já havia falado aqui no blog:
- fncSplit (Visualizar post)
- fncLer_Arquivo_FSO (Visualizar post)
UPDATE 20/03/2017: Caso você queira utilizar uma solução mais robusta, completa e mais performática, confira como importar dados de arquivos tabulares, com o CSV, utilizando o CLR (C#) no post SQL Server – Como exportar e importar arquivos com dados tabulares (Ex: CSV) utilizando o CLR (C#).
Vamos ao que interessa. Código-fonte!!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
USE [CLR] GO CREATE PROCEDURE [dbo].[stpImporta_CSV] ( @Ds_Caminho_Arquivo VARCHAR(MAX), @Ds_Separador VARCHAR(10) = ';', @Fl_Primeira_Linha_Cabecalho BIT = 1, @Ds_Tabela_Destino VARCHAR(MAX) = NULL ) AS BEGIN DECLARE @tabela_bruta TABLE ( Id INT IDENTITY(1,1), Ds_Linha VARCHAR(MAX) ) -- Insere numa tabela temporária o conteúdo do CSV que será importado INSERT INTO @tabela_bruta(Ds_Linha) SELECT * FROM CLR.dbo.fncLer_Arquivo_FSO(@Ds_Caminho_Arquivo) DECLARE @contadorColunas INT = 1, @numeroColunas INT = (SELECT TOP 1 LEN(Ds_Linha) - LEN(REPLACE(Ds_Linha, @Ds_Separador, '')) + LEN(@Ds_Separador) FROM @tabela_bruta), @query VARCHAR(MAX) IF (OBJECT_ID('tempdb..#Tabela_Final') IS NOT NULL) DROP TABLE #Tabela_Final CREATE TABLE #Tabela_Final ( Id INT IDENTITY(1,1) ) -- Loop para inserir as colunas de acordo com a estrutura do CSV WHILE(@contadorColunas <= @numeroColunas) BEGIN SET @query = 'ALTER TABLE #Tabela_Final ADD Ds_Coluna_' + CAST(@contadorColunas AS VARCHAR(20)) + ' VARCHAR(MAX)' EXEC(@query) SET @contadorColunas = @contadorColunas + 1 END DECLARE @numeroLinhas INT = (SELECT COUNT(*) FROM @tabela_bruta), @linha VARCHAR(MAX), @contadorLinhas INT = 1 -- Loop para renomear as colunas de acordo com o nome do cabeçalho (se usada a Flag @Fl_Primeira_Linha_Cabecalho = 1) IF (@Fl_Primeira_Linha_Cabecalho = 1) BEGIN SET @contadorColunas = 1 DECLARE @cabecalho VARCHAR(MAX) = (SELECT Ds_Linha FROM @tabela_bruta WHERE Id = 1), @Nm_Coluna_Anterior VARCHAR(MAX), @Nm_Coluna_Nova VARCHAR(MAX) WHILE(@contadorColunas <= @numeroColunas) BEGIN SET @Nm_Coluna_Anterior = '#tabela_final.Ds_Coluna_' + CAST(@contadorColunas AS VARCHAR(20)) SET @Nm_Coluna_Nova = (SELECT CLR.dbo.fncSplit(@cabecalho, @Ds_Separador, @contadorColunas)) -- Remove aspas (se houver) IF (LEFT(@Nm_Coluna_Nova, 1) = '"' AND RIGHT(@Nm_Coluna_Nova, 1) = '"') SET @Nm_Coluna_Nova = SUBSTRING(@Nm_Coluna_Nova, 2, LEN(@Nm_Coluna_Nova) - 2) EXEC tempdb..sp_RENAME @Nm_Coluna_Anterior, @Nm_Coluna_Nova, 'COLUMN' SET @contadorColunas = @contadorColunas + 1 END DELETE FROM @tabela_bruta WHERE id = 1 SET @contadorLinhas = 2 END -- Loop para inserir os dados na tabela temporária final DECLARE @coluna VARCHAR(MAX) WHILE(@contadorLinhas <= @numeroLinhas) BEGIN SET @contadorColunas = 1 SET @linha = (SELECT Ds_Linha FROM @tabela_bruta WHERE Id = @contadorLinhas) SET @query = 'INSERT INTO #tabela_final VALUES(' WHILE(@contadorColunas <= @numeroColunas) BEGIN SET @coluna = ISNULL(CLR.dbo.fncSplit(@linha, @Ds_Separador, @contadorColunas), '') -- Remove aspas (se houver) IF (LEFT(@coluna, 1) = '"' AND RIGHT(@coluna, 1) = '"') SET @coluna = SUBSTRING(@coluna, 2, LEN(@coluna) - 2) SET @query = @query + CHAR(39) + @coluna + CHAR(39) IF (@contadorColunas + 1 <= @numeroColunas) SET @query = @query + ',' SET @contadorColunas = @contadorColunas + 1 END SET @query = @query + ')' EXEC(@query) SET @contadorLinhas = @contadorLinhas + 1 END IF (@Ds_Tabela_Destino IS NOT NULL) BEGIN SET @query = 'SELECT * INTO ' + @Ds_Tabela_Destino + ' FROM #Tabela_Final' EXEC(@query) END ELSE BEGIN -- Exibe o resultado final da SP SELECT * FROM #Tabela_Final END END |
Forma de utilização:
1 2 3 4 5 |
-- Importa o arquivo "C:\Arquivos\Clientes.csv" onde as colunas são separadas por ";" e a primeira coluna é o cabeçalho EXEC CLR.dbo.stpImporta_CSV @Ds_Caminho_Arquivo = 'C:\Arquivos\Clientes.csv', @Ds_Separador = ';', @Fl_Primeira_Linha_Cabecalho = 1 |
Tabela importada no banco de dados com o comando acima:
É isso aí pessoal!
Gostaram?
Falta: fncSplit
Desculpe encontrei…
😉