Как можно создать новую таблицу из другой после Calculate Some Field - PullRequest
0 голосов
/ 02 ноября 2019

У меня есть таблица с именем WorkSpace с 4 столбцами.

Каждая запись в этой таблице ссылается на свою родительскую запись через столбец Parent_id.

Вы должны продолжать этот цикл доВы достигли основного родителя, и все эти шаги указаны в столбце View_Parent_id.

Идентификатор основного родительского номера определяется как 1.

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

Столбец View_Parent_id в конечном итоге представляет собой 15 значений, поэтому нам нужно 15 столбцов в новой таблице (Level_1 ... Level_15).

Я использовал цикл в своем запросе, но он был очень медленным.

WorkSpace таблица содержит 1,5 миллиона строк.

WorkSpace таблица:

+-----+-------+-----------+------------------+
| W_ID| Title | Parent_id | View_Parent_id   |
+-----+-------+-----------+------------------+
| 1   |   AAA |    null   | null             |
| 2   |   BV  |    1      | 1                |
| 3   |   CX  |    2      | 1+2              |
| 4   |   DSO |    2      | 1+2              |
| 5   |   ER  |    3      | 1+2+3            |
| 6   |   ER  |    5      | 1+2+3+5          |
| ... |  ...  |    ...    | ...              |
| 1000|   MNV |    1      | 1                |
| 1001|   SF  |    1000   | 1+1000           |
| 1002|   EDD |    1000   | 1+1000           |
| 1003|   YSG |    1001   | 1+1000+1001      |
| 1004|   RPO |    1003   | 1+1000+1001+1003 |
+-----+-------+-----------+------------------+

NEW_WorkSpace таблица:

+-----+-------+-----------+---------+---------+---------+-----+----------+
| ID  |  W_id | Parent_id | Level_1 | Level_2 | Level_3 | ... | Level_15 |
+-----+-------+-----------+---------+-------- +---------+-----+----------+
| 100 |   1   |    null   | AAA     |         |         | ... |          |
| 101 |   2   |    1      | AAA     |  BV     |         | ... |          |
| 102 |   3   |    2      | AAA     |  BV     |         | ... |          |
| 103 |   4   |    2      | AAA     |  BV     |  CX     | ... |          |
| 104 |   5   |    3      | AAA     |  BV     |  CX     | ... |          |
| ... |   ... |    ...    | ...     |  ...    |  ...    | ... |  ...     |
+-----+-------+-----------+---------+---------+---------+-----+----------+

Мой код:

