Sql 将字符串拆分为3列-扭曲
我有一个很长的文本字符串被导入到一个表中。我想把绳子分开;我有一个将数据拉入表的例程,但它会在表中的单个字段中创建所有数据 示例文本:Sql 将字符串拆分为3列-扭曲,sql,sql-server,tsql,sql-server-2014,Sql,Sql Server,Tsql,Sql Server 2014,我有一个很长的文本字符串被导入到一个表中。我想把绳子分开;我有一个将数据拉入表的例程,但它会在表中的单个字段中创建所有数据 示例文本: 05/10/2018 21:14,#FXAAF00123456,,Cup 1 X Plane,0.00000,OK,Cup 1 Y Plane,0.00000,OK,Cup 1 Z Plane,40.64252,OK,Cup 2 X Plane,77.89434,OK,..etc 测试字符串比这个长得多,大约1500-1700个字符,但在字符串的其余部分具有相
05/10/2018 21:14,#FXAAF00123456,,Cup 1 X Plane,0.00000,OK,Cup 1 Y Plane,0.00000,OK,Cup 1 Z Plane,40.64252,OK,Cup 2 X Plane,77.89434,OK,..etc
测试字符串比这个长得多,大约1500-1700个字符,但在字符串的其余部分具有相同的结构
该数据是一系列测试测量值,包括值、值和OK/NOK指示器的名称
我想将结果存储在一个包含三个字段的表变量中,因此上面的数据变成:
Field1|Field2|Field3
05/10/2018 21:14|#FXAAF00123456|null|
Cup 1 X Plane|0.00000|OK|
Cup 1 Y Plane|0.00000|OK|
Cup 1 Z Plane|40.64252|OK|
Cup 2 X Plane|77.89434|OK|
...etc
我使用此函数将字符串拆分为表变量:
CREATE FUNCTION [dbo].[fnSplitString]
(
@InputString NVARCHAR(MAX),
@Delim VARCHAR(255)
)
RETURNS TABLE
AS
RETURN ( SELECT [Value] FROM
(
SELECT
[Value] = LTRIM(RTRIM(SUBSTRING(@InputString, [Number],
CHARINDEX(@Delim, @InputString + @Delim, [Number]) - [Number])))
FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_objects) AS x
WHERE Number <= LEN(@InputString)
AND SUBSTRING(@Delim + @InputString, [Number], LEN(@Delim)) = @Delim
) AS y
);
如何对其进行修改以提供上述所需的输出?这需要对fnSplitString函数进行少量修改。添加行号以标识分隔项的原始序列
CREATE FUNCTION [dbo].[fnSplitString]
(
@InputString NVARCHAR(MAX),
@Delim VARCHAR(255)
)
RETURNS TABLE
AS
RETURN ( SELECT [Value] FROM
(
SELECT RowNo = ROW_NUMBER() OVER (ORDER BY Number),
[Value] = LTRIM(RTRIM(SUBSTRING(@InputString, [Number],
CHARINDEX(@Delim, @InputString + @Delim, [Number]) - [Number])))
FROM (SELECT Number = ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_objects) AS x
WHERE Number <= LEN(@InputString)
AND SUBSTRING(@Delim + @InputString, [Number], LEN(@Delim)) = @Delim
) AS y
);
您可以尝试这种微小的内联拆分方法
DECLARE @s VARCHAR(MAX)='05/10/2018 21:14,#FXAAF00123456,,Cup 1 X Plane,0.00000,OK,Cup 1 Y Plane,0.00000,OK,Cup 1 Z Plane,40.64252,OK,Cup 2 X Plane,77.89434,OK';
;WITH
a AS (SELECT n=0, i=-1, j=0 UNION ALL SELECT n+1, j, CAST(CHARINDEX(',', @s, j+1) AS INT) FROM a WHERE j > i)
,b AS (SELECT n, SUBSTRING(@s, i+1, IIF(j>0, j, LEN(@s)+1)-i-1) s FROM a WHERE i >= 0)
,c AS (SELECT n,(n-1) % 3 AS Position,(n-1)/3 AS RowIndex,s FROM b)
SELECT MAX(CASE WHEN Position=0 THEN s END) AS part1
,MAX(CASE WHEN Position=1 THEN s END) AS part2
,MAX(CASE WHEN Position=2 THEN s END) AS part3
FROM c
GROUP BY RowIndex
OPTION (MAXRECURSION 0);
结果
part1 part2 part3
05/10/2018 21:14 #FXAAF00123456
Cup 1 X Plane 0.00000 OK
Cup 1 Y Plane 0.00000 OK
Cup 1 Z Plane 40.64252 OK
Cup 2 X Plane 77.89434 OK
暗示
您可以将拆分器函数更改为上面的递归方法。一方面,您被限制为sys.all_对象中计数的字符串长度,该长度可能小于您的输入。另一方面,您的方法必须测试每个位置和任何位置,而递归方法从一个点跳到另一个点。应该快一点。。。
如果需要,可以很容易地打开多字符分隔符
不使用递归更新另一种方法
…由于选项MAXRECURSION0必须放在查询的末尾,并且不能在函数中使用,因此在拆分器函数中使用它非常笨拙。试一试:
;WITH
a(Casted) AS (SELECT CAST('<x>' + REPLACE((SELECT @s AS [*] FOR XML PATH('')),',','</x><x>') + '</x>' AS XML))
,b(s,RowIndex,Position) AS
(
SELECT x.value(N'text()[1]','nvarchar(max)')
,(ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) -1) /3
,(ROW_NUMBER() OVER(ORDER BY(SELECT NULL)) -1) %3
FROM a
CROSS APPLY Casted.nodes(N'/x') X(x)
)
SELECT RowIndex
,MAX(CASE WHEN Position=0 THEN s END) AS part1
,MAX(CASE WHEN Position=1 THEN s END) AS part2
,MAX(CASE WHEN Position=2 THEN s END) AS part3
FROM b
GROUP BY RowIndex;
提示:
使用SELECT@s AS[*]FOR XML PATH将使此方法以禁止字符保存…在参考文档中创建给定的脚本后,您可以尝试以下脚本吗 该split函数返回被拆分字符串片段的顺序,以便将信息用于行数据
declare @str nvarchar(max) = '05/10/2018 21:14,#FXAAF00123456,,Cup 1 X Plane,0.00000,OK,Cup 1 Y Plane,0.00000,OK,Cup 1 Z Plane,40.64252,OK,Cup 2 X Plane,77.89434,OK'
select
floor(id / 3)+1 rn,
case when id % 3 = 1 then val end Field1,
case when id % 3 = 2 then val end Field2,
case when id % 3 = 0 then val end Field3
from dbo.Split(@str,',')
select
rn,
max(Field1) Field1,
max(Field2) Field2,
max(Field3) Field3
from (
select
floor((id-1) / 3)+1 rn,
case when id % 3 = 1 then val end Field1,
case when id % 3 = 2 then val end Field2,
case when id % 3 = 0 then val end Field3
from dbo.Split(@str,',')
) t
group by rn
您确实应该考虑将数据的第一部分保持在不同的列中——因为它明显不同的数据——第一个是日期时间值,第二个可能是测试标识符——您不想将日期时间保存为字符串,您可能需要知道什么样的测试属于什么标识符。你在和谁一起工作?@Zohar是的,我同意。我的目的是处理变量中的数据,以正确存储结果。我只需要先得到正确格式的数据。第一行或文本字符串中的前3个值的处理方式将与实际结果不同。我将在接收记录的表上创建一个触发器,以拾取数据并在其他地方创建一个已清理的副本。此特定服务器当前为SQL 2014,因此无法使用2016年引入的新功能!回答得好,我这边+1。我们使用相同的技巧来计算三行逻辑:-我喜欢这个想法。。。但无法使函数正常工作。你能用调整过的fnSplitString函数更新你的帖子吗?链接拆分器函数有几个严重的缺陷:1老式的多语句TVF非常慢!,2它不是为禁止字符保存,3它使用了深度搜索//根,这不必要地再次减慢了这种方法。有很多更好的方法…@Shnugo,我知道:我将用你的替代品asapIt works修改UDF,给我我所需要的。速度不会是一个大问题,因为每小时插入的记录不多,因此负载将相当低。有兴趣看到这种方法的最终结果-我在这里学到了很多,所以感谢大家的见解和投入。感谢你们的评论,鼓励分享更多:谢谢。不幸的是,我在100之后得到了一个递归错误——我猜这可以在SQL设置中的某个地方增加?我需要大约170个字符串,因为每个字符串返回56个不同的读数。另外,这是否可以调整为也给我一个行号?@AdzzzUK只需添加选项MAXRECURSION 0作为查询的最后一行…@AdzzzUK您只需将其添加到结果集即可。看我的最新口述。谢谢你的帮助,这正是我所需要的。非常感谢。
declare @str nvarchar(max) = '05/10/2018 21:14,#FXAAF00123456,,Cup 1 X Plane,0.00000,OK,Cup 1 Y Plane,0.00000,OK,Cup 1 Z Plane,40.64252,OK,Cup 2 X Plane,77.89434,OK'
select
floor(id / 3)+1 rn,
case when id % 3 = 1 then val end Field1,
case when id % 3 = 2 then val end Field2,
case when id % 3 = 0 then val end Field3
from dbo.Split(@str,',')
select
rn,
max(Field1) Field1,
max(Field2) Field2,
max(Field3) Field3
from (
select
floor((id-1) / 3)+1 rn,
case when id % 3 = 1 then val end Field1,
case when id % 3 = 2 then val end Field2,
case when id % 3 = 0 then val end Field3
from dbo.Split(@str,',')
) t
group by rn