Sql 动态创建表并从另一个具有CSV值的表插入到其中

Sql 动态创建表并从另一个具有CSV值的表插入到其中,sql,sql-server,sql-server-2008,tsql,sql-server-2005,Sql,Sql Server,Sql Server 2008,Tsql,Sql Server 2005,有一个表格,列中包含CSV值,如下所示 ID Name text 1 SID,DOB 123,12/01/1990 2 City,State,Zip NewYork,NewYork,01234 3 SID,DOB 456,12/21/1990 在这个场景中,需要得到的是两个表以及相应的值 ID SID DOB 1 123 12/01/1990 3 456 12/21/1990 ID City State Zip 2

有一个表格,列中包含CSV值,如下所示

ID  Name    text    
1   SID,DOB 123,12/01/1990  
2   City,State,Zip  NewYork,NewYork,01234   
3   SID,DOB 456,12/21/1990  
在这个场景中,需要得到的是两个表以及相应的值

ID  SID DOB 
1   123 12/01/1990  
3   456 12/21/1990


ID  City    State   Zip
2   NewYork NewYork 01234

在SQL server中,是否有任何方法可以使用游标或任何其他方法来实现它?

您需要将其作为一个多步骤ETL项目来处理。我可能首先将这两种类型的行导出到两个临时表中。例如:

select * from yourtable        /* rows that start with a number */
where substring(text,1,1) in 
('0','1','2','3','4','5','6','7','8','9')  

select * from yourtable        /* rows that don't start with a number */
where substring(text,1,1) 
not in ('0','1','2','3','4','5','6','7','8','9')

/* or simply this to follow your example explicitly */

select * from yourtable where name like 'sid%' 

select * from yourtable where name like 'city%'
一旦你把这两种类型分开,你就可以用互联网上已经编写好的拆分函数之一来拆分它们

Aaron Bertrand(经常在这里)写了一篇关于使用SQL拆分逗号删除字符串的各种方法的文章。这里对每种方法进行了比较和对比

如果您的行数是最小的(比如说低于50k),并且这将是一次操作,那么选择最简单的方法,不要太担心所有的性能数字


如果您有大量的行,或者这是一个将一直运行的ETL过程,那么您真的需要注意这些内容。

有几种方法可以做到这一点。我建议的一种方法是将数据从逗号分隔的列表中分割成多行

因为您使用的是SQL Server,所以可以实现递归CTE来分割数据,然后应用PIVOT函数来创建所需的列

;with cte (id, NameItem, Name, textItem, text) as
(
  select id,
    cast(left(Name, charindex(',',Name+',')-1) as varchar(50)) NameItem,
    stuff(Name, 1, charindex(',',Name+','), '') Name,
    cast(left(text, charindex(',',text+',')-1) as varchar(50)) textItem,
    stuff(text, 1, charindex(',',text+','), '') text
  from yt
  union all
  select id,
    cast(left(Name, charindex(',',Name+',')-1) as varchar(50)) NameItem,
    stuff(Name, 1, charindex(',',Name+','), '') Name,
    cast(left(text, charindex(',',text+',')-1) as varchar(50)) textItem,
    stuff(text, 1, charindex(',',text+','), '') text
  from cte
  where Name > ''
    and text > ''
) 
select id, SID, DOB
into table1
from
(
  select id, nameitem, textitem
  from cte
  where nameitem in ('SID', 'DOB')
) d
pivot
(
  max(textitem)
  for nameitem in (SID, DOB)
) piv;
看。递归版本将非常有效,但如果数据集很大,可能会出现一些性能问题,因此也可以使用用户定义的函数拆分数据:

create FUNCTION [dbo].[Split](@String1 varchar(MAX), @String2 varchar(MAX), @Delimiter char(1))       
returns @temptable TABLE (colName varchar(MAX), colValue varchar(max))       
as       
begin      
    declare @idx1 int       
    declare @slice1 varchar(8000)  
    declare @idx2 int       
    declare @slice2 varchar(8000)     

    select @idx1 = 1       
        if len(@String1)<1 or @String1 is null  return       

    while @idx1 != 0       
    begin       
        set @idx1 = charindex(@Delimiter,@String1)  
        set @idx2 = charindex(@Delimiter,@String2) 
        if @idx1 !=0    
          begin
            set @slice1 = left(@String1,@idx1 - 1)   
            set @slice2 = left(@String2,@idx2 - 1)  
          end
        else  
          begin
            set @slice1 = @String1   
            set @slice2 = @String2
          end

        if(len(@slice1)>0)  
            insert into @temptable(colName, colValue) values(@slice1, @slice2)       

        set @String1 = right(@String1,len(@String1) - @idx1) 
        set @String2 = right(@String2,len(@String2) - @idx2) 
        if len(@String1) = 0 break       
    end   
return 
end;

请参阅使用游标构建临时表的简单解决方案。这有使所有列变为VARCHAR的限制,并且对于大量数据来说速度较慢

--** Set up example data
DECLARE @Source TABLE (ID INT, Name VARCHAR(50), [text] VARCHAR(200));
INSERT  INTO @Source
        (ID, Name, [text])
