¡Hola, chicos!
¡Buen día!

Hoy estoy muy entusiasmado con este procedimiento almacenado que les voy a presentar, porque realmente me tomó un poco de trabajo desarrollarlo. Seguramente habrás oído hablar de los archivos CSV (valores separados por comas), esos archivos de texto donde la información se organiza mediante un delimitador, normalmente coma (,) o punto y coma (;).

Bueno, la semana pasada surgió una necesidad donde trabajo para importar un archivo CSV a la base de datos, donde habría que crear las columnas de acuerdo al archivo CSV. Para resolver esto, creé un procedimiento almacenado que lee el archivo CSV, identifica las columnas y crea dinámicamente una tabla para almacenar los datos provenientes del CSV.

En el procedimiento, también agregué un parámetro opcional para informar si la primera línea del archivo es el encabezado CSV. En este caso, la primera fila no se mostrará en el resultado final y los títulos de las columnas se cambiarán dinámicamente al nombre contenido en esa columna de la fila del encabezado.

Antes de pasar al código, este procedimiento utiliza otras dos funciones de las que ya había hablado aquí en el blog:

ACTUALIZACIÓN 20/03/2017: Si desea utilizar una solución más robusta, completa y con mayor rendimiento, consulte cómo importar datos desde archivos tabulares, con CSV, usando CLR (C#) en la publicación SQL Server: cómo exportar e importar archivos con datos tabulares (Ej.: CSV) usando CLR (C#).

Vayamos al grano. Código fuente!!

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

Cómo utilizar:

-- 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

Archivo original:

Importa_CSV_1
Importa_CSV_1

Tabla importada a la base de datos con el comando anterior:

Importa_CSV_2
Importa_CSV_2

¡Eso es todo amigos!
¿Te gustó?