【问题标题】:Importing to sql from csv in changing order of columns以更改列的顺序从csv导入sql
【发布时间】:2014-02-12 23:25:29
【问题描述】:

我正在使用 SQL 2012。我想在 SQL 中创建一个存储过程,该过程将删除一个表,然后根据从 csv 文件的导入创建一个表。到目前为止很简单。

问题是由于某些我无法控制的问题,我的 CSV 文件中的列可能会按顺序更改。我计划用一个名称来识别列,该名称也将链接到表中的另一个名称(同样,我无法控制)。

例如(CSV 格式):

Name1           Name2      Name3
1                3           2
22               4           3
3                13          12
41               8           1
22               6           4

在元数据表中:

Names         ID      
Name1         ID1           
Name2         ID2           
Name3         ID3         

还有我想通过导入创建的表:

ID1           ID2      ID3
1             3         2
22            4         3
3             13        12
41            8         1
22            6         4

问题是列的名称并不总是以相同的顺序排列,任何一天我都可以得到一个看起来像这样的 CSV:

Name3         Name2        Name1
2               3           1
3               4           22
12              13          3
1               8           41
4               6           22

您将如何通过内部连接之类的方式命名表中的列?

【问题讨论】:

  • 文件中的列名是否总是相同(即使它们的顺序不同)?
  • 是的,名字总是一样的!

标签: sql sql-server csv import multiple-columns


【解决方案1】:

这做了一些假设:总是 3 列,所有数据都是 int 类型,目标表是静态的,等等。如果您的需求更复杂,您可能会开始寻找专用的 ETL 工具。

CREATE PROCEDURE Import (
  @filename varchar(max)
) AS
BEGIN
  DECLARE @sql nvarchar(max)
  DECLARE @columns_i nvarchar(max)
  DECLARE @columns_o nvarchar(max)

  CREATE TABLE #header ([1] sysname, [2] sysname, [3] sysname)
  SET @sql = N'BULK INSERT #header FROM ' + QUOTENAME(@filename) + ' WITH (DATAFILETYPE = ''char'',FIELDTERMINATOR = '','',ROWTERMINATOR = ''0x0D0A'',FIRSTROW = 1, LASTROW = 1);'
  EXEC sp_executesql @sql

  SELECT @columns_i = ISNULL(@columns_i+',','') + QUOTENAME([column]) + ' int' FROM #header UNPIVOT([column] FOR [colnum] IN ([1],[2],[3])) p ORDER BY [colnum]
  SELECT @columns_o = ISNULL(@columns_o+',','') + QUOTENAME([Names] + ' AS ' + QUOTENAME([ID]) FROM MyMetadata ORDER BY [Names]

  SET @sql = N'CREATE TABLE #data ('+@columns_i+');'
  SET @sql = @sql + N'BULK INSERT #data FROM ' + QUOTENAME(@filename) + ' WITH (DATAFILETYPE = ''char'',FIELDTERMINATOR = '','',ROWTERMINATOR = ''0x0D0A'',FIRSTROW = 2);'
  SET @sql = @sql + N'SELECT '+@columns_o+' FROM #data'

  INSERT MyDestination
  EXEC sp_executesql @sql
END

【讨论】:

    【解决方案2】:

    使用的表结构:

    create table mapTable(
        inCsv varchar(max),
        inSql varchar(max)
    );
    
    create table csvImported(
        Name1 int,
        Name2 int,
        Name3 int
    );
    

    在这里,我假设您已经有一个导入了 csv 数据的表格。 所以,我用一些东西填充 csvImported 表,只是为了测试。地图表,同理。

    填充mapTable表;

    insert into mapTable values ('Name1', 'ID');
    insert into mapTable values ('Name2', 'ID2');
    insert into mapTable values ('Name3', 'ID3');
    

    填充 csvImported 表;

    insert into csvImported values (11, 122, 333);
    insert into csvImported values (110, 1422, 37833);
    insert into csvImported values (101, 1252, 33213);
    

    所以,这里是过程定义。

    CREATE PROCEDURE CREATE_TABLE_FROM_CSV(
        @CsvTableName varchar(max),
        @WantedTableName varchar(max)
    
    ) AS
    
    SET NOCOUNT ON;
    
    DECLARE @vColumnList            varchar(max) = '',
            @vColumnName            varchar(max) = '',
            @vColumnNameRenamed     varchar(max) = '',
            @vColumns               TinyInt = 0,
            @vTableNameInSql        varchar(max) = @WantedTableName,
            @vTableNameInCsv        varchar(max) = @CsvTableName,
            @vDoCreateTable         varchar(max) = '';
    
            if ((select OBJECT_ID('ShadowMapTable')) > 0 ) 
            begin 
                drop table ShadowMapTable;
            end;    
    
            select * into ShadowMapTable from mapTable;
    
            select      t1.name
            from        sys.columns t1 
            join        sys.tables t2 on (t1.object_id = t2.object_id)
            join        ShadowMapTable t3 on (t1.name = t3.inCsv)
            where       t2.name = @vTableNameInCsv;
    
            set @vColumns = @@rowcount;
    
            WHILE (@vColumns > 0)
            begin
                select      @vColumnName = t1.name, @vColumnNameRenamed = t1.name + ' as ' + t3.inSql
                from        sys.columns t1 
                join        sys.tables t2 on (t1.object_id = t2.object_id)
                join        ShadowMapTable t3 on (t1.name = t3.inCsv)
                where       t2.name = @vTableNameInCsv
    
                -- Define the ColumnListRenamed -> used in the into clause.
                if (@vColumnList = '')
                begin 
                    set @vColumnList = @vColumnNameRenamed
                end else
                begin
                    set @vColumnList = @vColumnNameRenamed + ',' + @vColumnList;
                end;
    
                delete from ShadowMapTable
                where       inCsv = @vColumnName
    
                select      t1.name, t3.inCsv, t3.inSql
                from        sys.columns t1 
                join        sys.tables t2 on (t1.object_id = t2.object_id)
                join        ShadowMapTable t3 on (t1.name = t3.inCsv )
                where       t2.name = @vTableNameInCsv
    
                set @vColumns = @@rowcount;
            end;
    
            set @vDoCreateTable = '
                                    if((select object_id('''+@vTableNameInSql+''')) > 0) 
                                    begin 
                                        drop table '+@vTableNameInSql+'; 
                                    end; 
    
                                    select ' + @vColumnList + ' into ' + @vTableNameInSql + ' from ' + @vTableNameInCsv + ';';
            --print @vDoCreateTable;
            exec    (@vDoCreateTable);
            exec    ('select * from ' + @vTableNameInSql);
            print   ('Table created: ' + @vTableNameInSql);
    

    最后,应该如何调用 Proc。

    exec CREATE_TABLE_FROM_CSV 'csvImported', 'wantedTableName';
    

    我真的希望它有所帮助。

    亲切的问候, 维克多。

    【讨论】:

      猜你喜欢
      • 2021-09-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多