我有一个很长的文本字符串被导入到表中。我想把线分开。我有一个例程可以将数据提取到表中,但是它会在表的单个字段中创建所有数据。
示例文字:
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
);
如何修改它以提供上面要求的输出?
答案 0 :(得分:2)
您可以尝试这种微小的内联拆分方法。
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_objects
中计数的字符串长度,该长度可能小于您的输入。另一方面,您的方法必须测试每个位置,而递归方法会从一个地方跳到另一个地方。应该更快...
如果需要,可以轻松地为多字符定界符打开它。
...使得在拆分器函数中使用起来很笨拙(由于OPTION MAXRECURSION(0)
,必须将其放在查询的末尾,并且不能存在于该函数中)。试试看:
;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(''))
将使此方法保存为禁止字符...
答案 1 :(得分:1)
这需要对您的fnSplitString
函数进行一些小的修改。添加RowNo
来标识定界项目的原始顺序
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
);
因此,您可以将每3行分组为一个。 RowNo
也可以用来标识列
查询
; with tbl as
(
select col = '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 Field1 = MAX(CASE WHEN (RowNo - 1) % 3 = 0 THEN Value END),
Field2 = MAX(CASE WHEN (RowNo - 1) % 3 = 1 THEN Value END),
Field3 = MAX(CASE WHEN (RowNo - 1) % 3 = 2 THEN Value END)
from tbl t
cross apply dbo.fnSplitString (t.col, ',')
group by (RowNo - 1) / 3
答案 2 :(得分:0)
在创建参考文档中给出的SQL split function之后,您可以尝试使用以下脚本吗?
该拆分函数返回拆分后的字符串片段的顺序,以便将信息用于行数据
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