动态创建表并使用CSV值从另一个表中插入表

时间:2013-04-18 21:39:43

标签: 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  

需要获得的是此场景中的2个表格,其中包含相应的值

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


ID  City    State   Zip
2   NewYork NewYork 01234

有没有办法在SQL Server中使用Cursor或任何其他方法实现它?

3 个答案:

答案 0 :(得分:0)

您需要将其作为多步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%'

一旦你将这两种类型分开,你就可以用一个已经写好的分离函数将它们拆分出来,这些函数很容易在interweb上找到。

Aaron Bertrand(经常在这里)写了一篇关于使用SQL分割逗号分隔字符串的各种方法的精彩帖子。这里比较和比较每种方法。

http://www.sqlperformance.com/2012/07/t-sql-queries/split-strings

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

如果你有大量的行,或者这是一个一直运行的ETL过程,那么你真的要注意那些东西。

答案 1 :(得分:0)

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

由于您使用的是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;

SQL Fiddle with Demo。递归版本可以很好地工作但是如果你有一个大型数据集,你可能会遇到一些性能问题,所以你也可以使用用户定义的函数来分割数据:

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;

然后您可以使用CROSS APPLY获取每行的结果:

select id, SID, DOB
into table1
from
(
  select t.id,
    c.colname,
    c.colvalue
  from yt t
  cross apply dbo.split(t.name, t.text, ',') c
  where c.colname in ('SID', 'DOB')
) src
pivot
(
  max(colvalue)
  for colname in (SID, DOB)
) piv;

请参阅SQL Fiddle with Demo

答案 2 :(得分:0)

使用游标构建临时表的简单解决方案。这具有使所有列为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;