Sql 在列中拆分字符串
我有来自分层数据库的数据,如果原始数据库是关系数据库,它通常有包含应该在另一个表中的数据的列 列的数据成对格式化,使用空格作为分隔符的Sql 在列中拆分字符串,sql,sql-server,tsql,parsing,split,Sql,Sql Server,Tsql,Parsing,Split,我有来自分层数据库的数据,如果原始数据库是关系数据库,它通常有包含应该在另一个表中的数据的列 列的数据成对格式化,使用空格作为分隔符的LABEL\VALUE,如下所示: LABEL1\VALUE LABEL2\VALUE LABEL3\VALUE 一张唱片中很少有超过一对的,但最多有三对。有24种不同的标签。此表中还有其他列,包括ID。我已经能够将此列转换为稀疏数组,而无需使用游标,其中包含ID、LABEL1、LABEL2等列 但这对于在另一个查询中使用并不理想。我的另一个选择是使用游标,在整个
LABEL\VALUE
,如下所示:
LABEL1\VALUE LABEL2\VALUE LABEL3\VALUE
一张唱片中很少有超过一对的,但最多有三对。有24种不同的标签。此表中还有其他列,包括ID。我已经能够将此列转换为稀疏数组,而无需使用游标,其中包含ID、LABEL1、LABEL2等列
但这对于在另一个查询中使用并不理想。我的另一个选择是使用游标,在整个表中循环一次,然后写入临时表,但我看不到如何让它以我想要的方式工作。我已经能够在VB.NET中使用几个嵌套循环在几分钟内完成这项工作,但在t-SQL中即使使用游标也无法完成这项工作。问题是,在我想使用它创建的表之前,我必须记住每次都要运行这个程序。不理想
因此,我读取一行,将“LABEL1\VALUE-LABEL2\VALUE-LABEL3\VALUE”中的对拆分为一个数组,然后再次拆分,然后写入行
ID,标签1,值
ID,标签2,值
ID,标签3,值
等等
我意识到在这里“拆分”字符串对于SQL来说是很难做到的,但它似乎比需要做到的要困难得多。我遗漏了什么?只有三个值,您可以通过暴力做到这一点:
select (case when rest like '% %'
then left(rest, charindex(' ', rest) - 1)
else rest
end) as val2,
(case when rest like '% %'
then substring(col, charindex(' ', col) + 1, 1000)
end) as val3
from (select (case when col like '% %'
then left(col, charindex(' ', col) - 1)
else col
end) as val1,
(case when col like '% %'
then substring(col, charindex(' ', col) + 1, 1000)
end) as rest
from t
) t
假设数据标签不包含
字符,您可以使用一个简单的函数:
CREATE FUNCTION [dbo].[SplitGriswold]
(
@List NVARCHAR(MAX),
@Delim1 NCHAR(1),
@Delim2 NCHAR(1)
)
RETURNS TABLE
AS
RETURN
(
SELECT
Val1 = PARSENAME(Value,2),
Val2 = PARSENAME(Value,1)
FROM
(
SELECT REPLACE(Value, @Delim2, '.') FROM
(
SELECT LTRIM(RTRIM(SUBSTRING(@List, [Number],
CHARINDEX(@Delim1, @List + @Delim1, [Number]) - [Number])))
FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_objects) AS x
WHERE Number <= LEN(@List)
AND SUBSTRING(@Delim1 + @List, [Number], LEN(@Delim1)) = @Delim1
) AS y(Value)
) AS z(Value)
);
GO
(我使用的Unicode字符不太可能出现在上面的数据中,只是因为空格对于长度检查之类的事情可能会有问题。如果可能出现此字符,请选择其他字符。)
结果:
ID val1 val2
-- -------- --------
1标签1值
1 LABEL2值
1 LABEL3值
2标签1值2
2标签2值2
如果您的数据可能有
,那么您可以在不更改函数的情况下,通过向混合中添加另一个不太可能或不可能出现在数据中的字符,使查询稍微复杂一些:
DECLARE @x TABLE(ID INT, string VARCHAR(255));
INSERT @x VALUES
(1, 'LABEL1\VALUE.A LABEL2\VALUE.B LABEL3\VALUE.C'),
(2, 'LABEL1\VALUE2.A LABEL2.1\VALUE2.B');
SELECT x.ID, val1 = REPLACE(t.val1, N'ű', '.'), val2 = REPLACE(t.val2, N'ű', '.')
FROM @x AS x CROSS APPLY
dbo.SplitGriswold(REPLACE(REPLACE(x.string, ' ', 'ŏ'), '.', N'ű'), 'ŏ', '\') AS t;
结果:
ID val1 val2
-- -------- --------
1标签1值。A
1标签2值。B
1标签3值C
2标签1值2.A
2标签2.1值2.B
使用给定的at参考SQL教程,您可以按如下方式拆分标签值对
SELECT
id, max(label) as label, max(value) as value
FROM (
SELECT
s.id,
label = case when t.id = 1 then t.val else NULL end,
value = case when t.id = 2 then t.val else NULL end
FROM dbo.Split(N'LABEL1\VALUE1 LABEL2\VALUE2 LABEL3\VALUE3', ' ') s
CROSS APPLY dbo.Split(s.val, '\') t
) t
group by id
您可以看到,splitstring函数被调用了两次,第一次用于从其他函数中拆分对。然后,使用交叉应用将第二个拆分函数连接到前一个拆分函数,将标签成对拆分
看看这个问题,也许这可以帮助FYI,因为所有这些帮助都证明了分析师关于数据的信息是错误的。有问题的专栏是关于产品的包装信息,它的格式与他们想象的不一样。这似乎是我问题的一个完整解决方案。我能够用我的表数据简单地替换x,并更改字段名称以匹配,这是第一次成功。标签中没有可能的“.”,因此第一个查询成功,这并不重要,但所有标签都只有两个字符。我的数据中的ID不是每个标签\值对。每个ID可能有多个label\value对。这会起作用,但我应该说,在当前数据中,我只看到了三个label\value对,但这并不排除将来会有更多的label\value对。@DavidGriswold。问题是“多达三个”。用户定义函数或递归CTE是更一般地实现这一点的方法。你是对的,这就是我所说的。如果我能:(
SELECT
id, max(label) as label, max(value) as value
FROM (
SELECT
s.id,
label = case when t.id = 1 then t.val else NULL end,
value = case when t.id = 2 then t.val else NULL end
FROM dbo.Split(N'LABEL1\VALUE1 LABEL2\VALUE2 LABEL3\VALUE3', ' ') s
CROSS APPLY dbo.Split(s.val, '\') t
) t
group by id