Tsql 在一个表的列中分解csv以插入到新表中
如何拆分如下所示的值: 'some:1234,dumb:4321,thing:6534,some:65432,did:43287' 为此:Tsql 在一个表的列中分解csv以插入到新表中,tsql,sql-server-2012,Tsql,Sql Server 2012,如何拆分如下所示的值: 'some:1234,dumb:4321,thing:6534,some:65432,did:43287' 为此: value1 value2 some 1234 dumb 4321 thing 6534 someone 65432 did 43287 用于插入到新表中 下面的代码显示了一些我想要实现的东西。我已经为此工作了大约一天了,现在把头发拔出来 我还包括了表和函数定义以及几行数据,以说明我希望实现的目标 -- S
value1 value2
some 1234
dumb 4321
thing 6534
someone 65432
did 43287
用于插入到新表中
下面的代码显示了一些我想要实现的东西。我已经为此工作了大约一天了,现在把头发拔出来
我还包括了表和函数定义以及几行数据,以说明我希望实现的目标
-- Shamelessly copied this function from the CTE String Split here: https://sqlperformance.com/2012/07/t-sql-queries/split-strings
CREATE FUNCTION dbo.splitStrings -- Just being trying to be complete so adding this function since it is not an mssql built-in
(
@List NVARCHAR(MAX),
@Delimiter NVARCHAR(255)
)
RETURNS @Items TABLE (Item NVARCHAR(4000))
WITH SCHEMABINDING
AS
BEGIN
DECLARE @ll INT = LEN(@List) + 1, @ld INT = LEN(@Delimiter);
WITH a AS
(
SELECT
[start] = 1,
[end] = COALESCE(NULLIF(CHARINDEX(@Delimiter,
@List, 1), 0), @ll),
[value] = SUBSTRING(@List, 1,
COALESCE(NULLIF(CHARINDEX(@Delimiter,
@List, 1), 0), @ll) - 1)
UNION ALL
SELECT
[start] = CONVERT(INT, [end]) + @ld,
[end] = COALESCE(NULLIF(CHARINDEX(@Delimiter,
@List, [end] + @ld), 0), @ll),
[value] = SUBSTRING(@List, [end] + @ld,
COALESCE(NULLIF(CHARINDEX(@Delimiter,
@List, [end] + @ld), 0), @ll)-[end]-@ld)
FROM a
WHERE [end] < @ll
)
INSERT @Items SELECT [value]
FROM a
WHERE LEN([value]) > 0
OPTION (MAXRECURSION 0);
RETURN;
END
GO
-- Primary table where the messed up data currently resides
create table dbo.things
(
Id int identity(1,1) not null,
User int not null,
Values varchar(256)
)
-- This is the bad data
insert into things(USER,Values) values
{100,'some:1234,dumb:4321,thing:6534,someone:65432,did:43287'},
{101,'this:31234,is:43221,so wrong:65482'}
-- This is where the split up data will reside in the future
create table dbo.propertableforvalues
(
ThingId int not null,
ValueID int not null,
ValueName varchar(256) not null
)
-- This doesn't come close to working, but 'looks' like what I'm trying to achieve
insert into dbo.propertableforvalues
select
(
select
t.Id,
outerSplit.Name,
outerSplit.ValueId
from
(select -- one 'things' record should create n rows based on the number of items in the comma split of the 'Values' string
innerT.Name,
innerT.ValueId
from
dbo.splitStrings(
(select
ValueId,
ValueName
from
dbo.splitStrings(t.Values, ',') -- Split first based on the comma
, ':')) as innerSplit -- Split a second time on the colon
) as outerSplit
)
from
things t
where
t.Values is not null
and t.Values <> ''
一种选择是将字符串转换为XML,然后简单地解析XML 范例 编辑-通知数据位于表中
在这种情况下,我更喜欢这种方法: 这是你的绳子
DECLARE @csv VARCHAR(100)='some:1234,dumb:4321,thing:6534,someone:65432,did:43287';
某些替换将此字符串转换为XML:
SELECT CAST('<x><y>' + REPLACE(REPLACE((SELECT @csv AS [*] FOR XML PATH('')),',','</y></x><x><y>'),':','</y><y>') + '</y></x>' AS XML);
这是我们可以用XML方法查询的结果
<x>
<y>some</y>
<y>1234</y>
</x>
<x>
<y>dumb</y>
<y>4321</y>
</x>
<x>
<y>thing</y>
<y>6534</y>
</x>
<x>
<y>someone</y>
<y>65432</y>
</x>
<x>
<y>did</y>
<y>43287</y>
</x>
试一试:
DECLARE @csv VARCHAR(100)='some:1234,dumb:4321,thing:6534,someone:65432,did:43287';
SELECT x.value('y[1]','nvarchar(max)') AS Item
,x.value('y[2]','nvarchar(max)') AS [Value]
FROM (SELECT CAST('<x><y>' + REPLACE(REPLACE((SELECT @csv AS [*] FOR XML PATH('')),',','</y></x><x><y>'),':','</y><y>') + '</y></x>' AS XML)) t(casted)
CROSS APPLY t.casted.nodes('/x') A(x);
使现代化
表数据也是如此
DECLARE @things TABLE
(
Id int identity(1,1) not null,
[User] int not null,
[Values] varchar(256)
)
insert into @things([USER],[Values]) values
(100,'some:1234,dumb:4321,thing:6534,someone:65432,did:43287'),
(101,'this:31234,is:43221,so wrong:65482');
SELECT t.Id
,t.[User]
,x.value('y[1]','nvarchar(max)') AS Item
,x.value('y[2]','nvarchar(max)') AS [Value]
FROM @things t
CROSS APPLY (SELECT CAST('<x><y>' + REPLACE(REPLACE((SELECT t.[Values] AS [*] FOR XML PATH('')),',','</y></x><x><y>'),':','</y><y>') + '</y></x>' AS XML)) A(casted)
CROSS APPLY A.casted.nodes('/x') B(x);
作为一种选择,我将不发布执行相同任务的xml方法,不破坏前面给出的答案 为了使此方法正常工作,@CSV字符串必须在开头和结尾用分隔符括起来:
DECLARE @csv VARCHAR(8000) = 'some:1234,dumb:4321,thing:6534,some one:65432,did:43287'
SET @csv = ',' + @csv + ','
首先,建立一个数字cte,也称为计数:
并找到separatordelimiter的索引,在我们的例子中,separatordelimiter是一个'
SELECT N
FROM TALLY
WHERE SUBSTRING(@csv,N,1) = ','
然后使用子字符串并将索引值为delimiter的CSV切片以获得第一部分,然后使用新的delimiter重复相同的操作,即“:”
只有一个提示:如果项作为属性名无效,或者字符串可能包含禁止的字符,这种方法可能会遇到麻烦。。。就像第二个例子中的错误…John,你看到提供的测试数据了吗?不知道OP的真实世界数据,但101,“这:31234,是:43221,所以错误:65482”将由于错误的空白而中断…@ Shnugo它感觉像JSON对象。但你是对的。如果错了,它就会失败。没错。。。这是用sql-server-2012标记的-遗憾的是没有JSON支持。。。仍然是一个非常圆滑的方法-@Shnugo谢谢但你的X/Y更安全
<x>
<y>some</y>
<y>1234</y>
</x>
<x>
<y>dumb</y>
<y>4321</y>
</x>
<x>
<y>thing</y>
<y>6534</y>
</x>
<x>
<y>someone</y>
<y>65432</y>
</x>
<x>
<y>did</y>
<y>43287</y>
</x>
DECLARE @csv VARCHAR(100)='some:1234,dumb:4321,thing:6534,someone:65432,did:43287';
SELECT x.value('y[1]','nvarchar(max)') AS Item
,x.value('y[2]','nvarchar(max)') AS [Value]
FROM (SELECT CAST('<x><y>' + REPLACE(REPLACE((SELECT @csv AS [*] FOR XML PATH('')),',','</y></x><x><y>'),':','</y><y>') + '</y></x>' AS XML)) t(casted)
CROSS APPLY t.casted.nodes('/x') A(x);
DECLARE @things TABLE
(
Id int identity(1,1) not null,
[User] int not null,
[Values] varchar(256)
)
insert into @things([USER],[Values]) values
(100,'some:1234,dumb:4321,thing:6534,someone:65432,did:43287'),
(101,'this:31234,is:43221,so wrong:65482');
SELECT t.Id
,t.[User]
,x.value('y[1]','nvarchar(max)') AS Item
,x.value('y[2]','nvarchar(max)') AS [Value]
FROM @things t
CROSS APPLY (SELECT CAST('<x><y>' + REPLACE(REPLACE((SELECT t.[Values] AS [*] FOR XML PATH('')),',','</y></x><x><y>'),':','</y><y>') + '</y></x>' AS XML)) A(casted)
CROSS APPLY A.casted.nodes('/x') B(x);
DECLARE @csv VARCHAR(8000) = 'some:1234,dumb:4321,thing:6534,some one:65432,did:43287'
SET @csv = ',' + @csv + ','
-- Build 10000 numbers.
;WITH
TENS (N) AS (SELECT 0 UNION ALL SELECT 0 UNION ALL
SELECT 0 UNION ALL SELECT 0 UNION ALL
SELECT 0 UNION ALL SELECT 0 UNION ALL
SELECT 0 UNION ALL SELECT 0 UNION ALL
SELECT 0 UNION ALL SELECT 0),
THOUSANDS (N) AS (SELECT 1
FROM TENS t1
CROSS JOIN TENS t2
CROSS JOIN TENS t3),
TALLY (N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM THOUSANDS)
SELECT N
FROM TALLY
WHERE SUBSTRING(@csv,N,1) = ','
DECLARE @csv VARCHAR(8000) = 'some:1234,dumb:4321,thing:6534,some one:65432,did:43287'
SET @csv = ',' + @csv + ','
-- Build 10000 numbers.
;WITH
TENS (N) AS (SELECT 0 UNION ALL SELECT 0 UNION ALL
SELECT 0 UNION ALL SELECT 0 UNION ALL
SELECT 0 UNION ALL SELECT 0 UNION ALL
SELECT 0 UNION ALL SELECT 0 UNION ALL
SELECT 0 UNION ALL SELECT 0),
THOUSANDS (N) AS (SELECT 1
FROM TENS t1
CROSS JOIN TENS t2
CROSS JOIN TENS t3),
TALLY (N) AS (SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 0))
FROM THOUSANDS)
--Split CSV values into columns
,SPLITTED AS (
SELECT SUBSTRING(@csv,N+1,CHARINDEX(',',@csv,N+1)-N-1) AS ColName
FROM TALLY
WHERE N < LEN(@csv) AND SUBSTRING(@csv,N,1) = ',' )
--Split column values further into as multiple columns
SELECT Part1 = SUBSTRING(ColName,1,CHARINDEX(':', ColName,1)-1),
Part2 = SUBSTRING(ColName,CHARINDEX(':', ColName,1)+1,LEN(ColName))
FROM SPLITTED