1

在下面的列中有一个包含 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 或任何其他方法来实现它?

4

3 回答 3

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

于 2013-04-18T23:04:22.047 回答
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%'

将这两种类型分开后,您可以使用在互联网上很容易找到的已编写的拆分函数之一将它们拆分出来。

Aaron Bertrand(他经常在这里)写了一篇很棒的文章,介绍了使用 SQL 拆分逗号分隔字符串的各种方法。这里对每种方法进行比较和对比。

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

如果您的行数很少(假设低于 50k)并且这将是一次操作,而不是选择最简单的方法,并且不要太担心所有性能数字。

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

于 2013-04-18T21:52:33.360 回答
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;
于 2013-04-19T00:23:08.050 回答