Повторное извлечение строки для строки с разделителями - PullRequest
0 голосов
/ 02 апреля 2019

К сожалению, у меня нет разрешения на создание функций, поэтому я написал следующий запрос для извлечения первой, второй, третьей и четвертой частей разделительного канала page_name:

select page_name,
LEFT(page_name, first-1) AS P1,
case when second>0 then SUBSTRING(page_name,first+1,second-first-1) 
     else substring(page_name, first+1,1000) end As P2,
case when third>0 then SUBSTRING(page_name,second+1,third-second-1) 
     when second>0 then substring(page_name, second+1,1000) else '' end AS P3,
case when fourth>0 then SUBSTRING(page_name,third+1,fourth-third-1) 
     when third>0 then substring(page_name, third+1,1000) else '' end AS P4

from (
select distinct page_name,
CHARINDEX('|', page_name) first,
CHARINDEX('|', page_name, CHARINDEX('|', page_name)+1) second,
case when CHARINDEX('|', page_name, CHARINDEX('|', page_name)+1)=0 then 0 
     else CHARINDEX('|', page_name, CHARINDEX('|', page_name, charindex('|', page_name)+1)+1) end third,
case when CHARINDEX('|', page_name, CHARINDEX('|', page_name, charindex('|', page_name)+1)+1)=0 then 0 
     else CHARINDEX('|', page_name, CHARINDEX('|', page_name, CHARINDEX('|', page_name, charindex('|', page_name)+1)+1)+1) end fourth
from adobe_analytics
where page_name like '[a-z]%' and page_name like '%|%' 
) a

Проблемаиногда бывает около 10 частей, поэтому мне было интересно, есть ли лучший способ написать в подзапросе, который не заставит меня повторить тот же тип формулировки запроса при создании частей page_name?

Пример данных:

it-bae|
it-bae|content|advisor in newsletter
it-bae|content|area products|showcase products fideuram|fideuram fonditalia dynamic 
it-bae|content|events|events|events|webinars|new

Ответы [ 2 ]

1 голос
/ 02 апреля 2019

Я не уверен, если вам нужны ваши значения в столбцах или в строках. В строках было бы больше смысла, если число значений неизвестно. Этого можно добиться с помощью xml-сплиттера.

SELECT page_name,
    Value
FROM @tbl
CROSS APPLY( SELECT cast(('<X>' + replace( page_name, '|' ,'</X><X>') + '</X>') as xml) AS xmlpage_name) AS x
CROSS APPLY( SELECT N.value('.', 'varchar(255)') as value FROM xmlpage_name.nodes('X') as T(N)) AS Split;

Если вам нужно отобразить значения в столбцах, у вас должно быть определенное количество столбцов для хранения значений.

SELECT page_name,
    MAX(CASE WHEN ItemNumber = 1 THEN Value ELSE '' END) AS P1,
    MAX(CASE WHEN ItemNumber = 2 THEN Value ELSE '' END) AS P2,
    MAX(CASE WHEN ItemNumber = 3 THEN Value ELSE '' END) AS P3,
    MAX(CASE WHEN ItemNumber = 4 THEN Value ELSE '' END) AS P4,
    MAX(CASE WHEN ItemNumber = 5 THEN Value ELSE '' END) AS P5,
    MAX(CASE WHEN ItemNumber = 6 THEN Value ELSE '' END) AS P6,
    MAX(CASE WHEN ItemNumber = 7 THEN Value ELSE '' END) AS P7,
    MAX(CASE WHEN ItemNumber = 8 THEN Value ELSE '' END) AS P8,
    MAX(CASE WHEN ItemNumber = 9 THEN Value ELSE '' END) AS P9,
    MAX(CASE WHEN ItemNumber = 10 THEN Value ELSE '' END) AS P10
FROM @tbl
CROSS APPLY( SELECT cast(('<X>' + replace( page_name, '|' ,'</X><X>') + '</X>') as xml) AS xmlpage_name) AS x
CROSS APPLY( SELECT N.value('.', 'varchar(255)') as value, ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) ItemNumber FROM xmlpage_name.nodes('X') as T(N)) AS Split
GROUP BY page_name;
1 голос
/ 02 апреля 2019

Если вы заранее знаете максимальное количество строк с разделителями, вы можете использовать что-то в следующих строках (он использует рекурсивный CTE для построения таблицы отдельных строк, а затем поворачивает их, получая строкудля каждого поля page_name):

-- Table variable is just for example purposes.
DECLARE @tbl table (page_name varchar(255))
;

