Транспонировать строки в столбцы по имени столбца разделителя и заголовку столбца - PullRequest
1 голос
/ 22 июня 2019

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

SELECT ID, Path, Path_Values 
FROM TABLE1

Выход:

ID  | Path                      | Path_Data_Values
----+---------------------------+----------------------------------
1   | Root                      | Org
2   | Root / Hemisphere         | Org / North Hemisphere
3   | Root / Hemisphere / State | Org / North Hemisphere / Texas
4   | Root / State              | Org / Texas

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

Визуальный пример этой конечной таблицы (требуемый результат):

ID | Root | Hemisphere           | State | Other_1 | Other_2 | Other_3
---+------+----------------------+-------+---------+---------+----------
 1 | Org  | NULL                 | NULL  | NULL    | NULL    | NULL
 2 | Org  | Northern Hemisphere  | NULL  | NULL    | NULL    | NULL
 3 | Org  | Northern Hemisphere  | Texas | NULL    | NULL    | NULL
 4 | Org  | NULL                 | Texas | NULL    | NULL    | NULL

Ответы [ 3 ]

4 голосов
/ 22 июня 2019

Это дает вам большую часть пути туда.Однако, поскольку определение ваших данных меняется от строки к строке, то нет способа получить данные в нужном вам порядке, если вы хотите сделать это динамически.(Например, состояние должно быть в положении 2 или 3, как это показано в обоих случаях. Без знания ваших данных или таблицы поиска это невозможно. Это динамическое решение, однако оно показывает вам код, который он генерирует дляполучите результаты, к которым вы стремитесь, если вы хотите вручную закодировать все свои позиции.

Здесь также используется DelimitedSplit8k_LEAD, поскольку STRING_SPLIT не предоставляет порядковый номерэлемента в списке с разделителями, что делает его здесь бесполезным.

CREATE TABLE dbo.YourTable (ID int,
                            [Path] varchar(8000),
                            Path_Data_Values varchar(8000));
INSERT INTO dbo.YourTable(ID, [Path], Path_Data_Values)
VALUES (1,'Root','Org'),
       (2,'Root / Hemisphere','Org / North Hemisphere'),
       (3,'Root / Hemisphere / State','Org / North Hemisphere / Texas'),
       (4,'Root / State','Org / Texas');

GO

DECLARE @SQL nvarchar(MAX);

SET @SQL = N'SELECT YT.ID,' + NCHAR(13) + NCHAR(10) +
           STUFF((SELECT N',' + NCHAR(13) + NCHAR(10) + 
                         N'       MAX(CASE P.Item WHEN ' + QUOTENAME(P.Item,'''') + N' THEN PDV.Item END) AS ' + QUOTENAME(P.Item)
                  FROM dbo.YourTable YT
                       CROSS APPLY (VALUES(REPLACE(YT.[Path],' / ','|')))V([Path])
                       CROSS APPLY dbo.DelimitedSplit8K_LEAD(V.[Path],'|') P
                  GROUP BY P.Item
                  FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,3,N'') + NCHAR(13) + NCHAR(10) +
           N'FROM dbo.YourTable YT' + NCHAR(13) + NCHAR(10) +
           N'     CROSS APPLY (VALUES(REPLACE(YT.[Path],'' / '',''|''),REPLACE(YT.Path_Data_Values,'' / '',''|'')))V([Path],Path_Data_Values)' + NCHAR(13) + NCHAR(10) +
           N'     CROSS APPLY dbo.DelimitedSplit8K_LEAD(V.[Path],''|'') P' + NCHAR(13) + NCHAR(10) +
           N'     CROSS APPLY(SELECT DS.Item FROM dbo.DelimitedSplit8K_LEAD(V.Path_Data_Values,''|'') DS WHERE P.ItemNumber = DS.ItemNumber) PDV' + NCHAR(13) + NCHAR(10) +
           N'GROUP BY YT.ID;';

PRINT @SQL;

EXEC sp_executesql @SQL;
2 голосов
/ 22 июня 2019

Благодаря образцу данных Ларну я смог опубликовать другой способ сделать это.
Этот способ состоит в создании предложения values для выбора из динамического SQL.

-- Get the maximum number of columns
DECLARE @ColumnsCount int = 
(SELECT MAX(LEN(Path_Data_Values) - LEN(REPLACE(Path_Data_Values, ' / ', '  '))) 
FROM YourTAble                               
)

DECLARE @Sql nvarchar(max) = 'SELECT * FROM (VALUES' + 

STUFF(
(
    -- Construct the values clause
    SELECT ',(''' + REPLACE(data, ' / ', ''',''') + ''')'
    FROM 
    (
        -- use Replicate to add the "missing" values to each line
        SELECT Path_Data_Values + REPLICATE(' / ', @ColumnsCount - (LEN(Path_Data_Values) - LEN(REPLACE(Path_Data_Values, ' / ', '  ')))) As data
        FROM YourTAble
    ) x
    FOR XML PATH('')
), 1, 1, '')
+ ') V (' + 
STUFF(
(
    -- Construct the values names
    SELECT ',' + REPLACE(Path, ' / ', ',')
    FROM YourTAble
    WHERE LEN(Path) - LEN(REPLACE(Path, ' / ', '  ')) = @ColumnsCount 
    FOR XML PATH('')
), 1, 1, '')
+ ')';

-- Whenever using dynamic SQL, Print is your best friend
PRINT @Sql

-- this will print, with the sample data provided, the following SQL statement:

-- SELECT * FROM (VALUES('Org','',''),('Org','North Hemisphere',''),('Org','North Hemisphere','Texas'),('Org','Texas','')) V (Root,Hemisphere,State) 

-- unremark once print gets you the desired sql
--EXEC(@Sql)

Результат exec(sql) для данных данного примера будет следующим:

Root    Hemisphere          State
Org     
Org     North Hemisphere    
Org     North Hemisphere    Texas
Org                         Texas   

Если вы хотите null вместо пустых строк, это так же просто, как заменить '' на null в построенномдинамический SQL.

Обратите внимание, что для версии 2017 или выше вы можете упростить это, используя string_agg вместо использованной комбинации stuff и for xml.

Вы можете увидеть живое демо на rextester.

0 голосов
/ 22 июня 2019

Можно ли создать таблицу номеров строк и затем развернуть ее?

Например,

with cte_t
as
(
    Select * From
    (
    values(1,'Root','Org'),(2,'Root / Hemisphere','Org / North Hemisphere'),(3,'Root / Hemisphere / State','Org / North Hemisphere / Texas'),(4,'Root / State','Org / Texas')
    ) as t (ID,ThePath,Path_data_Values)
)

SELECT t1.[Id], [x].[value] as ThePath, x.[rn1], [x2].[value] as Path_data_Values, x2.[rn2] -- t1.ThePath,t1.Path_data_Values
FROM cte_t AS t1
CROSS APPLY (SELECT v1.[value], ROW_NUMBER() OVER (PARTITION BY t1.[Id] ORDER BY t1.[Id]) AS RN1 FROM STRING_SPLIT(t1.ThePath, '/') AS [v1]) AS x
CROSS APPLY (SELECT v2.[value], ROW_NUMBER() OVER (PARTITION BY t1.[Id] ORDER BY t1.[Id]) AS RN2 FROM STRING_SPLIT(t1.Path_data_Values, '/') AS [v2]) AS x2
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...