Please start any new threads on our new
site at https://forums.sqlteam.com. We've got lots of great SQL Server
experts to answer whatever question you can come up with.
| Author |
Topic |
|
X002548
Not Just a Number
15586 Posts |
Posted - 2003-02-28 : 14:38:23
|
| Don't know if this is the place to post this...but,If I have Budget $ at the lowest level Organization:ORG BUDGET$A $10B $15C $27X $1000And I have an Organization "Chart"ORG PARENT ORGA DB EC DD EE FX FYou'll notice a tree that has many different branches.I need to take the lowest level $ and sum them through their parents. Such that:ORG SUMMED BUDGETD $25E $52F $1052I've actually built a skeletal hierarch, with a level code table, and I'm stuck at a point of trying to apply updates. Because I think it's important to know what level you're on I've been able to build:ORG PARENT ORG LEVELA D 4C D 4B E 4X F 4D E 3E F 2F Null 1I know I can get a result set for what I want, such thatSELECT PARENT_ORG, SUM($) FROM TABLE WHERE LEVEL = 4 GROUP BY PARENTBut I can't figure how to updatet the parent rows with their childrens summed dollars. That means A and C would update D, B would update E and so on. Another problem then comes when I go up a level. E now needs to add the sum of D, plus what I've already added From B in the previous step.Now I'm not making this up, and if anyone has worked with Smartstream general ledger stuff, the fact that they let users do whatever they want causes this mess (all in the name of flexibility).IF IT CAN HAPPEN IT WILL HAPPEN. This is good adive when building a DB.Any help will be GREATLY appreciated. I'm sure it can be done with cursors....(Nooooooooooooooooo not the cursors.....ahhhhhhh)ThanksBrettPS I Hope any of this makes sense |
|
|
macka
Posting Yak Master
162 Posts |
Posted - 2003-03-02 : 14:01:58
|
Joe Celko's nested set model is ideal for this kind of operation, although it would require your adjaceny model being converted/modified.The following is based on employees salaries and how much each employee is responsible for. It should be trivial to convert for your requirements.Snipped from Celko's "classic" post:[url]http://groups.google.com/groups?q=celko+salary&hl=en&lr=&ie=UTF-8&oe=UTF-8&selm=89k4h0%24ard%241%40nnrp1.deja.com&rnum=1[/url]quote: CREATE TABLE Personnel (emp CHAR(10) PRIMARY KEY, boss CHAR(10), -- this column is unneeded & denormalizes thetable salary DECIMAL(6,2) NOT NULL, lft INTEGER NOT NULL, rgt INTEGER NOT NULL); Personnel emp boss salary lft rgt =================================== Albert NULL 1000.00 1 12 Bert Albert 900.00 2 3 Chuck Albert 900.00 4 11 Donna Chuck 800.00 5 6 Eddie Chuck 700.00 7 8 Fred Chuck 600.00 9 10
and then....quote: 3. Add a GROUP BY and aggregate functions to these basic queries andyou have hierarchical reports. For example, the total salaries whicheach employee controls: SELECT P2.emp, SUM(P1.salary) FROM Personnel AS P1, Personnel AS P2 WHERE P1.lft BETWEEN P2.lft AND P2.rgt GROUP BY P2.emp;
macka.--There are only 10 types of people in the world - Those who understand binary, and those who don't. |
 |
|
|
X002548
Not Just a Number
15586 Posts |
Posted - 2003-03-03 : 10:47:56
|
| Thanks. But does this address where multiple levels? What happens for example if a parent has multiplt "lineages". |
 |
|
|
macka
Posting Yak Master
162 Posts |
Posted - 2003-03-03 : 11:32:49
|
| Its not entirely clear what you mean by multiple "lineages". Can a node have more than one parent ? OR do you mean, does it take into account the budgets across multiple levels. If its the latter then the answer is yes. I've added the lft and rgt columns to the table. --DDLCREATE TABLE org( orgID CHAR, parent CHAR null, budget MONEY, lft INT, rgt INT)--DMLINSERT INTO orgSELECT 'F',NULL,0,1,14UNIONSELECT 'X','F',1000,2,3UNIONSELECT 'E','F',0,4,13UNIONSELECT 'B','E',15,5,6UNIONSELECT 'D','E',0,7,12UNIONSELECT 'A','D',10,8,9UNIONSELECT 'C','D',27,10,11--output the resultsSELECT o2.orgID,SUM(o1.budget) AS total_budgetFROM org o1,org o2WHERE o1.lft BETWEEN o2.lft AND o2.rgtGROUP BY o2.orgIDThe result of this query is:orgID total_budget--------------------A 10.0000B 15.0000C 27.0000D 37.0000E 52.0000F 1052.0000X 1000.0000macka.--There are only 10 types of people in the world - Those who understand binary, and those who don't. |
 |
|
|
X002548
Not Just a Number
15586 Posts |
Posted - 2003-03-03 : 13:33:53
|
| VERY COOL !!I've just order the SQL For Smarties Book and I'm reading the link online, But I'm still lost on the process to derive Left and Right.THANKS FOR YOUR HELPBrett9-) |
 |
|
|
X002548
Not Just a Number
15586 Posts |
Posted - 2003-03-04 : 09:38:50
|
| The book should come in a couple of days, but any idea how to derive LEFT and RIGHT would be appreciated. Otherwise I'll work on something else till then.ThanksBrett8-) |
 |