INSERT INTO @tbl
VALUES
('it-bae|')
, ('it-bae|content|advisor in newsletter')
, ('it-bae|content|area products|showcase products fideuram|fideuram fonditalia dynamic')
, ('it-bae|content|events|events|events|webinars|new')
;

WITH cteParts
AS
(
    SELECT
        page_name
        , 1 single_page_no
        , CASE WHEN CHARINDEX('|', page_name) = 0 THEN page_name ELSE LEFT(page_name, CHARINDEX('|', page_name) - 1) END single_page_name
        , CASE WHEN CHARINDEX('|', page_name) > 0 THEN RIGHT(page_name, LEN(page_name) - CHARINDEX('|', page_name)) END remainder
        , ROW_NUMBER() OVER (ORDER BY page_name) row_id
    FROM @tbl

    UNION ALL

    SELECT
        page_name
        , single_page_no + 1
        , CASE WHEN CHARINDEX('|', remainder) > 0 THEN LEFT(remainder, CHARINDEX('|', remainder) - 1) ELSE remainder END
        , CASE WHEN CHARINDEX('|', remainder) > 0 THEN RIGHT(remainder, LEN(remainder) - CHARINDEX('|', remainder)) END
        , row_id
    FROM cteParts
    WHERE LEN(remainder) > 0
)

SELECT
    row_id
    , page_name
    , [1]
    , [2]
    , [3]
    , [4]
    , [5]
    , [6]
    , [7]
    , [8]
    , [9]
    , [10]
FROM
    (
        SELECT
            row_id
            , page_name
            , single_page_no
            , single_page_name
        FROM cteParts
    ) Q
    PIVOT
    (
        MAX(single_page_name)
        FOR single_page_no IN ([1], [2], [3], [4], [5], [6], [7], [8], [9], [10])
    ) P

В противном случае вам потребуется использовать динамический SQL для достижения чего-то подобного.

Вот пример решения с динамическим SQL:

-- Temporary table is just for example purposes.
CREATE TABLE #tbl (page_name varchar(255))
;

INSERT INTO #tbl
VALUES
('it-bae|')
, ('it-bae|content|advisor in newsletter')
, ('it-bae|content|area products|showcase products fideuram|fideuram fonditalia dynamic')
, ('it-bae|content|events|events|events|webinars|new')
;

DECLARE @maxStrings int =
(
    SELECT MAX(LEN(page_name) - LEN(REPLACE(page_name, '|', ''))) + 1
    FROM #tbl
)
;

DECLARE @headers varchar(1000)
;

WITH cteHeaders
AS
(
    SELECT 1 Header
    UNION ALL
    SELECT Header + 1
    FROM cteHeaders
    WHERE Header + 1 <= @maxStrings
)

SELECT DISTINCT @headers = STUFF((SELECT ', [' + CAST(Header AS varchar(3)) + ']' FROM cteHeaders FOR XML PATH('')), 1, 2, '')
FROM cteHeaders
;

DECLARE @sql varchar(8000) =
'
WITH cteParts
AS
(
    SELECT
        page_name
        , 1 single_page_no
        , CASE WHEN CHARINDEX(''|'', page_name) = 0 THEN page_name ELSE LEFT(page_name, CHARINDEX(''|'', page_name) - 1) END single_page_name
        , CASE WHEN CHARINDEX(''|'', page_name) > 0 THEN RIGHT(page_name, LEN(page_name) - CHARINDEX(''|'', page_name)) END remainder
        , ROW_NUMBER() OVER (ORDER BY page_name) row_id
    FROM #tbl

    UNION ALL

    SELECT
        page_name
        , single_page_no + 1
        , CASE WHEN CHARINDEX(''|'', remainder) > 0 THEN LEFT(remainder, CHARINDEX(''|'', remainder) - 1) ELSE remainder END
        , CASE WHEN CHARINDEX(''|'', remainder) > 0 THEN RIGHT(remainder, LEN(remainder) - CHARINDEX(''|'', remainder)) END
        , row_id
    FROM cteParts
    WHERE LEN(remainder) > 0
)

SELECT
    row_id
    , page_name
    , ' + @headers + '
FROM
    (
        SELECT
            row_id
            , page_name
            , single_page_no
            , single_page_name
        FROM cteParts
    ) Q
    PIVOT
    (
        MAX(single_page_name)
        FOR single_page_no IN (' + @headers + ')
    ) P
'

EXEC (@sql)

DROP TABLE #tbl
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...