Как создать одну строку в SQL на основе иерархических данных - CTE и XML? - PullRequest
0 голосов
/ 19 сентября 2019

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

Один номер детали может иметь несколько отделов, в каждом отделе может быть несколько стоек и т. Д. (Полки, а затем ряды).

Мне нужно объединить эту информацию, чтобы разместить набор данных на этикетке (включенодна строка).

Чтобы получить набор данных, мой запрос выглядит следующим образом.

select d.Department_Name, r.Rack_Name, s.Shelf_Name, rw.Row_Name from Part_Locator l
join Part_Numbers p
  on l.Part_Id=p.Part_Id
join PL_Departments d
  on l.Department_Id=d.Department_Id
join PL_Racks r
  on l.Rack_Id=r.Rack_Id
join PL_Shelfs s
  on l.Shelf_Id=s.Shelf_Id
join PL_Rows rw
  on l.Row_Id=rw.Row_Id
where p.Part_Number='1BODY000997'

Результаты выглядят следующим образом:

The Results are shown below

Я могу сделать вложенный взгляд, чтобы построить строку, но я считаю, что комбинация рекурсивных CTE, Stuff и XML могла бы быть такой же для меня.Пока я могу найти много примеров, но ни одного, который я могу понять достаточно хорошо, чтобы заставить их работать вместе.

Желаемый результат следующий:

Сборка - Стойка IA-10 - ПолкаA - Строка 1,2,3,4,5,6 - Полка B - Строка 1,2,3,4,5,6 - Полка C - Строка 1,2,3,4,5,6

Я не против изменения этого формата, если он читается логически.Также обратите внимание, что там может быть несколько отделов.

Любая помощь, которая поможет мне двигаться в правильном направлении, будет отличнойЗаранее спасибо!

Мне удалось заставить это работать с помощью курсора.Я также создал представление для удаления объединений из запроса, которое гораздо легче читать.

    DECLARE
@partnumber varchar(50)='1BODY000997', @dept varchar(50), @rack varchar(50), @shelf varchar(50), @row varchar(50), @text varchar(500)='';

 DECLARE department_cursor CURSOR FOR
 SELECT DISTINCT Department_Name FROM Part_Locations WHERE Part_Number=@partnumber
 OPEN department_cursor;
 FETCH NEXT FROM department_cursor INTO @dept; 
 WHILE @@FETCH_STATUS = 0
 BEGIN 
 SET @text=@text+CAST(@dept as varchar(50))+' - ';
    DECLARE rack_cursor CURSOR FOR
    SELECT DISTINCT Rack_Name FROM Part_Locations WHERE Part_Number=@partnumber AND Department_Name=@dept
    OPEN rack_cursor;
    FETCH NEXT FROM rack_cursor INTO @rack
    WHILE @@FETCH_STATUS = 0
    BEGIN   
    SET @text=@text+'Rack:'+CAST(@rack as varchar(50))+' - ';
        DECLARE shelf_cursor CURSOR FOR
        SELECT DISTINCT Shelf_Name FROM Part_Locations WHERE Part_Number=@partnumber AND Department_Name=@dept AND Rack_Name=@rack
        OPEN shelf_cursor;
        FETCH NEXT FROM shelf_cursor INTO @shelf
        WHILE @@FETCH_STATUS = 0
        BEGIN       
        SET @text=@text+'Shelf:'+CAST(@shelf as varchar(50))+' (Row ';      
            SET @text=@text+(SELECT abc=STUFF((SELECT DISTINCT ',' + Row_Name FROM Part_Locations WHERE Part_Number=@partnumber AND Department_Name=@dept AND Rack_Name=@rack AND Shelf_Name=@shelf FOR XML PATH ('')), 1, 1, ''))          
            SET @text=@text+') ';  
        FETCH NEXT FROM shelf_cursor INTO @shelf
        END;
        CLOSE shelf_cursor;
        DEALLOCATE shelf_cursor;   
    FETCH NEXT FROM rack_cursor INTO @rack
    END;
    CLOSE rack_cursor;
    DEALLOCATE rack_cursor;
  FETCH NEXT FROM department_cursor INTO @dept;
 END
 CLOSE department_cursor;
 DEALLOCATE department_cursor;
 PRINT (@text)

