将字符串分成3列-旋转

时间:2018-10-10 08:40:53

标签: 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个字符之间,但其余字符串具有相同的结构)。

此数据是一系列测试测量,带有值的名称,值和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
        );

如何修改它以提供上面要求的输出?

3 个答案:

答案 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