BEGIN    
    DECLARE @W_ID decimal(20, 0);
    DECLARE @parent_id decimal(20, 0);

    DECLARE @Level1 nvarchar(MAX);
    DECLARE @Level2 nvarchar(MAX);
    DECLARE @Level3 nvarchar(MAX);
    DECLARE @Level4 nvarchar(MAX);
    DECLARE @Level5 nvarchar(MAX);
    DECLARE @Level6 nvarchar(MAX);
    DECLARE @Level7 nvarchar(MAX);
    DECLARE @Level8 nvarchar(MAX);
    DECLARE @Level9 nvarchar(MAX);
    DECLARE @Level10 nvarchar(MAX);
    DECLARE @Level11 nvarchar(MAX);
    DECLARE @Level12 nvarchar(MAX);
    DECLARE @Level13 nvarchar(MAX);
    DECLARE @Level14 nvarchar(MAX);
    DECLARE @Level15 nvarchar(MAX);
    DECLARE @titles_tmp nvarchar(MAX);

    DECLARE @cont_spilit_tittle int;
    DECLARE @parent_titles_tmp nvarchar(MAX);
    DECLARE @cont_tmp int;
    DECLARE @cont int;

    SELECT @cont = COUNT(*) FROM dbo.WorkSpace ;
    SET @cont_tmp = 0;

    WHILE (@cont_tmp < @cont)
    BEGIN  
        SET @W_ID = (SELECT dbo.WorkSpace.W_ID FROM dbo.WorkSpace  
                     ORDER BY W_ID ASC OFFSET @cont_tmp ROWS FETCH NEXT 1 ROWS ONLY)
        SET @parent_id = (SELECT dbo.WorkSpace.parent_id FROM dbo.WorkSpace 
                          ORDER BY W_ID ASC OFFSET @cont_tmp ROWS FETCH NEXT 1 ROWS ONLY)
        SET @titles_tmp = (SELECT dbo.WorkSpace.title FROM dbo.WorkSpace 
                           ORDER BY W_ID ASC OFFSET @cont_tmp ROWS FETCH NEXT 1 ROWS ONLY)
        SET @parent_titles_tmp = (SELECT dbo.WorkSpace.parent_titles FROM dbo.WorkSpace 
                                  ORDER BY W_ID ASC OFFSET @cont_tmp ROWS FETCH NEXT 1 ROWS ONLY)

        IF OBJECT_ID('tempdb..#MyTempTable') IS NOT NULL 
             DROP TABLE #MyTempTable

        SELECT IDENTITY(INT, 1, 1) AS 'RowID', * 
        INTO #MyTempTable 
        FROM StringSplitXML(@parent_titles_tmp, '+')

        INSERT INTO #MyTempTable 
        VALUES (@titles_tmp)

        SET @cont_spilit_tittle = (SELECT COUNT(*) FROM #MyTempTable)

        IF(@cont_spilit_tittle < 0)
            SET @cont_spilit_tittle = 1

        WHILE (@cont_spilit_tittle < 15)
        BEGIN
            INSERT INTO #MyTempTable VALUES ('')
            SET @cont_spilit_tittle = CAST(@cont_spilit_tittle AS INT) + 1
        END

        SET @Level1 = (SELECT Value FROM #MyTempTable 
                       ORDER BY RowID ASC OFFSET 0 ROWS FETCH NEXT 1 ROWS ONLY)
        SET @Level2 = (SELECT Value FROM #MyTempTable 
                       ORDER BY RowID ASC OFFSET 1 ROWS FETCH NEXT 1 ROWS ONLY)
        SET @Level3 = (SELECT Value FROM #MyTempTable 
                       ORDER BY RowID ASC OFFSET 2 ROWS FETCH NEXT 1 ROWS ONLY)
        SET @Level4 = (SELECT Value FROM #MyTempTable 
                       ORDER BY RowID ASC OFFSET 3 ROWS FETCH NEXT 1 ROWS ONLY)
        SET @Level5 = (SELECT Value FROM #MyTempTable 
                       ORDER BY RowID ASC OFFSET 4 ROWS FETCH NEXT 1 ROWS ONLY)
        SET @Level6 = (SELECT Value FROM #MyTempTable 
                       ORDER BY RowID ASC OFFSET 5 ROWS FETCH NEXT 1 ROWS ONLY)
        SET @Level7 = (SELECT Value FROM #MyTempTable 
                       ORDER BY RowID ASC OFFSET 6 ROWS FETCH NEXT 1 ROWS ONLY)
        SET @Level8 = (SELECT Value FROM #MyTempTable 
                       ORDER BY RowID ASC OFFSET 7 ROWS FETCH NEXT 1 ROWS ONLY)
        SET @Level9 = (SELECT Value FROM #MyTempTable 
                       ORDER BY RowID ASC OFFSET 8 ROWS FETCH NEXT 1 ROWS ONLY)
        SET @Level10 = (SELECT Value FROM #MyTempTable 
                        ORDER BY RowID ASC OFFSET 9 ROWS FETCH NEXT 1 ROWS ONLY)
        SET @Level11 = (SELECT Value FROM #MyTempTable 
                        ORDER BY RowID ASC OFFSET 10 ROWS FETCH NEXT 1 ROWS ONLY)
        SET @Level12 = (SELECT Value FROM #MyTempTable 
                        ORDER BY RowID ASC OFFSET 11 ROWS FETCH NEXT 1 ROWS ONLY)
        SET @Level13 = (SELECT Value FROM #MyTempTable 
                        ORDER BY RowID ASC OFFSET 12 ROWS FETCH NEXT 1 ROWS ONLY)
        SET @Level14 = (SELECT Value FROM #MyTempTable 
                        ORDER BY RowID ASC OFFSET 13 ROWS FETCH NEXT 1 ROWS ONLY)
        SET @Level15 = (SELECT Value FROM #MyTempTable 
                        ORDER BY RowID ASC OFFSET 14 ROWS FETCH NEXT 1 ROWS ONLY)


        INSERT INTO [].[dbo].[NEW_WorkSpace]
           ([W_ID], [parent_id],
            [Level1], [Level2], [Level3], [Level4], [Level5],
            [Level6], [Level7], [Level8], [Level9], [Level10],
            [Level11], [Level12], [Level13], [Level14], [Level15])
        VALUES (@W_ID, @parent_id,
                @Level1, @Level2, @Level3, @Level4, @Level5,
                @Level6, @Level7, @Level8, @Level9, @Level10,
                @Level11, @Level12, @Level13, @Level14, @Level15) 

        SET @cont_tmp = CAST(@cont_tmp AS INT) + 1
    END  

    RETURN
END

Спасибо за помощь.

Ответы [ 2 ]

1 голос
/ 02 ноября 2019

Я бы подошел к этому, используя строковые операции на view_parent_id. Затем агрегирование окончательного результата:

with cte as (
      select w_id, parent_id, view_parent_id,
             0 as lev, convert(varchar(max), concat(view_parent_id, '+', w_id, '+')) as parents 
      from t
      union all
      select w_id, parent_id, view_parent_id,
             1 + lev, 
             convert(int, left(parents, charindex('+', parents) - 1)),
             stuff(parents, 1, charindex('+', parents), '')
      from cte
      where parents <> ''
     )
select w_id, parent_id, view_parent_id,
       max(case when lev = 1 then parent_title end) as title_1,
       max(case when lev = 2 then parent_title end) as title_2,
       max(case when lev = 3 then parent_title end) as title_3,
       max(case when lev = 4 then parent_title end) as title_4,
       max(case when lev = 5 then parent_title end) as title_5
from (select cte.*, t.title as parent_title, count(*) over (partition by cte.w_id) as cnt
      from cte join
           t
           on t.w_id = cte.parent
      where lev > 0
     ) cte
group by w_id, parent_id, view_parent_id;

Здесь - это скрипта db <>.

Что касается обработки. Вероятно, самой дорогой частью запроса является агрегация после рекурсивного CTE. Рекурсивная часть не выполняет никакого объединения, поэтому оно должно быть достаточно быстрым (строковые операции могут быть медленными).

Соединение для получения метки использует правильные типы, поэтому можно использовать индекс для w_id.

1 голос
/ 02 ноября 2019

Ну, это уродливо и предполагает, что у вас максимум 15 уровней, и ваши ожидаемые результаты неверны;как у меня W_ID 4 имеет значение 'DSO' для Level_3, но ваши ожидаемые результаты имеют CX, хотя эта строка никак не связана с CX.

USE Sandbox;
GO

CREATE TABLE dbo.YourTable (W_ID int NOT NULL,
                            Title varchar(3) NOT NULL,
                            Parent_id int NULL,
                            View_Parent_id varchar(100) NULL);
GO

INSERT INTO dbo.YourTable
VALUES (1,'AAA',NULL,NULL),
       (2,'BV',1,'1'),
       (3,'CX',2,'1+2'),
       (4,'DSO',2,'1+2'),
       (5,'ER',3,'1+2+3'),
       (6,'ER',5,'1+2+3+5');

GO

SELECT *
FROM dbo.YourTable;
GO
WITH rCTE AS(
    SELECT (YT.W_ID + 99) AS ID,
           YT.W_ID      ,
           YT.Parent_id,
           Title AS level_1,
           CONVERT(varchar(3),NULL) AS Level_2,
           CONVERT(varchar(3),NULL) AS Level_3,
           CONVERT(varchar(3),NULL) AS Level_4,
           CONVERT(varchar(3),NULL) AS Level_5,
           CONVERT(varchar(3),NULL) AS Level_6,
           CONVERT(varchar(3),NULL) AS Level_7,
           CONVERT(varchar(3),NULL) AS Level_8,
           CONVERT(varchar(3),NULL) AS Level_9,
           CONVERT(varchar(3),NULL) AS Level_10,
           CONVERT(varchar(3),NULL) AS Level_11,
           CONVERT(varchar(3),NULL) AS Level_12,
           CONVERT(varchar(3),NULL) AS Level_13,
           CONVERT(varchar(3),NULL) AS Level_14,
           CONVERT(varchar(3),NULL) AS Level_15,
           1 AS [Level]
    FROM dbo.YourTable YT
    WHERE YT.Parent_id IS NULL
    UNION ALL
    SELECT (YT.W_ID + 99) AS ID,
           YT.W_ID,
           YT.Parent_id,
           r.Level_1,
           CASE r.[Level] WHEN 1 THEN YT.Title ELSE r.Level_2 END AS Level_2,
           CASE r.[Level] WHEN 2 THEN YT.Title ELSE r.Level_3 END AS Level_3,
           CASE r.[Level] WHEN 3 THEN YT.Title ELSE r.Level_4 END AS Level_4,
           CASE r.[Level] WHEN 4 THEN YT.Title ELSE r.Level_5 END AS Level_5,
           CASE r.[Level] WHEN 5 THEN YT.Title ELSE r.Level_6 END AS Level_6,
           CASE r.[Level] WHEN 6 THEN YT.Title ELSE r.Level_7 END AS Level_7,
           CASE r.[Level] WHEN 7 THEN YT.Title ELSE r.Level_8 END AS Level_8,
           CASE r.[Level] WHEN 8 THEN YT.Title ELSE r.Level_9 END AS Level_9,
           CASE r.[Level] WHEN 9 THEN YT.Title ELSE r.Level_10 END AS Level_10,
           CASE r.[Level] WHEN 10 THEN YT.Title ELSE r.Level_11 END AS Level_11,
           CASE r.[Level] WHEN 11 THEN YT.Title ELSE r.Level_12 END AS Level_12,
           CASE r.[Level] WHEN 12 THEN YT.Title ELSE r.Level_13 END AS Level_13,
           CASE r.[Level] WHEN 13 THEN YT.Title ELSE r.Level_14 END AS Level_14,
           CASE r.[Level] WHEN 14 THEN YT.Title ELSE r.Level_15 END AS Level_15,
           r.[Level] + 1 AS [Level]
    FROM dbo.YourTable YT
         JOIN rCTe r ON YT.Parent_id = r.W_ID)
SELECT r.ID,
       r.W_ID,
       r.Parent_id,
       r.Level_1,
       r.Level_2,
       r.Level_3,
       r.Level_4,
       r.Level_5,
       r.Level_6,
       r.Level_7,
       r.Level_8,
       r.Level_9,
       r.Level_10,
       r.Level_11,
       r.Level_12,
       r.Level_13,
       r.Level_14,
       r.Level_15
FROM rCTE r;
GO

DROP TABLE dbo.YourTable;

дБ <> скрипка

...