Sql 动态创建表并从另一个具有CSV值的表插入到其中
有一个表格,列中包含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
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;