2013-06-22 3 views
5

Ich habe eine Datenbank, in der Kosteninformationen für eine Organisation nach einer Untergliederung und nach Jahren sortiert gespeichert werden. Die Kostenstruktur beinhaltet eine Eltern-Kind-Beziehung; Die Benutzer können die Kostenwerte auf jeder Ebene in der Struktur angeben. Die einzige Einschränkung besteht darin, dass alle Werte auf höheren Ebenen in der Hierarchie als Summe der untergeordneten Knoten berechnet werden, wenn ein untergeordneter Knoten über einen Wert verfügt. Elternknotenwerte, die sich als Summe der untergeordneten Knoten ergeben, werden nicht in der Datenbank gespeichert.Rekursive Summe in Parent-Child-Hierarchie T-SQL

Ich brauche eine Abfrage, die rekursiv die Werte der Eltern auf ihre Kinder und für Kinder ohne Werte auf Basis berechnen wird Null gesetzt werden (T-SQL, SQL 2008R2)

[SQL Fiddle] MS SQL Server 2008-Schema-Setup:

CREATE TABLE CostStructureNodes (
    Id INT NOT NULL PRIMARY KEY, 
    Name NVARCHAR(250) NOT NULL, 
    ParentNodeId INT, 
    FOREIGN KEY(ParentNodeId) REFERENCES CostStructureNodes(Id) 
); 

CREATE TABLE Years (
    Year INT NOT NULL PRIMARY KEY 
); 

CREATE TABLE CostsPerYear (
    NodeId INT NOT NULL, 
    Year INT NOT NULL, 
    Value DECIMAL(18,6) NOT NULL, 
    PRIMARY KEY(NodeId, Year), 
    FOREIGN KEY(NodeId) REFERENCES CostStructureNodes(Id), 
    FOREIGN KEY(Year) REFERENCES Years(Year) 
); 

INSERT INTO CostStructureNodes VALUES ('1', 'Total Costs', NULL); 
INSERT INTO CostStructureNodes VALUES ('2', 'R&D', 1); 
INSERT INTO CostStructureNodes VALUES ('3', 'Legal', 1); 
INSERT INTO CostStructureNodes VALUES ('4', 'HR', 1); 
INSERT INTO CostStructureNodes VALUES ('5', 'IT', 1); 
INSERT INTO CostStructureNodes VALUES ('6', 'Software', 5); 
INSERT INTO CostStructureNodes VALUES ('7', 'Hardware', 5); 

INSERT INTO Years VALUES (2010); 
INSERT INTO Years VALUES (2011); 
INSERT INTO Years VALUES (2012); 

INSERT INTO CostsPerYear VALUES (1, 2010, 100000); 
INSERT INTO CostsPerYear VALUES (2, 2011, 50000); 
INSERT INTO CostsPerYear VALUES (5, 2011, 20000); 
INSERT INTO CostsPerYear VALUES (6, 2012, 22000); 
INSERT INTO CostsPerYear VALUES (7, 2012, 13000); 
INSERT INTO CostsPerYear VALUES (2, 2012, 76000); 

die Struktur oben und die Probendaten gegeben, das ist, wie die Dinge aussehen würden:

|  NAME | YEAR | VALUE | 
    ------------------------------- 
    | Total Costs | 2010 | 100000 | 
    |   R&D | 2010 |  0 | 
    |   IT | 2010 |  0 | 
    | Software | 2010 |  0 | 
    | Hardware | 2010 |  0 | 
    |   HR | 2010 |  0 | 
    | Total Costs | 2011 | 70000 | 
    |   R&D | 2011 | 50000 | 
    |   IT | 2011 | 20000 | 
    | Software | 2011 |  0 | 
    | Hardware | 2011 |  0 | 
    |   HR | 2011 |  0 | 
    | Total Costs | 2012 | 111000 | 
    |   R&D | 2012 | 76000 | 
    |   IT | 2012 | 35000 | 
    | Software | 2012 | 22000 | 
    | Hardware | 2012 | 13000 | 
    |   HR | 2012 |  0 | 
+0

Können Sie die angeforderte Ausgabe erneut überprüfen. Vorausgesetzt, HR hat id 4 und Sie fügen 75000 für HR 2012 in der letzten Zeile des Beispielschemas ein. Es fällt mir schwer zu verstehen, dass HR in der angeforderten Ausgabe für 2012 einen Wert von 0 hat. – souplex

+0

Ich habe die Beispieldaten eingefügt. – kjv

Antwort

4

Dies sollte ein korrektes Ergebnis geben:

with DirectReport (ParentNodeId, Id, Name, Level, Struc, year) 
as 
(
    -- anchor 
    select a.ParentNodeId, a.Id, a.Name, 0 as Level, cast(':' + cast(a.Id as varchar) + ':' as varchar (100)) as Struc, y.year 
    from CostStructureNodes a, Years y 
    where a.ParentNodeId is null 
    union all 
    -- recursive 
    Select a.ParentNodeId, a.Id, a.Name, Level +1, cast(d.Struc + cast(a.Id as varchar)+ ':' as varchar(100)) as Struc, d.year 
    from CostStructureNodes a 
    join DirectReport d on d.Id = a.ParentNodeId 
) 

Select d.ParentNodeId, d.year, d.Id, d.Name, d.level, d.Struc,-- dd.Struc, 
sum(case when d.Struc = SUBSTRING(dd.Struc, 1, len(d.Struc))then c.Value else 0 end) as TotCost 
from DirectReport d 
    left join DirectReport dd on d.year = dd.year 
    join CostsPerYear c on c.Year = dd.year and c.NodeId = dd.Id 
group by d.ParentNodeId, d.year, d.Id, d.Name, d.level, d.Struc 
order by d.year, d.id 

Hier ist die Geige Link: http://sqlfiddle.com/#!3/cd98d/22/0

Notiere die linke der beiden DirectReport Teil verbinden, denn ohne Kosten auch die Abteilungen zu halten.

-1
WITH DirectReport (ParentNodeId, Id, Name, LEVEL, Struc) 
AS 
(
-- anchor 
SELECT a.ParentNodeId, a.Id, a.Name, 0 AS LEVEL, cast(':' + cast(a.Id AS varchar) + ':' AS varchar (100)) AS Struc 
FROM CostStructureNodes a 
WHERE a.ParentNodeId IS NULL 
UNION ALL 
-- recursive 
SELECT a.ParentNodeId, a.Id, a.Name, LEVEL +1, cast(d.Struc + cast(a.Id AS varchar)+ ':' AS varchar(100)) AS Struc 
FROM CostStructureNodes a 
    JOIN DirectReport d ON d.Id = a.ParentNodeId 
) 

SELECT d.ParentNodeId, d.Id, d.Name, d.level, d.Struc, 
sum(CASE WHEN d.Struc = SUBSTRING(dd.Struc, 1, len(d.Struc))THEN c.Value ELSE 0 END) AS TotCost 
FROM DirectReport d,DirectReport dd 
JOIN CostsPerYear c ON c.NodeId = dd.Id 
GROUP BY d.ParentNodeId,d.Id, d.Name, d.level, d.Struc 
ORDER BY d.id 
+0

http://sqlfiddle.com/#!3/bf0f3/16 – kamal