VALUES  (1, 'SID,DOB', '123,12/01/1990')
,       (2, 'City,State,Zip', 'NewYork,NewYork,01234')
,       (3, 'SID,DOB', '456,12/21/1990');

--** Declare variables
DECLARE @Name VARCHAR(200) = '';
DECLARE @Text VARCHAR(1000) = '';
DECLARE @SQL VARCHAR(MAX);

--** Set up cursor for the tables
DECLARE cursor_table CURSOR FAST_FORWARD READ_ONLY FOR
SELECT s.Name
  FROM @Source AS s
 GROUP BY Name;

OPEN cursor_table

FETCH NEXT FROM cursor_table INTO @Name;

WHILE @@FETCH_STATUS = 0
BEGIN
    --** Dynamically create a temp table with the specified columns
    SET @SQL = 'CREATE TABLE ##Table  (' + REPLACE(@Name, ',', ' VARCHAR(50),') + ' VARCHAR(50));';
    EXEC(@SQL);

    --** Set up cursor to insert the rows
    DECLARE row_cursor CURSOR FAST_FORWARD READ_ONLY FOR
     SELECT s.Text
       FROM @Source AS s
      WHERE Name = @Name;

    OPEN row_cursor;

    FETCH NEXT FROM row_cursor INTO @Text;

    WHILE @@FETCH_STATUS = 0
    BEGIN
        --** Dynamically insert the row  
        SELECT @SQL = 'INSERT INTO ##Table VALUES (''' + REPLACE(@Text, ',', ''',''') + ''');';
        EXEC(@SQL);

        FETCH NEXT FROM row_cursor INTO @Text;
    END

    --** Display the table
    SELECT *
    FROM ##Table;

    --** Housekeeping
    CLOSE row_cursor;
    DEALLOCATE row_cursor;
    DROP TABLE ##Table;

    FETCH NEXT FROM cursor_table INTO @Name;
END

CLOSE cursor_table;
DEALLOCATE cursor_table;

这是可能的,但编写脚本来解析文件、创建表和插入数据可能要简单得多。TSQL在处理一般文本方面非常弱。更好的是,如果报告仅针对单个文件,则解析文本并显示结果。这可能是用excel宏完成的。更正一下,代码正在比较名称列(“SID,DOB”或“City,State,Zip”)以查看它是否为数字,我想您是想使用
文本
列。(在我看来)查找数字字符串的另一种更简单的方法是
其中的子字符串(,1,1)类似于“[0-9]]”
(或者使用
左(,1)
而不是子字符串)。这也很有效-感谢您的指出。我忘记了like的范围功能。您好,这很好,但是当您执行交叉应用(数据透视和插入)时,您已经硬编码了列名,即id、SID和DOB。是否有一种方法可以从源表动态传递它们(我可以执行类似的操作
从源表中选择不同的名称
)并插入每一行
--** Set up example data
DECLARE @Source TABLE (ID INT, Name VARCHAR(50), [text] VARCHAR(200));
INSERT  INTO @Source
        (ID, Name, [text])
VALUES  (1, 'SID,DOB', '123,12/01/1990')
,       (2, 'City,State,Zip', 'NewYork,NewYork,01234')
,       (3, 'SID,DOB', '456,12/21/1990');

--** Declare variables
DECLARE @Name VARCHAR(200) = '';
DECLARE @Text VARCHAR(1000) = '';
DECLARE @SQL VARCHAR(MAX);

--** Set up cursor for the tables
DECLARE cursor_table CURSOR FAST_FORWARD READ_ONLY FOR
SELECT s.Name
  FROM @Source AS s
 GROUP BY Name;

OPEN cursor_table

FETCH NEXT FROM cursor_table INTO @Name;

WHILE @@FETCH_STATUS = 0
BEGIN
    --** Dynamically create a temp table with the specified columns
    SET @SQL = 'CREATE TABLE ##Table  (' + REPLACE(@Name, ',', ' VARCHAR(50),') + ' VARCHAR(50));';
    EXEC(@SQL);

    --** Set up cursor to insert the rows
    DECLARE row_cursor CURSOR FAST_FORWARD READ_ONLY FOR
     SELECT s.Text
       FROM @Source AS s
      WHERE Name = @Name;

    OPEN row_cursor;

    FETCH NEXT FROM row_cursor INTO @Text;

    WHILE @@FETCH_STATUS = 0
    BEGIN
        --** Dynamically insert the row  
        SELECT @SQL = 'INSERT INTO ##Table VALUES (''' + REPLACE(@Text, ',', ''',''') + ''');';
        EXEC(@SQL);

        FETCH NEXT FROM row_cursor INTO @Text;
    END

    --** Display the table
    SELECT *
    FROM ##Table;

    --** Housekeeping
    CLOSE row_cursor;
    DEALLOCATE row_cursor;
    DROP TABLE ##Table;

    FETCH NEXT FROM cursor_table INTO @Name;
END

CLOSE cursor_table;
DEALLOCATE cursor_table;