Sql server 从SQL Server中的CSV列创建表,而不使用光标
给出一张表格:Sql server 从SQL Server中的CSV列创建表,而不使用光标,sql-server,tsql,cursor,Sql Server,Tsql,Cursor,给出一张表格: |Name | Hobbies | ----------------------------------- |Joe | Eating,Running,Golf | |Dafydd | Swimming,Coding,Gaming | 我想将这些行拆分为: |Name | Hobby | ---------------------- |Joe | Eating | |Joe | Runnin
|Name | Hobbies |
-----------------------------------
|Joe | Eating,Running,Golf |
|Dafydd | Swimming,Coding,Gaming |
我想将这些行拆分为:
|Name | Hobby |
----------------------
|Joe | Eating |
|Joe | Running |
|Joe | Golf |
|Dafydd | Swimming |
|Dafydd | Coding |
|Dafydd | Gaming |
我已经完成了下面的示例,可以在SSMS中运行了,我的解决方案使用了一个我认为很难看的游标。有更好的方法吗?我使用的是SQL Server 2008 R2,如果有什么新的东西可以帮助我的话
谢谢
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[Split]') and xtype in (N'FN', N'IF', N'TF')) drop function [dbo].Split
go
CREATE FUNCTION dbo.Split (@sep char(1), @s varchar(512))
RETURNS table
AS
RETURN (
WITH Pieces(pn, start, stop) AS (
SELECT 1, 1, CHARINDEX(@sep, @s)
UNION ALL
SELECT pn + 1, stop + 1, CHARINDEX(@sep, @s, stop + 1)
FROM Pieces
WHERE stop > 0
)
SELECT pn,
SUBSTRING(@s, start, CASE WHEN stop > 0 THEN stop-start ELSE 512 END) AS s
FROM Pieces
)
go
declare @inputtable table (
name varchar(200) not null,
hobbies varchar(200) not null
)
declare @outputtable table (
name varchar(200) not null,
hobby varchar(200) not null
)
insert into @inputtable values('Joe', 'Eating,Running,Golf')
insert into @inputtable values('Dafydd', 'Swimming,Coding,Gaming')
select * from @inputtable
declare inputcursor cursor for
select name, hobbies
from @inputtable
open inputcursor
declare @name varchar(255), @hobbiescsv varchar(255)
fetch next from inputcursor into @name, @hobbiescsv
while(@@FETCH_STATUS <> -1) begin
insert into @outputtable
select @name, splithobbies.s
from dbo.split(',', @hobbiescsv) splithobbies
fetch next from inputcursor into @name, @hobbiescsv
end
close inputcursor
deallocate inputcursor
select * from @outputtable
使用类似于找到的字符串解析函数。关键是用于为基表中的每一行执行函数
CREATE FUNCTION [dbo].[fnParseStringTSQL] (@string NVARCHAR(MAX),@separator NCHAR(1))
RETURNS @parsedString TABLE (string NVARCHAR(MAX))
AS
BEGIN
DECLARE @position int
SET @position = 1
SET @string = @string + @separator
WHILE charindex(@separator,@string,@position) <> 0
BEGIN
INSERT into @parsedString
SELECT substring(@string, @position, charindex(@separator,@string,@position) - @position)
SET @position = charindex(@separator,@string,@position) + 1
END
RETURN
END
go
declare @MyTable table (
Name char(10),
Hobbies varchar(100)
)
insert into @MyTable
(Name, Hobbies)
select 'Joe', 'Eating,Running,Golf'
union all
select 'Dafydd', 'Swimming,Coding,Gaming'
select t.Name, p.String
from @mytable t
cross apply dbo.fnParseStringTSQL(t.Hobbies, ',') p
DROP FUNCTION [dbo].[fnParseStringTSQL]
在数据库中创建此函数:
CREATE FUNCTION dbo.Split(@origString varchar(max), @Delimiter char(1))
returns @temptable TABLE (items varchar(max))
as
begin
declare @idx int
declare @split varchar(max)
select @idx = 1
if len(@origString )<1 or @origString is null return
while @idx!= 0
begin
set @idx = charindex(@Delimiter,@origString)
if @idx!=0
set @split= left(@origString,@idx - 1)
else
set @split= @origString
if(len(@split)>0)
insert into @temptable(Items) values(@split)
set @origString= right(@origString,len(@origString) - @idx)
if len(@origString) = 0 break
end
return
end
只需执行以下操作:
select *
from @inputtable
outer apply dbo.split(',', hobbies) splithobbies
我通常更喜欢使用XML将CSV列表拆分为表值格式。您可以检查此功能:
CREATE FUNCTION dbo.SplitStrings_XML
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
FROM
(
SELECT x = CONVERT(XML, '<i>'
+ REPLACE(@List, @Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i)
);
GO
下面我们将介绍更多的技术来演示如何做到这一点。然后,您只需要使用CROSS APPLY子句来应用函数。即使在您的示例中,每个人都有三个兴趣爱好,但我假设每个人实际上可以有一个或多个兴趣爱好?@littlebbytables是的,我应该提到兴趣爱好列表可以是任意长度的。您必须交叉应用或外部应用TVF。@Denis-谢谢Denis。忘了我在处理表函数。现在更新我的答案。谢谢,交叉申请是我想要的!我将进一步阅读SQL Server中没有本机拆分函数,这很糟糕。@Philip答案在问题的上下文中给出。您是否看到在问题的开头已经实现了一个拆分函数?所以,没关系。是正确的,外部应用是我需要知道的部分,所以这个答案是完全有效的。我只是阅读了介绍,暗示这是一个典型的基于光标的循环解决方案,看到了其他人发布的拆分函数,并假设这与SO上发布的所有其他字符串解析问题一样。唉,除非修改原来的答复,否则投票被否决或以其他方式被否决的情况是无法逆转的;如果你加一个空格,改变一些标点,我会把它倒过来。
CREATE FUNCTION dbo.SplitStrings_XML
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
SELECT Item = y.i.value('(./text())[1]', 'nvarchar(4000)')
FROM
(
SELECT x = CONVERT(XML, '<i>'
+ REPLACE(@List, @Delimiter, '</i><i>')
+ '</i>').query('.')
) AS a CROSS APPLY x.nodes('i') AS y(i)
);
GO