分割字符串并保留字符位置

时间:2020-06-11 17:54:29

标签: sql-server sql-server-2016

我有一个大字符串(nvarchar(MAX)),我试图将其拆分为2,并确定它们是原始字符串的哪一部分。

示例

字符串:

5;718;0;1071;1.23|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25

我首先需要根据'|'字符拆分字符串,如下所示:

5;718;0;1071;1.23

0;750;0;997;1.25

0;750;0;997;1.25

0;750;0;997;1.25

0;750;0;997;1.25

0;750;0;997;1.25

然后我将根据';'字符对每个字符进行拆分:

因此,5;718;0;1071;1.23将拆分为:

5
718
0
1071
1.23

我知道我可以在'|'上进行一个string_split,然后在';'上进行另一个string_split。但这并不能保持顺序,也无法确定结果是从字符串的哪一部分中分离出来的,很不幸,当尝试使用OPENJSON()时,我无法完全获得所需的结果:

基于上面的示例,我需要一个可以识别718来自第一组和第二组的结果。

3 个答案:

答案 0 :(得分:2)

这是一个可以解析您的字符串并保持顺序的选项

示例

Declare @S varchar(max) = '5;718;0;1071;1.23|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25'

Select Seq1 = A.RetSeq
      ,Val1 = A.RetVal
      ,Seq2 = B.RetSeq
      ,Val2 = B.RetVal
 From  [dbo].[tvf-Str-Parse](@S,'|') A
 Cross Apply [dbo].[tvf-Str-Parse](A.RetVal,';') B

返回

enter image description here

感兴趣的功能

CREATE FUNCTION [dbo].[tvf-Str-Parse] (@String varchar(max),@Delimiter varchar(10))
Returns Table 
As
Return (  
    Select RetSeq = row_number() over (order by 1/0)
          ,RetVal = ltrim(rtrim(B.i.value('(./text())[1]', 'varchar(max)')))
    From  (Select x = Cast('<x>' + replace((Select replace(@String,@Delimiter,'§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A 
    Cross Apply x.nodes('x') AS B(i)
);

编辑-更新表

Declare @YourTable table (ID int,SomeCol varchar(max))
Insert Into @YourTable values
(1,'5;718;0;1071;1.23|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25')


Select A.ID
      ,Seq1 = B.RetSeq
      ,Val1 = B.RetVal
      ,Seq2 = C.RetSeq
      ,Val2 = C.RetVal
 From  @YourTable A
 Cross Apply [dbo].[tvf-Str-Parse](SomeCol,'|') B
 Cross Apply [dbo].[tvf-Str-Parse](B.RetVal,';') C

答案 1 :(得分:0)

这是我的旧学校照:

declare @v nvarchar(max) = N'5;718;0;1071;1.23|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25'


select a.value('.', 'nvarchar(max)') [value], v2.col, rnInternal,ROW_NUMBER() over(partition by rnInternal order by rnInternal, Split.a) rnExternal
from 
(
    select cast('<M>' + REPLACE(v.[value], ';', '</M><M>') + '</M>' AS XML) as col, rnInternal
    from 
    (
        select 
            a.value('.', 'nvarchar(max)') [value],
            ROW_NUMBER() over(order by Split.a) rnInternal
        from
            (select cast('<M>' + REPLACE(@v, '|', '</M><M>') + '</M>' AS XML) as col) as A
            CROSS APPLY A.col.nodes ('/M') AS Split(a)
        where
            a.value('.', 'nvarchar(max)') <> ''
    ) v
) v2
CROSS APPLY v2.col.nodes ('/M') AS Split(a)
order by rnInternal, rnExternal

答案 2 :(得分:0)

您对STRING_SPLIT()是正确的(如documentation 输出行可以按任何顺序所述),但是您可以使用基于JSON的方法获得预期的结果。

您需要将输入文本转换为有效的嵌套JSON数组(将5;718;0;1071;1.23|0;750;0;997;1.25|转换为[[5,718,0,1071,1.23],[0,750,0,997,1.25]]),并使用默认模式通过两次OPENJSON()调用来解析此数组。 OPENJSON()调用的结果是一个包含列keyvaluetype的表,如果是JSON数组,则key列返回索引的索引。指定数组中的元素:

DECLARE @text nvarchar(max) = N'5;718;0;1071;1.23|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25|0;750;0;997;1.25'

SELECT 
   CONVERT(int, j1.[key]) + 1 AS id1,
   CONVERT(int, j2.[key]) + 1 AS id2,
   j2.[value]
FROM OPENJSON(CONCAT('[[', REPLACE(REPLACE(@text, '|', '],['), ';', ','), ']]')) j1
CROSS APPLY OPENJSON(j1.[value]) j2
ORDER BY CONVERT(int, j1.[key]), CONVERT(int, j2.[key])

结果:

id1 id2 value
1   1   5
1   2   718
1   3   0
1   4   1071
1   5   1.23
2   1   0
2   2   750
...
6   1   0
6   2   750
6   3   0
6   4   997
6   5   1.25

如果要标识每个组和子组的ID,则需要添加适当的WHERE子句:

SELECT 
   CONVERT(int, j1.[key]) + 1 AS id1,
   CONVERT(int, j2.[key]) + 1 AS id2
FROM OPENJSON(CONCAT('[[', REPLACE(REPLACE(@text, '|', '],['), ';', ','), ']]')) j1
CROSS APPLY OPENJSON(j1.[value]) j2
WHERE j2.[value] = '718'
ORDER BY CONVERT(int, j1.[key]), CONVERT(int, j2.[key])

结果:

id1 id2
1   2