|
|
macka
Posting Yak Master
162 Posts |
Posted - 2003-03-04 : 10:25:49
|
I initially had some problems visualising the tree using the system, but once you have mastered it on paper for small trees you can mentally apply the principle to larger trees.Draw a tree with 3 nodes on a piece of paper - 1 root and 3 children. For example A / \ B C / D Label the left hand side of the root (A) with the number 1. That gives us: (1) A / \ B C / D We now need to move down the tree from left to right. So the first node we come to is B. Label the left hand side of the tree with the number 2. (1) A / (2) B C / D As I said we are moving from left to right so keep going left and we come to node D. Label the left hand side of this with 3. (1) A / \ (2) B C /(3) D We can no longer go left as we are at the bottom of the tree - so we now need to travel back "up" on the "right" and label the nodes on the right hand side. (1) A / \ (2) B C /(3) D (4) Up again it is: (1) A / \ (2) B (5) C /(3) D (4) Having now labelled node B on the right, we are able to travel back down on the left to C. So we get : (1) A / \ (2) B(5) (6)C /(3) D (4) At the bottom of the tree again, so up on the right we go: (1) A / \ (2) B(5) (6)C (7) /(3) D (4) and then finally: (1) A (8) / \ (2) B(5) (6)C (7) /(3) D (4) As you can see we have a counter which is incremented for each side of each node of the tree. Do you remember doing dot-to-dots as a kid? - well if you join up the numbers from 1 through 8 you'll see that you have travelled around the tree from left to right, visiting each node twice.Now for the code - this will apply the above principle and convert you adjancey model to the nested set model.(courtesy of Mr. Celko - slightly modified for SQL Server by moi)-- Tree holds the adjacency modelCREATE TABLE Tree(node CHAR(10) NOT NULL, parent CHAR(10));-- THIS IS BASED ON YOUR ORG DBINSERT INTO tree(node,parent)SELDCT OrgID,ParentFROM ORG-- Stack starts empty, will holds the nested set modelCREATE TABLE Stack(stack_top INTEGER NOT NULL, node CHAR(10) NOT NULL, lft INTEGER, rgt INTEGER);DECLARE @counter INTEGER;DECLARE @max_counter INTEGER;DECLARE @current_top INTEGER;SET @counter = 2;SET @max_counter = 2 * (SELECT COUNT(*) FROM Tree);SET @current_top = 1;--clear the stackDELETE FROM Stack;-- push the rootINSERT INTO StackSELECT 1, node, 1, @max_counterFROM TreeWHERE parent IS NULL;-- delete rows from tree as they are usedDELETE FROM Tree WHERE parent IS NULL;WHILE @counter <= @max_counter- 1BEGIN IF EXISTS (SELECT * FROM Stack AS S1, Tree AS T1 WHERE S1.node = T1.parent AND S1.stack_top = @current_top) BEGIN -- push when top has subordinates and set lft value INSERT INTO Stack SELECT (@current_top + 1), MIN(T1.node), @counter, NULL FROM Stack AS S1, Tree AS T1 WHERE S1.node = T1.parent AND S1.stack_top = @current_top; -- delete rows from tree as they are used DELETE FROM Tree WHERE node = (SELECT node FROM Stack WHERE stack_top = @current_top + 1); -- housekeeping of stack pointers and counter SET @counter = @counter + 1; SET @current_top = @current_top + 1; END ELSE BEGIN -- pop the stack and set rgt value UPDATE Stack SET rgt = @counter, stack_top = -stack_top -- pops the stack WHERE stack_top = @current_top SET @counter = @counter + 1; SET @current_top = @current_top - 1; END; END -- the top column is not needed in the final answerSELECT node, lft, rgt FROM Stack;--UPDATE lft/rgt node values in your tableUPDATE OrgSET Org.lft = stack.lft,Org.rgt = stack.rgtFROM Org INNER JOIN stack ON node = OrgIDDROP TABLE treeDROP TABLE stack macka.--There are only 10 types of people in the world - Those who understand binary, and those who don't.Edited by - macka on 03/04/2003 10:30:05Edited by - macka on 03/04/2003 10:30:53Edited by - macka on 03/04/2003 10:31:34 |
 |
|
|
Sitka
Aged Yak Warrior
571 Posts |
Posted - 2003-03-07 : 07:40:11
|
| GivenPersonnel emp boss salary lft rgt =================================== Albert NULL 1000.00 1 12 Bert Albert 900.00 2 3 Chuck Albert 900.00 4 11 Donna Chuck 800.00 5 6 Eddie Chuck 700.00 7 8 Fred Chuck 600.00 9 10Can someone translate this... SELECT P2.* FROM Personnel AS P1, Personnel AS P2 WHERE P1.lft BETWEEN P2.lft AND P2.rgt AND P1.emp = :myemployee;to ANSI.Voted best SQL forum nickname...."Tutorial-D" |
 |
|
|
jsmith8858
Dr. Cross Join
7423 Posts |
Posted - 2003-03-07 : 08:00:40
|
| I think :myemployee is the input parameter.so, unless I'm mistaken, I believe the following is ANSI standard:SELECT P2.* FROM Personnel AS P1INNER JOINPersonnel AS P2 ON P1.lft BETWEEN P2.lft AND P2.rgt WHERE P1.emp = @MyEmployee; Usually, you just remove all join conditions from the WHERE clause and re-write them using INNER/OUTER JOIN syntax.- Jeff |
 |
|
|
Sitka
Aged Yak Warrior
571 Posts |
Posted - 2003-03-07 : 08:40:16
|
no mistakes Thank-youVoted best SQL forum nickname...."Tutorial-D" |
 |
|
|
|
|
|
|
|