Разбейте струну и сложите в T- SQL - PullRequest
0 голосов
/ 30 марта 2020

Можно ли разделить строку с разделителями и затем «сложить» разделенные части так, чтобы в результате получилась строка, содержащая все возможные «пути»? Я пытаюсь использовать только встроенные функции, если это возможно, не прибегая к рекурсивным CTE, и т. Д. c.

Это распространенная функциональная схема, известная как сканирование / складывание. Интересно, если T- SQL имеет похожий шаблон.

Пример

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

РЕДАКТИРОВАТЬ: порядок подстрок должен оставаться неизменным в результате. Целевая SQL версия Azure SQL.

Ответы [ 4 ]

2 голосов
/ 30 марта 2020

Как я отмечаю в комментариях, STRING_SPLIT имеет большое предостережение в документации :

Порядок не гарантированно соответствует порядку подстрок в входная строка.

В результате вы безопаснее, используя функцию, которая дает вам порядковый номер. В этом случае я использую 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;

Если вы не используете SQL Server 2017+, вам нужно использовать «старый» метод FOR XML PATHSTUFF).

DB <> Fiddle

2 голосов
/ 30 марта 2020

если у вас sql -server-2017, вы можете использовать STRING_AGG и STRING_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 

Результат:

[A],[A|B],[A|B|C],[A|B|C|D]
0 голосов
/ 31 марта 2020

Начиная с v2016, существует JSON, что позволяет использовать позиционно-безопасный сплиттер с использованием массива JSON. Путь можно построить с помощью рекурсивного 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;

Идея вкратце:

  • Первый cte преобразует вашу строку в набор с гарантированным порядком сортировки (другое чем STRING_SPLIT()).
  • Второй cte начинается с индекса массива 0, а затем пересекает список, добавляя каждый элемент в растущую строку.
0 голосов
/ 30 марта 2020

На тот случай, если вы не хотите (или не можете использовать) это SUPER DelimitedSplit8K_LEAD, вот подход XML, который будет поддерживать последовательность

Пример

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,'')

Возвращает

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