это дает мне хороший результат:

Сборка - Стойка: IA-10 - Полка: A (Строка 1,2,3,4,5,6) Полка: B (Строка 1,2,3,4,5,6) Полка: C (Строка 1,2,3,4,5,6)

1 Ответ

0 голосов
/ 22 сентября 2019

Как уже упоминалось, вы могли бы сделать cte, или вы могли бы создать бегущее поле (аналогично итоговой сумме) или путь XML.На самом деле, забыть поле для бега, слишком сложно.Ниже приведены два примера, один с xml, за которым следует cte.Я использовал в той ссылке, которую я отправил ранее на путь XML.Из-за сложности, мне нужно было сделать два XML-пути (сначала один во временную таблицу)

Обратите внимание, что я создал таблицу (ITEMS) и поместил ваши результаты в нее ...

SELECT DISTINCT i.Dept, i.rack, i.shelf,
    stuff(  (   select ',' + i2.rowname
                from ITEMS i2   
            where i2.Dept = i.Dept and
            i2.rack = i.rack and
            i2.shelf =i.shelf
            FOR XML PATH('')),1,1,'') as RownumbersCommaSeparated
INTO #T1
FROM ITEMS i

SELECT 
    stuff(  (   select ' ' +  T.Dept + ' Rack ' + T.rack + ' Shelf ' + T.shelf + ' Rows ' + T.RownumbersCommaSeparated
                from #T1 T
            FOR XML PATH('')),1,1,'' )   as DesiredOutput

Общее табличное выражение немного длиннее!Есть две рекурсии ...

;

WITH
    CTE0 AS
        (   
            SELECT i.*, 
            ROW_NUMBER() OVER(ORDER BY i.Dept,i.rack,i.shelf, i.rowname) recordnumber,
            RANK() OVER (  ORDER BY i.Dept,i.rack,i.shelf) Parent,
            RANK() OVER (PARTITION BY i.Dept,i.rack,i.shelf ORDER BY i.Dept,i.rack,i.shelf,i.rowname) rowidForeachParent
            FROM ITEMS i

        ),
    CTE1 AS
        (   SELECT 
                 i.Parent,
                    i.Dept + ' Rack ' + 
                    i.rack + ' Shelf ' + 
                    i.shelf + ' Rows ' as CoreData,
                    MAX(i.rowidforeachparent) maxrowidforeachparent
                     FROM cte0 i
                     group by i.Parent, i.Dept, i.rack, i.shelf
        ),
    CTE2 AS
        (
            SELECT
                i1.Parent,
                i1.maxrowidforeachparent,
                  CAST( i1.CoreData +  ',' + i0.rowname  AS varchar(MAX) )  as outputX,
                   i0.rowidForeachParent
            FROM cte1 i1
            join cte0 i0 on 
                i0.Parent = i1.Parent AND
                I0.rowidForeachParent = 1

            UNION ALL

            SELECT
            i1.Parent,
            i1.maxrowidforeachparent,
                i1.outputX +  ',' + i0.rowname, 
                i0.rowidForeachParent
            FROM cte2 i1
            join cte0 i0 on 
                i0.Parent = i1.Parent AND
                I0.rowidForeachParent = I1.rowidForeachParent+1
        ) ,

    CTE3 AS
        (

            SELECT i.outputX ,
            ROW_NUMBER() OVER (ORDER BY Parent) ParentId
             FROM  cte2 i 
            where i.rowidForeachParent = i.maxrowidforeachparent
        ),

    CTE4 AS
        (
            SELECT outputX, i3.ParentId FROM CTE3 i3 
            where i3.ParentId = 1
            UNION ALL
            SELECT i4.outputX + ' ' + i3.outputX, i3.ParentId FROM CTE3 i3 
            join CTE4 i4 on i3.ParentId = i4.ParentId + 1

        )

        select top 1 * from cte4 order by ParentId desc
...