Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/80.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Sql 将字符串拆分为3列-扭曲_Sql_Sql Server_Tsql_Sql Server 2014 - Fatal编程技术网

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