简体   繁体   English

在 T-SQL 中拆分字符串并折叠

[英]Split string and fold in T-SQL

Is it possible to split a delimited string and then 'fold' the delimited parts such that the result is a string containing all possible 'paths'?是否可以分割一个分隔的字符串,然后“折叠”分隔的部分,这样结果是一个包含所有可能的“路径”的字符串? I'm looking to purely use built-in functions if possible without resorting to recursive CTEs, etc.如果可能的话,我希望纯粹使用内置函数而不诉诸递归 CTE 等。

This is a common functional pattern known as scan/fold.这是一种常见的功能模式,称为扫描/折叠。 Wondering if T-SQL has a similar pattern.想知道 T-SQL 是否有类似的模式。

Example例子

FOLD('A|B|C|D') = '[A],[A|B],[A|B|C],[A|B|C|D]'

EDIT: The order of the substrings must remain the same in the result.编辑:结果中子字符串的顺序必须保持不变。 The target SQL version is Azure SQL.目标 SQL 版本是 Azure SQL。

if you have sql-server-2017 you can use STRING_AGG and STRING_SPLIT如果你有 sql-server-2017 你可以使用STRING_AGGSTRING_SPLIT

declare @text VARCHAR(MAX) ='A|B|C|D'

declare @result VARCHAR(MAX) = ''

SELECT @result  = @result  + ',[' + STRING_AGG(X.value, '|') + ']' FROM 
    STRING_SPLIT(@text ,'|') X
    INNER JOIN STRING_SPLIT(@text ,'|') Y
    ON X.value <= Y.Value
GROUP BY Y.Value

SET @result = STUFF(@result,1,1,'')

print @result 

Result:结果:

[A],[A|B],[A|B|C],[A|B|C|D]

As I note in the comments, STRING_SPLIT has a big caveat in the documentation :正如我在评论中所指出的, STRING_SPLIT文档中有一个很大的警告:

The order is not guaranteed to match the order of the substrings in the input string.该顺序不能保证与输入字符串中子字符串的顺序匹配。

As a result, you're safer off using a function that gives you the ordinal position.因此,使用为您提供序数位置的函数会更安全。 In this case I use DelimitedSplit8K_LEAD and then assume you are using SQL Server 2017+:在这种情况下,我使用DelimitedSplit8K_LEAD ,然后假设您使用的是 SQL Server 2017+:

DECLARE @YourString varchar(20) = 'A|B|C|D';
WITH Splits AS(
    SELECT DS.ItemNumber,
           DS.Item
    FROM dbo.DelimitedSplit8K_LEAD(@YourString,'|') DS),
Groups AS(
    SELECT S1.ItemNumber,
           CONCAT('[',STRING_AGG(S2.Item,'|') WITHIN GROUP (ORDER BY S2.ItemNumber),']') AS Agg
    FROM Splits S1
         JOIN Splits S2 ON S1.ItemNumber >= S2.ItemNumber
    GROUP BY S1.ItemNumber)
SELECT STRING_AGG(Agg,',') WITHIN GROUP (ORDER BY ItemNumber)
FROM Groups;

If you aren't on SQL Server 2017+, you'll need to use the "old" FOR XML PATH (and STUFF ) method.如果您不在 SQL Server 2017+ 上,则需要使用“旧” FOR XML PATH (和STUFF )方法。

DB<>Fiddle 数据库<>小提琴

Just in case you don't want (or can't use) that SUPER DelimitedSplit8K_LEAD , here is an XML approach that will maintain the sequence以防万一您不想要(或不能使用) SUPER DelimitedSplit8K_LEAD ,这里有一种 XML 方法可以维护序列

Example例子

Declare @S varchar(max) = 'A|B|C|D'

;with cte as (
    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(@S,'|','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A 
    Cross Apply x.nodes('x') AS B(i)
), cte1 as (
   Select *
         ,Comb ='['+stuff((select '|' +RetVal From cte Where RetSeq<=A.RetSeq Order By RetSeq For XML Path ('')),1,1,'') +']'
    From  cte A
    Group By RetSeq,RetVal
)
Select NewValue = stuff((select ',' +Comb From cte1 Order By RetSeq For XML Path ('')),1,1,'')

Returns退货

NewValue
[A],[A|B],[A|B|C],[A|B|C|D]

Starting with v2016 there is JSON, which allows for a position-safe splitter using a JSON array.从 v2016 开始,有 JSON,它允许使用 JSON 数组的位置安全拆分器。 The path can be built with a recursive CTE:可以使用递归 CTE 构建路径:

DECLARE @yourString VARCHAR(MAX) ='A|B|C|D';

WITH cte AS
(
    SELECT A.[key] AS ItmIndex
          ,A.[value] AS ItmVal 
    FROM OPENJSON(CONCAT('["',REPLACE(@yourString,'|','","'),'"]')) A
)
,rcte AS
(
    SELECT ItmIndex, ItmVal
          ,CAST(ItmVal AS VARCHAR(MAX)) AS Result 
    FROM cte 
    WHERE ItmIndex=0

    UNION ALL
    SELECT cte.ItmIndex, cte.ItmVal 
          ,CAST(CONCAT(rcte.Result,'|',cte.ItmVal) AS VARCHAR(MAX))
    FROM cte
    INNER JOIN rcte ON cte.ItmIndex=rcte.ItmIndex+1
)
SELECT * FROM rcte;

The idea in short:简而言之这个想法:

  • The first cte will transform your string into a set with a guaranteed sort order (other than STRING_SPLIT() ).第一个 cte 会将您的字符串转换为具有保证排序顺序的集合( STRING_SPLIT()除外)。
  • The second cte will start with the array's index 0 and then travers the list adding each item to the growing string.第二个 cte 将从数组的索引 0 开始,然后遍历列表,将每个项目添加到不断增长的字符串中。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM