3

Ich kann nicht herausfinden, wie ein CTE geschrieben wird, das die Kosten von einer Transaktionstabelle in eine Self-Join-Tabelle aufteilt Ebene der Hierarchie. Ich habe ein sehr einfaches Beispiel zur Veranschaulichung des Problems zusammengestellt. Hier sind die DDL und Insert-Skripte, so dass Sie das Problem reproduzieren können, sollten Sie mich so freundlich sein, um zu helfen:SQL Server-CTE zum Aggregieren von Kosten auf jeder Hierarchieebene

CREATE TABLE [Items](
    [ItemId] [int] IDENTITY(1,1) NOT NULL, 
    [ParentId] [int] NULL, 
    [ItemName] [varchar](100) NOT NULL, 
CONSTRAINT [PK_Items] PRIMARY KEY CLUSTERED 
(
    [ItemId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 
CREATE TABLE [Transactions](
    [TransactionId] [int] IDENTITY(1,1) NOT NULL, 
    [ItemId] [int] NOT NULL, 
    [Amount] [money] NOT NULL, 
CONSTRAINT [PK_Transactions] PRIMARY KEY CLUSTERED 
(
    [TransactionId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 
GO 
SET IDENTITY_INSERT [Items] ON 
GO 
INSERT [Items] ([ItemId], [ParentId], [ItemName]) VALUES (1, NULL, N'Warehouse') 
GO 
INSERT [Items] ([ItemId], [ParentId], [ItemName]) VALUES (3, 1, N'Bin 1') 
GO 
INSERT [Items] ([ItemId], [ParentId], [ItemName]) VALUES (4, 1, N'Bin 2') 
GO 
INSERT [Items] ([ItemId], [ParentId], [ItemName]) VALUES (5, 3, N'Item 1.1') 
GO 
INSERT [Items] ([ItemId], [ParentId], [ItemName]) VALUES (6, 3, N'Item 1.2') 
GO 
INSERT [Items] ([ItemId], [ParentId], [ItemName]) VALUES (7, 4, N'Item 2.1') 
GO 
INSERT [Items] ([ItemId], [ParentId], [ItemName]) VALUES (8, 4, N'Item 2.2') 
GO 
SET IDENTITY_INSERT [Items] OFF 
GO 
SET IDENTITY_INSERT [Transactions] ON 
GO 
INSERT [Transactions] ([TransactionId], [ItemId], [Amount]) VALUES (1, 5, 10.0000) 
GO 
INSERT [Transactions] ([TransactionId], [ItemId], [Amount]) VALUES (2, 5, 10.0000) 
GO 
INSERT [Transactions] ([TransactionId], [ItemId], [Amount]) VALUES (3, 6, 10.0000) 
GO 
INSERT [Transactions] ([TransactionId], [ItemId], [Amount]) VALUES (4, 6, 10.0000) 
GO 
INSERT [Transactions] ([TransactionId], [ItemId], [Amount]) VALUES (5, 4, 10.0000) 
GO 
INSERT [Transactions] ([TransactionId], [ItemId], [Amount]) VALUES (6, 7, 10.0000) 
GO 
INSERT [Transactions] ([TransactionId], [ItemId], [Amount]) VALUES (7, 7, 10.0000) 
GO 
INSERT [Transactions] ([TransactionId], [ItemId], [Amount]) VALUES (8, 8, 10.0000) 
GO 
INSERT [Transactions] ([TransactionId], [ItemId], [Amount]) VALUES (9, 8, 10.0000) 
GO 
SET IDENTITY_INSERT [Transactions] OFF 
GO 
ALTER TABLE [Items] WITH CHECK ADD CONSTRAINT [FK_Items_Items] FOREIGN KEY([ParentId]) 
REFERENCES [Items] ([ItemId]) 
GO 
ALTER TABLE [Items] CHECK CONSTRAINT [FK_Items_Items] 
GO 
ALTER TABLE [Transactions] WITH CHECK ADD CONSTRAINT [FK_Transactions_Items] FOREIGN KEY([ItemId]) 
REFERENCES [Items] ([ItemId]) 
GO 
ALTER TABLE [Transactions] CHECK CONSTRAINT [FK_Transactions_Items] 
GO 

Hier ist der CTE, die ich auf gearbeitet:

With cteAggregateCost 
as 
( 
    select i.itemId, i.ParentId, t.Amount 
    from Items i join Transactions t on i.ItemId = t.ItemId 
union all 
    select i.itemId, i.ParentId, t.Amount 
    from Items i join cteAggregateCost c on i.ItemId = c.ParentId 
    join Transactions t on i.ItemId = t.ItemId 
) 
select i.ParentId, i.ItemId, i.ItemName, sum(Amount) As AggregateCost 
from Items i left join cteAggregateCost c on i.ItemId = c.ItemId 
group by i.ParentId, i.ItemId, i.ItemName 

Dies ist das Ergebnis bin ich immer:

Bad Result

Und das ist das Ergebnis, das ich hoffe, bin zu bekommen:

Hoped-for Result

Wie Sie alle Zeilen mit Ausnahme der ersten beiden sehen können, arbeiten, dass ihre enthaltenen Elemente keine Kosten für den Container haben, nur.

Vielen Dank für jede mögliche Richtung, die Sie anbieten können!

+0

Woher kommen diese Werte? 90 und 40? Für diese Kombination von Daten '(itemid = 1 und parentid = null)' und '(itemid = 3, parentid = 1)'. In dem von Ihnen bereitgestellten Beispiel gibt es keine übereinstimmenden Werte für diese Datensätze in der Tabelle "Transaction". –

+0

@Rigerta 'summe' die Kinder der' ItemId'. 'ItemId' 1 ist das Elternteil für' ItemId' 3 und 4, die Summen von 40 und 50 haben, was 'Summe' von bis zu 90 ergibt.' ItemId'3's Gesamtmenge von 40 kommt von der 'Summe' der Kinder 5 und 6 von 20 Stück. 'ItemId' 4 ist 50, weil Sie beide untergeordneten Elemente (' ItemId' 7 und 8) sowie die einzelne Transaktion für 'ItemId' 4 selbst einschließen müssen. – iamdave

Antwort

2

könnten Sie recursive CTE verwenden wie diese

;WITH temp AS 
(
    SELECT i.*, sum(isnull(t.Amount,0)) AS Amount 
    FROM @Items i 
    LEFT JOIN @Transactions t ON t.ItemId = i.ItemId 
    GROUP BY i.ItemId, i.ParentId, i.ItemName 
) 
,cteAggregateCost 
as 
( 
    select i.ItemId, i.ItemId AS RootId, i.Amount 
    from temp i  
union all 
    select i.ItemId, c.RootId, i.Amount 
    from cteAggregateCost c 
    INNER JOIN temp i ON i.ParentId = c.ItemId 
) 
select i.*, ca.TotalAmount 
from @Items i 
CROSS APPLY 
(
    SELECT Sum(cac.Amount) AS TotalAmount 
    FROM cteAggregateCost cac WHERE i.ItemId = cac.RootId 
) ca 
OPTION (MAXRECURSION 0) 

Demo Link: http://rextester.com/XMK96314

1

Danke, TRIV. Deine Antwort ist brilliant! Allerdings habe ich eine einfachere Antwort von Xi Jin on the SQL Server forum gefunden. Hier ist seine Lösung:

With cteAggregateCost 
    as 
    ( 
     select i.itemId as rootid,i.itemid, i.ParentId 
     from Items i 
     union all 
     select rootid, i.itemId, i.ParentId 
     from Items i join cteAggregateCost c on i.ParentId = c.ItemId 
    ) 

    select a.parentid, a.ItemId , a.ItemName , sum(t.Amount) As AggregateCost 
    from items a 
    left join cteAggregateCost i on a.itemid = i.rootid 
    left join Transactions t on i.ItemId = t.ItemId 
    group by a.parentid, a.ItemId, a.ItemName 

Beiden Lösungen der gleichen korrekten Ergebnisse, die ich suchte bei der Prüfung gegen einen viel größeren Datensatz mit vielen Schichten der hierarchischen Beziehungen. Für mich ist Xi Lins Antwort leichter zu verstehen. Ich konnte einfach nicht herausfinden, wie man die rootID-Technik hinzufügt, die die Werte der Artikel bewahrt, die keine eigenen Kosten hatten und nur Kosten von untergeordneten Elementen hatten.

+0

Der Zweck der Verwendung von RootId ist, alle untergeordneten Knoten von * jedem * Knoten in Ihrer Tabelle zu übernehmen. Es wurde durch rekursive CTE gemacht und eine Sache übrig ist 'left join' zu' transactions' um zu garantieren, dass alle Elemente in der linken Tabelle existieren. Ihre erste Abfrage ist nicht korrekt, da die interne Verknüpfung mit der Transaktionstabelle besteht. Und "rekursive CTE" erlaubt nicht die Verwendung von 'left join', also müssen Sie es nach außen verschieben. – TriV

+0

Danke TriV. Es macht jetzt vollkommen Sinn, dass ich die endgültige Antwort sehe. Ich schätze deine Erklärung sehr. – Karin

+0

Gut zu helfen .... – TriV

Verwandte Themen