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