Friday, March 23, 2012
nested tree, how to ?
my scenario is :
several process (p1,p2,p3,etc..) makes several operations
(op1,op2,op3,...)
i need to store the combination process,operation,time and this ismy
table
structure (processid,opid,dateop)
now i need to show a tree hystory of the operations in a definited date
range...
here's a sample:
op1
50% makes op2
20% makes op1
60% makes op3
100% makes op4
20% makes op5
50% makes op3
100% makes op6
100% makes op1
the real problem is the amount of data ., about 20 000 000 records ...
how can represent this tree in an efficient way ?!'!!?
thanks in advance for help
andrewI'm guessing you don't want to display all 20M lines at once, so what are yo
u
really trying to 'represent' ?
So, make sure you have an index on processid, and another on dateop.
Populate a temporary table with the processes you're interested in. Then add
to that table the details of the sub-processes, and repeat until there's
nothing more to add. You'll go through the table only once for each level yo
u
need to go down.
To make sure you only ask the 20M row table for the data you need, mark
things as done once you've pulled the data in. So, 3 values. 0 = new, 1 =
processing, 2 = done. Populate with 0. Then increment everything. Then pull
in new values (defaulting to 0), for the records that are marked with 1.
For making sure that it's ordered correctly, try using a string which
indicates the node you're looking at, with something appended to it. After
all, "aba" will come between "ab" and "ac". You might want to use lots of
characters per node though, etc... but string sorting may well work for your
ordering here better than numbers.
Hope this helps... I'm sure other people will have better ideas though.
"andrew" wrote:
> hi, i've problems representing nested tree in sql server strucutre ...
> my scenario is :
> several process (p1,p2,p3,etc..) makes several operations
> (op1,op2,op3,...)
> i need to store the combination process,operation,time and this ismy
> table
> structure (processid,opid,dateop)
> now i need to show a tree hystory of the operations in a definited date
> range...
> here's a sample:
> op1
> 50% makes op2
> 20% makes op1
> 60% makes op3
> 100% makes op4
> 20% makes op5
> 50% makes op3
> 100% makes op6
> 100% makes op1
> the real problem is the amount of data ., about 20 000 000 records ...
> how can represent this tree in an efficient way ?!'!!?
> thanks in advance for help
> andrew
>|||Please post sample data the the tree below is based on.
So far, you don't have anything that sounds like a tree datawise, since you
have not explained any relationships. Trees have parents and children, what
you posted doesn't seem to have either, outside of the formatting of the
results.
Supervisors and employees is a good example of a tree. We all understand
how that relationship works. Explain how yours works and we will be able to
give you much better advice.
"andrew" <cekgroup@.yahoo.com> wrote in message
news:1147337447.998183.307900@.i39g2000cwa.googlegroups.com...
> hi, i've problems representing nested tree in sql server strucutre ...
> my scenario is :
> several process (p1,p2,p3,etc..) makes several operations
> (op1,op2,op3,...)
> i need to store the combination process,operation,time and this ismy
> table
> structure (processid,opid,dateop)
> now i need to show a tree hystory of the operations in a definited date
> range...
> here's a sample:
> op1
> 50% makes op2
> 20% makes op1
> 60% makes op3
> 100% makes op4
> 20% makes op5
> 50% makes op3
> 100% makes op6
> 100% makes op1
> the real problem is the amount of data ., about 20 000 000 records ...
> how can represent this tree in an efficient way ?!'!!?
> thanks in advance for help
> andrew
>|||Please post DDL, so that people do not have to guess what the keys,
constraints, Declarative Referential Integrity, data types, etc. in
your schema are. Sample data is also a good idea, along with clear
specifications. What you posted makes no sense.
You have described a matrix (ops by processes), not a tree. In a tree,
one process might have one or more operations as subordinates.
You show a cycle where OP1 is one of its own subordinates. Trees do
not cycles.
Have you gotten a copy of TREES & HIERARCHIES IN SQL?|||> Have you gotten a copy of TREES & HIERARCHIES IN SQL?
Thanks I think i need it ?!?!? :-)
here is a sample of my table
TABLE : OPERATIONSLOG
--
LogID , <-- PK
CompanyID ,
ProcID ,
OperationID ,
PreviousOperationID ,
OperationDate ,
OperationName ,
PreviousOperationName
SAMPLE DATA
--
1,1, 1, null , 2006-01-01 10:00 <-- ENTRY OPERATION
1,1, 2, 1 , 2006-01-01 10:17
1,1, 5, 2 , 2006-01-01 10:32
1,1, 1, 5 , 2006-01-01 10:36
1,1, 2, 5 , 2006-01-01 10:36 --> EXIT OPERATION
1,2, 3, null , 2006-01-01 10:00 <-- ENTRY OPERATION
1,2, 1, 3 , 2006-01-01 10:06
1,2, 4, 1 , 2006-01-01 10:15 --> EXIT OPERATION
2,3, 6, null , 2006-01-02 10:00 <-- ENTRY OPERATION
2,3, 7, 6 , 2006-01-02 10:26
2,3, 11, 7 , 2006-01-02 10:46
2,3, 1, 11 , 2006-01-02 11:06
2,3, 2, 1 , 2006-01-02 12:06
2,3, 4, 2 , 2006-01-02 13:15 --> EXIT OPERATION
1,4, 1, null , 2006-01-03 10:00 <-- ENTRY OPERATION
1,4, 4, 1 , 2006-01-04 13:15 --> EXIT OPERATION
this is a flat table, with all this data , and other not relevand
fields fot this result
i need a tree result with the history of my operations.
some like is
Give me flow for operation "1" in Company "1" from 2006-01-01 to
2006-01-31
Result :
Operation "1 "
Operations "2" (qty 1 process 50%)
Operations "5" (qty 1 process 100%)
Operations "1" (qty 1 process 100%)
Operations "2" (qty 1 process 100%)
Operations "4" (qty 1 process 50%)
Give me flow for operation "3" in Company "1" from 2006-01-01 to
2006-01-31
Result:
Operations "3" (qty 1 process 100%)
Operations "1" (qty 1 process 100%)
Operations "4" (qty 1 process 100%)
the problem is with 20.000.000 records... (for all companies)
a process always execute operations in the same companyid
about 20 companies ==> 1.000.000 per company :)
a possible idea is with sql 2005 to create a new table with one record
per process ...
my idea is to store in this table :
processid , and an XML Field tha represent the flow of operations.
So when someone request me a tree history i think to use xml native
performance in sql 2005
to create the result xml ( with an xquery or xpath ...)
then i can schedule a job that each night (for eaxmple) can add the new
process stored in the
huge flat table ...
dreams ?
thanks a lot for patience, support and help
thanks in advance !!!!
andrew|||>You show a cycle where OP1 is one of its own subordinates. Trees do
>not cycles.
yes but OP1 in the root level is not the same of the OP1 excecuted
after another
operation, is relevant the operation in a specific level of the tree
...
i hope to explain me, sorry for this !?!? (and for my english too ) ;-)|||"--CELKO--" <jcelko212@.earthlink.net> wrote in message
news:1147365462.119015.291560@.u72g2000cwu.googlegroups.com...
> ...Have you gotten a copy of TREES & HIERARCHIES IN SQL?
>
It should be "...a copy of MY BOOK, TREES & HIERARCHIES IN SQL.
When you recommend your own book, you should have the moral obligation to
send a copy to the poster.
Free of charge (OK, the poster can pay transport) and autographed, of
course.|||If they buy a copy, then I promise NOT to autograph it. You cannot
return a book with an auto graph nto the distributor. I used to own
some bookstore.
.
Wednesday, March 21, 2012
Nested Sets Tree Structure
I need to store a tree using nested sets (http://www.codeproject.com/
database/nestedsets.asp) in SQL Server 2000.
I've got the database sorted, but ive got a problem reading that
data...
I need to get the data out of the database and into an XML structure
in C# (if anyones got a java example or similar i can translate it)
Can anyone point me at any code or tutorials on how to do this? I
really can't think of a sensible way of doing it, but there must be!
Cheers
Andrew
Hi
"trullock@.hotmail.com" wrote:
> Hi,
>
> I need to store a tree using nested sets (http://www.codeproject.com/
> database/nestedsets.asp) in SQL Server 2000.
> I've got the database sorted, but ive got a problem reading that
> data...
> I need to get the data out of the database and into an XML structure
> in C# (if anyones got a java example or similar i can translate it)
> Can anyone point me at any code or tutorials on how to do this? I
> really can't think of a sensible way of doing it, but there must be!
>
> Cheers
> Andrew
>
Check out http://www.perfectxml.com/Articles/XML/ExportSQLXML.asp
and http://sqlxml.org/faqs.aspx?faq=10
John
|||On 16 Feb, 08:25, John Bell <jbellnewspo...@.hotmail.com> wrote:
> Hi
>
> "trull...@.hotmail.com" wrote:
>
>
>
> Check outhttp://www.perfectxml.com/Articles/XML/ExportSQLXML.asp
> andhttp://sqlxml.org/faqs.aspx?faq=10
> John
Thanks, that looks like what I need

I've made another post about nested sets and adjacency lists if you
happen to know anything about them too,
Thanks
Andrew
|||Hi
"trullock@.hotmail.com" wrote:
> On 16 Feb, 08:25, John Bell <jbellnewspo...@.hotmail.com> wrote:
>
> Thanks, that looks like what I need

> I've made another post about nested sets and adjacency lists if you
> happen to know anything about them too,
> Thanks
> Andrew
Joe Celko's book "Trees and Hierarchies in SQL for Smarties" ISBN
1-55860-920-2 has just about everything you need for nested sets and you may
want to Google for his posts.
The parent will be the node with the maximum left value less than the
current node's left value and the minimum right values which is greater than
the current node's right value!
John
|||On 16 Feb, 10:05, John Bell <jbellnewspo...@.hotmail.com> wrote:
> The parent will be the node with the maximum left value less than the
> current node's left value and the minimum right values which is greater than
> the current node's right value!
> John
Cool thanks, think I get that.
Is it possible to do that in a single SQL command, or would I have to
use a user defined function like this:
SELECT Node_ID, Left, Right, fn_FindParent(Node_ID) as ParentID FROM
NODES
Im guessing that will be slow :s
Andrew
|||Hi Andrew
"trullock@.hotmail.com" wrote:
> On 16 Feb, 10:05, John Bell <jbellnewspo...@.hotmail.com> wrote:
>
> Cool thanks, think I get that.
> Is it possible to do that in a single SQL command, or would I have to
> use a user defined function like this:
> SELECT Node_ID, Left, Right, fn_FindParent(Node_ID) as ParentID FROM
> NODES
> Im guessing that will be slow :s
> Andrew
>
There may be other ways to get the parent, the speed would really be
dependent on the size of the hierarchy.
If you wanted to do this in one query it would be (using the ddl from the
article):
SELECT e.EmployeeID,
e.ParentID,
(SELECT f.EmployeeID
FROM Employee f WHERE f.LeftExtent =
(SELECT MAX(g.LeftExtent) FROM Employee g WHERE g.LeftExtent < e.LeftExtent
AND g.RightExtent > e.RightExtent )
AND f.RightExtent =
(SELECT Min(g.RightExtent) FROM Employee g WHERE g.RightExtent >
e.RightExtent AND g.LeftExtent < e.LeftExtent )
)
FROM Employee e
As a function:
CREATE Function dbo.fn_GetLevel ( @.LeftExtent int, @.RightExtent int )
RETURNS INT
AS
BEGIN
RETURN ( SELECT f.EmployeeID
FROM Employee f
WHERE f.LeftExtent =
(SELECT MAX(g.LeftExtent) FROM Employee g WHERE g.LeftExtent <
@.LeftExtent AND g.RightExtent > @.RightExtent )
AND f.RightExtent =
(SELECT Min(g.RightExtent) FROM Employee g WHERE g.RightExtent >
@.RightExtent AND g.LeftExtent < @.LeftExtent )
)
END
SELECT e.EmployeeID,
e.ParentID,
dbo.fn_GetLevel ( e.LeftExtent, e.RightExtent )
FROM Employee e
John
sql
Nested Sets Tree Structure
I need to store a tree using nested sets (http://www.codeproject.com/
database/nestedsets.asp) in SQL Server 2000.
I've got the database sorted, but ive got a problem reading that
data...
I need to get the data out of the database and into an XML structure
in C# (if anyones got a java example or similar i can translate it)
Can anyone point me at any code or tutorials on how to do this? I
really can't think of a sensible way of doing it, but there must be!
Cheers
AndrewHi
"trullock@.hotmail.com" wrote:
> Hi,
>
> I need to store a tree using nested sets (http://www.codeproject.com/
> database/nestedsets.asp) in SQL Server 2000.
> I've got the database sorted, but ive got a problem reading that
> data...
> I need to get the data out of the database and into an XML structure
> in C# (if anyones got a java example or similar i can translate it)
> Can anyone point me at any code or tutorials on how to do this? I
> really can't think of a sensible way of doing it, but there must be!
>
> Cheers
> Andrew
>
Check out http://www.perfectxml.com/Articles/XML/ExportSQLXML.asp
and http://sqlxml.org/faqs.aspx?faq=10
John|||On 16 Feb, 08:25, John Bell <jbellnewspo...@.hotmail.com> wrote:
> Hi
>
> "trull...@.hotmail.com" wrote:
> > Hi,
> > I need to store a tree using nested sets (http://www.codeproject.com/
> > database/nestedsets.asp) in SQL Server 2000.
> > I've got the database sorted, but ive got a problem reading that
> > data...
> > I need to get the data out of the database and into an XML structure
> > in C# (if anyones got a java example or similar i can translate it)
> > Can anyone point me at any code or tutorials on how to do this? I
> > really can't think of a sensible way of doing it, but there must be!
> > Cheers
> > Andrew
> Check outhttp://www.perfectxml.com/Articles/XML/ExportSQLXML.asp
> andhttp://sqlxml.org/faqs.aspx?faq=10
> John
Thanks, that looks like what I need :)
I've made another post about nested sets and adjacency lists if you
happen to know anything about them too,
Thanks
Andrew|||Hi
"trullock@.hotmail.com" wrote:
> On 16 Feb, 08:25, John Bell <jbellnewspo...@.hotmail.com> wrote:
> > Hi
> >
> >
> >
> > "trull...@.hotmail.com" wrote:
> > > Hi,
> >
> > > I need to store a tree using nested sets (http://www.codeproject.com/
> > > database/nestedsets.asp) in SQL Server 2000.
> >
> > > I've got the database sorted, but ive got a problem reading that
> > > data...
> >
> > > I need to get the data out of the database and into an XML structure
> > > in C# (if anyones got a java example or similar i can translate it)
> >
> > > Can anyone point me at any code or tutorials on how to do this? I
> > > really can't think of a sensible way of doing it, but there must be!
> >
> > > Cheers
> >
> > > Andrew
> >
> > Check outhttp://www.perfectxml.com/Articles/XML/ExportSQLXML.asp
> > andhttp://sqlxml.org/faqs.aspx?faq=10
> >
> > John
>
> Thanks, that looks like what I need :)
> I've made another post about nested sets and adjacency lists if you
> happen to know anything about them too,
> Thanks
> Andrew
Joe Celko's book "Trees and Hierarchies in SQL for Smarties" ISBN
1-55860-920-2 has just about everything you need for nested sets and you may
want to Google for his posts.
The parent will be the node with the maximum left value less than the
current node's left value and the minimum right values which is greater than
the current node's right value!
John|||On 16 Feb, 10:05, John Bell <jbellnewspo...@.hotmail.com> wrote:
> The parent will be the node with the maximum left value less than the
> current node's left value and the minimum right values which is greater than
> the current node's right value!
> John
Cool thanks, think I get that.
Is it possible to do that in a single SQL command, or would I have to
use a user defined function like this:
SELECT Node_ID, Left, Right, fn_FindParent(Node_ID) as ParentID FROM
NODES
Im guessing that will be slow :s
Andrew|||Hi Andrew
"trullock@.hotmail.com" wrote:
> On 16 Feb, 10:05, John Bell <jbellnewspo...@.hotmail.com> wrote:
> > The parent will be the node with the maximum left value less than the
> > current node's left value and the minimum right values which is greater than
> > the current node's right value!
> >
> > John
>
> Cool thanks, think I get that.
> Is it possible to do that in a single SQL command, or would I have to
> use a user defined function like this:
> SELECT Node_ID, Left, Right, fn_FindParent(Node_ID) as ParentID FROM
> NODES
> Im guessing that will be slow :s
> Andrew
>
There may be other ways to get the parent, the speed would really be
dependent on the size of the hierarchy.
If you wanted to do this in one query it would be (using the ddl from the
article):
SELECT e.EmployeeID,
e.ParentID,
(SELECT f.EmployeeID
FROM Employee f WHERE f.LeftExtent =(SELECT MAX(g.LeftExtent) FROM Employee g WHERE g.LeftExtent < e.LeftExtent
AND g.RightExtent > e.RightExtent )
AND f.RightExtent =(SELECT Min(g.RightExtent) FROM Employee g WHERE g.RightExtent >
e.RightExtent AND g.LeftExtent < e.LeftExtent )
)
FROM Employee e
As a function:
CREATE Function dbo.fn_GetLevel ( @.LeftExtent int, @.RightExtent int )
RETURNS INT
AS
BEGIN
RETURN ( SELECT f.EmployeeID
FROM Employee f
WHERE f.LeftExtent = (SELECT MAX(g.LeftExtent) FROM Employee g WHERE g.LeftExtent <
@.LeftExtent AND g.RightExtent > @.RightExtent )
AND f.RightExtent = (SELECT Min(g.RightExtent) FROM Employee g WHERE g.RightExtent >
@.RightExtent AND g.LeftExtent < @.LeftExtent )
)
END
SELECT e.EmployeeID,
e.ParentID,
dbo.fn_GetLevel ( e.LeftExtent, e.RightExtent )
FROM Employee e
John
Nested Sets Tree Structure
I need to store a tree using nested sets (http://www.codeproject.com/
database/nestedsets.asp) in SQL Server 2000.
I've got the database sorted, but ive got a problem reading that
data...
I need to get the data out of the database and into an XML structure
in C# (if anyones got a Java example or similar i can translate it)
Can anyone point me at any code or tutorials on how to do this? I
really can't think of a sensible way of doing it, but there must be!
Cheers
AndrewHi
"trullock@.hotmail.com" wrote:
> Hi,
>
> I need to store a tree using nested sets (http://www.codeproject.com/
> database/nestedsets.asp) in SQL Server 2000.
> I've got the database sorted, but ive got a problem reading that
> data...
> I need to get the data out of the database and into an XML structure
> in C# (if anyones got a Java example or similar i can translate it)
> Can anyone point me at any code or tutorials on how to do this? I
> really can't think of a sensible way of doing it, but there must be!
>
> Cheers
> Andrew
>
Check out http://www.perfectxml.com/Articles/XML/ExportSQLXML.asp
and http://sqlxml.org/faqs.aspx?faq=10
John|||On 16 Feb, 08:25, John Bell <jbellnewspo...@.hotmail.com> wrote:
> Hi
>
> "trull...@.hotmail.com" wrote:
>
>
>
>
>
>
> Check outhttp://www.perfectxml.com/Articles/XML/ExportSQLXML.asp
> andhttp://sqlxml.org/faqs.aspx?faq=10
> John
Thanks, that looks like what I need

I've made another post about nested sets and adjacency lists if you
happen to know anything about them too,
Thanks
Andrew|||Hi
"trullock@.hotmail.com" wrote:
> On 16 Feb, 08:25, John Bell <jbellnewspo...@.hotmail.com> wrote:
>
> Thanks, that looks like what I need

> I've made another post about nested sets and adjacency lists if you
> happen to know anything about them too,
> Thanks
> Andrew
Joe Celko's book "Trees and Hierarchies in SQL for Smarties" ISBN
1-55860-920-2 has just about everything you need for nested sets and you may
want to Google for his posts.
The parent will be the node with the maximum left value less than the
current node's left value and the minimum right values which is greater tha
n
the current node's right value!
John|||On 16 Feb, 10:05, John Bell <jbellnewspo...@.hotmail.com> wrote:
> The parent will be the node with the maximum left value less than the
> current node's left value and the minimum right values which is greater t
han
> the current node's right value!
> John
Cool thanks, think I get that.
Is it possible to do that in a single SQL command, or would I have to
use a user defined function like this:
SELECT Node_ID, Left, Right, fn_FindParent(Node_ID) as ParentID FROM
NODES
Im guessing that will be slow :s
Andrew|||Hi Andrew
"trullock@.hotmail.com" wrote:
> On 16 Feb, 10:05, John Bell <jbellnewspo...@.hotmail.com> wrote:
>
> Cool thanks, think I get that.
> Is it possible to do that in a single SQL command, or would I have to
> use a user defined function like this:
> SELECT Node_ID, Left, Right, fn_FindParent(Node_ID) as ParentID FROM
> NODES
> Im guessing that will be slow :s
> Andrew
>
There may be other ways to get the parent, the speed would really be
dependent on the size of the hierarchy.
If you wanted to do this in one query it would be (using the ddl from the
article):
SELECT e.EmployeeID,
e.ParentID,
(SELECT f.EmployeeID
FROM Employee f WHERE f.LeftExtent =
(SELECT MAX(g.LeftExtent) FROM Employee g WHERE g.LeftExtent < e.LeftExtent
AND g.RightExtent > e.RightExtent )
AND f.RightExtent =
(SELECT Min(g.RightExtent) FROM Employee g WHERE g.RightExtent >
e.RightExtent AND g.LeftExtent < e.LeftExtent )
)
FROM Employee e
As a function:
CREATE Function dbo.fn_GetLevel ( @.LeftExtent int, @.RightExtent int )
RETURNS INT
AS
BEGIN
RETURN ( SELECT f.EmployeeID
FROM Employee f
WHERE f.LeftExtent =
(SELECT MAX(g.LeftExtent) FROM Employee g WHERE g.LeftExtent <
@.LeftExtent AND g.RightExtent > @.RightExtent )
AND f.RightExtent =
(SELECT Min(g.RightExtent) FROM Employee g WHERE g.RightExtent >
@.RightExtent AND g.LeftExtent < @.LeftExtent )
)
END
SELECT e.EmployeeID,
e.ParentID,
dbo.fn_GetLevel ( e.LeftExtent, e.RightExtent )
FROM Employee e
John
Nested set show leaves of parent
Hello,
I have the following code which will show all bottom level leaf nodes of the hierachy:
SELECT name
FROM tree
WHERE rgt = lft + 1;
I'd like to be able to filter results by a node. For example in a tree such as:
Products
ReleaseProduct
Release1
Release build 1
Release build 2
Release 2
Release 2 build 1
Release 2 build 2
Build Product
Build 1
Build 2
If Build 2 is chosen (any node with no children) I'd like to just show the Buuild 2, if ReleaseProduct is chosen Release build 1, Release build 2, Release 2 build 1 and Release 2 build 2 will be shown and if BuildProduct is chosen I'd like to display Build 1, Build 2.
I understand the prinicipals but my SQL is quite lacking anything further than the select, where statements. If anyone could please lend me a little advice on how to go about this I would be very grateful!
Thanks :)
Hello,
Can you post the schema of the table in question and what version of SQL Server you are using?
If 2005, a recursive CTE sounds like it may suit, otherwise a more "creative" solution may apply. let us know the specifics and I'm sure we can help out.
Cheers,
Rob
|||Thank's for the quick reply!The schema is as follows:
CREATE TABLE site_category(
site_id INT IDENTITY(1,1) PRIMARY KEY,
name VARCHAR(20) NOT NULL,
lft INT NOT NULL,
rgt INT NOT NULL
);
So a site may be a root, parent or child depending on the left and right values of the nodes in the hierachy. I'm using 2005 Express.
Thanks for the help!|||
Hello,
I don't know what lft or rgt is, but I'm going to assume that they contain the site_id of the parent node. So, to simplify this, let's call it ParentSiteID:
with Sites(SiteName, site_id, ParentID, NestLevel)
AS
(
SELECT [name], site_id, parentSiteID, 0
FROM site_category
WHERE [name] = 'Site123'
UNION ALL
SELECT sc.[Name], sc.Site_ID, s.Site_ID,(NestLevel + 1)
FROM Sites s
JOIN site_category sc ON s.Site_ID = sc.ParentSiteID
)
SELECT *
FROM Sites
The above example will return "Site123" and all child nodes therein (including any nested relationships). The NestLevel column indicates how deep the nesting level is. You'll need to adjust this to cater for your lft/rgt columns...
Cheers,
Rob
|||The lft and rgt fields store values used to determine the level in the hierachy. The example from the MySQL site I am using as a guide is:
http://dev.mysql.com/tech-resources/articles/hierarchical-data.html
Following this I have got to the heading 'Finding the Depth of the Nodes' which produces the results I am after.
Where I'm having trouble is the heading 'Find the Immediate Subordinates of a Node' which is exactly what I need and is explained with code but I just can't figure it out! I feel there may be some subtle differences in the SQL used in this MySQL example and the TSQL SQL Server is expecting. Not to mention my SQL knowledge isn't great at this point!
I havn't tried your example but feel this post may offer a better explanation as (I may be wrong) your example looks like it assumes I am using an Adjacency List Model.
I appreciate your time! :)
|||Hello,
OK, I understand what you're trying to do:
SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
nested_category AS parent,
nested_category AS sub_parent,
(
SELECT TOP 100 node.name, (COUNT(parent.name) - 1) AS depth
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'PORTABLE ELECTRONICS'
GROUP BY node.name, node.lft
ORDER BY node.lft
)AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
AND sub_parent.name = sub_tree.name
GROUP BY node.name, depth, node.lft
HAVING depth <= 1
ORDER BY node.lft;
Does that do what you want?
Cheers,
Rob
|||That works exactly how I want!
Is the TOP keyword and value an approximation of the rows to be returned to be returned, as the complete result set is not loaded into memory?
Thanks :)
|||Actually, the only reason to use TOP in the sub query is because without it, you cannot use an order by. So you could actually remove it and the corresponding order by:
SELECT node.name, (COUNT(parent.name) - (sub_tree.depth + 1)) AS depth
FROM nested_category AS node,
nested_category AS parent,
nested_category AS sub_parent,
(
SELECT node.name, (COUNT(parent.name) - 1) AS depth
FROM nested_category AS node,
nested_category AS parent
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.name = 'PORTABLE ELECTRONICS'
GROUP BY node.name, node.lft
)AS sub_tree
WHERE node.lft BETWEEN parent.lft AND parent.rgt
AND node.lft BETWEEN sub_parent.lft AND sub_parent.rgt
AND sub_parent.name = sub_tree.name
GROUP BY node.name, depth, node.lft
HAVING depth <= 1
ORDER BY node.lft;
Cheers,
Rob
|||Oh I see, Thanks again!sqlMonday, March 12, 2012
Nested Coalescing possible in SQL?
describe my problem. I have a tree where each node has the same set of
attributes (is the same entity) but child nodes should inherit
attribute values from parent node.
for example, say I have the following table:
(nodeId int , color varchar, phone varchar) with two rows
5, "GREEN", "555-1212"
7, NULL, "777-5555"
8, NULL, NULL
9, "BLUE", NULL
in addition there is a tree structure that specifies that node 5 is
the parent of node 7, and 7 is the parent of nodes 8 and 9. I know
there is many ways to make trees in SQL but as a simple example let's
say the tree is:
id, parentid
8, 7
9, 7
7, 5
Thus in this case, node 7 inherits the value "GREEN" from node 5 for
attribute "color", but provides its own value "777-5555" for attribute
"phone". Node 8, in turn, inherits "GREEN" for "color" from node 7
(really from node 5 since 7 did not specify its own) and "777-5555"
for "phone" from node 7. Node 9 provides its own value for "color" and
inherits the one for "phone" from Node 7.
So the runtime values in the application are:
Node 5: "GREEN", "555-1212"
Node 7: "GREEN", "777-5555"
Node 8: "GREEN", "777-5555"
Node 9: "BLUE", "777-5555"
Question 1: Is there a single SQL statement that for a given node can
replace the NULLs with inherited values from the parent node?
Question 2: Is there a better way to structure such data in SQL as to
make answer to question 1 possible?
I would restate the problem as follows:
In a nested structure child nodes inherit values from parent nodes _by
reference_ or specify their own. "By reference" is the key word here.
If it wasn't for that you could just duplicate the necessary values
from the parent entitity upon creation.
Thanks!
- Jeff"Jeff Lanfield" <jlanfield2003@.yahoo.com> wrote in message
news:235c483f.0406011716.37d00399@.posting.google.c om...
> First of all, I apologize if coalescing is not the right term to
> describe my problem. I have a tree where each node has the same set of
> attributes (is the same entity) but child nodes should inherit
> attribute values from parent node.
> for example, say I have the following table:
> (nodeId int , color varchar, phone varchar) with two rows
> 5, "GREEN", "555-1212"
> 7, NULL, "777-5555"
> 8, NULL, NULL
> 9, "BLUE", NULL
> in addition there is a tree structure that specifies that node 5 is
> the parent of node 7, and 7 is the parent of nodes 8 and 9. I know
> there is many ways to make trees in SQL but as a simple example let's
> say the tree is:
> id, parentid
> 8, 7
> 9, 7
> 7, 5
> Thus in this case, node 7 inherits the value "GREEN" from node 5 for
> attribute "color", but provides its own value "777-5555" for attribute
> "phone". Node 8, in turn, inherits "GREEN" for "color" from node 7
> (really from node 5 since 7 did not specify its own) and "777-5555"
> for "phone" from node 7. Node 9 provides its own value for "color" and
> inherits the one for "phone" from Node 7.
> So the runtime values in the application are:
> Node 5: "GREEN", "555-1212"
> Node 7: "GREEN", "777-5555"
> Node 8: "GREEN", "777-5555"
> Node 9: "BLUE", "777-5555"
> Question 1: Is there a single SQL statement that for a given node can
> replace the NULLs with inherited values from the parent node?
> Question 2: Is there a better way to structure such data in SQL as to
> make answer to question 1 possible?
> I would restate the problem as follows:
> In a nested structure child nodes inherit values from parent nodes _by
> reference_ or specify their own. "By reference" is the key word here.
> If it wasn't for that you could just duplicate the necessary values
> from the parent entitity upon creation.
It looks easy. Find a closest node in the chain of ancestors that has
property not NULL.|||jlanfield2003@.yahoo.com (Jeff Lanfield) wrote in message news:<235c483f.0406011716.37d00399@.posting.google.com>...
> First of all, I apologize if coalescing is not the right term to
> describe my problem. I have a tree where each node has the same set of
> attributes (is the same entity) but child nodes should inherit
> attribute values from parent node.
> for example, say I have the following table:
> (nodeId int , color varchar, phone varchar) with two rows
> 5, "GREEN", "555-1212"
> 7, NULL, "777-5555"
> 8, NULL, NULL
> 9, "BLUE", NULL
> in addition there is a tree structure that specifies that node 5 is
> the parent of node 7, and 7 is the parent of nodes 8 and 9. I know
> there is many ways to make trees in SQL but as a simple example let's
> say the tree is:ancestor a
> id, parentid
> 8, 7
> 9, 7
> 7, 5
> Thus in this case, node 7 inherits the value "GREEN" from node 5 for
> attribute "color", but provides its own value "777-5555" for attribute
> "phone". Node 8, in turn, inherits "GREEN" for "color" from node 7
> (really from node 5 since 7 did not specify its own) and "777-5555"
> for "phone" from node 7. Node 9 provides its own value for "color" and
> inherits the one for "phone" from Node 7.
> So the runtime values in the application are:
> Node 5: "GREEN", "555-1212"
> Node 7: "GREEN", "777-5555"
> Node 8: "GREEN", "777-5555"
> Node 9: "BLUE", "777-5555"
> Question 1: Is there a single SQL statement that for a given node can
> replace the NULLs with inherited values from the parent node?
> Question 2: Is there a better way to structure such data in SQL as to
> make answer to question 1 possible?
> I would restate the problem as follows:
> In a nested structure child nodes inherit values from parent nodes _by
> reference_ or specify their own. "By reference" is the key word here.
> If it wasn't for that you could just duplicate the necessary values
> from the parent entitity upon creation.
> Thanks!
> - Jeff
If you would like to do it in a single query, you will have to extend
your tree, or if your db supports it, you can use recursion. There are
several ways of extending your tree, nested set, transitive closure,
etc. If you google comp.database and comp.database.theory you will
find several threads regarding this.
Assuming you can "calculate" the set of ancestors for any given node,
define the suspects as "ancestors with property p". The property you
are looking for can be found in the suspect with the largest depth*.
*depth = number of ancestors
HTH
/Lennart|||>> Question 2: Is there a better way to structure such data in SQL as
to make answer to question 1 possible? <<
Here is the link on Amazon.com for my new book on "Trees & Hierarchies
in SQL"
http://www.amazon.com/exec/obidos/t...product-details
The classic scenario calls for a root class with all the common
attributes and then specialized sub-classes under it. As an example,
let's take the class of Vehicles and find an industry standard
identifier (VIN), and add two mutually exclusive sub-classes, Sport
utility vehicles and sedans ('SUV', 'SED').
CREATE TABLE Vehicles
(vin CHAR(17) NOT NULL PRIMARY KEY,
vehicle_type CHAR(3) NOT NULL
CHECK(vehicle_type IN ('SUV', 'SED')),
UNIQUE (vin, vehicle_type),
..);
Notice the overlapping candidate keys. I then use a compound candidate
key (vin, vehicle_type) and a constraint in each sub-class table to
assure that the vehicle_type is locked and agrees with the Vehicles
table. Add some DRI actions and you are done:
CREATE TABLE SUV
(vin CHAR(17) NOT NULL PRIMARY KEY,
vehicle_type CHAR(3) DEFAULT 'SUV' NOT NULL
CHECK(vehicle_type = 'SUV'),
UNIQUE (vin, vehicle_type),
FOREIGN KEY (vin, vehicle_type)
REFERENCES Vehicles(vin, vehicle_type)
ON UPDATE CASCADE
ON DELETE CASCADE,
..);
CREATE TABLE Sedans
(vin CHAR(17) NOT NULL PRIMARY KEY,
vehicle_type CHAR(3) DEFAULT 'SED' NOT NULL
CHECK(vehicle_type = 'SED'),
UNIQUE (vin, vehicle_type),
FOREIGN KEY (vin, vehicle_type)
REFERENCES Vehicles(vin, vehicle_type)
ON UPDATE CASCADE
ON DELETE CASCADE,
..);
I can continue to build a hierarchy like this. For example, if I had
a Sedans table that broke down into two-door and four-door sedans, I
could a schema like this:
CREATE TABLE Sedans
(vin CHAR(17) NOT NULL PRIMARY KEY,
vehicle_type CHAR(3) DEFAULT 'SED' NOT NULL
CHECK(vehicle_type IN ('2DR', '4DR', 'SED')),
UNIQUE (vin, vehicle_type),
FOREIGN KEY (vin, vehicle_type)
REFERENCES Vehicles(vin, vehicle_type)
ON UPDATE CASCADE
ON DELETE CASCADE,
..);
CREATE TABLE TwoDoor
(vin CHAR(17) NOT NULL PRIMARY KEY,
vehicle_type CHAR(3) DEFAULT '2DR' NOT NULL
CHECK(vehicle_type = '2DR'),
UNIQUE (vin, vehicle_type),
FOREIGN KEY (vin, vehicle_type)
REFERENCES Sedans(vin, vehicle_type)
ON UPDATE CASCADE
ON DELETE CASCADE,
..);
CREATE TABLE FourDoor
(vin CHAR(17) NOT NULL PRIMARY KEY,
vehicle_type CHAR(3) DEFAULT '4DR' NOT NULL
CHECK(vehicle_type = '4DR'),
UNIQUE (vin, vehicle_type),
FOREIGN KEY (vin, vehicle_type)
REFERENCES Sedans (vin, vehicle_type)
ON UPDATE CASCADE
ON DELETE CASCADE,
..);
The idea is to build a chain of identifiers and types in a UNIQUE()
constraint that go up the tree when you use a REFERENCES constraint.
Obviously, you can do variants of this trick to get different class
structures.
If an entity doesn't have to be exclusively one subtype, you play with
the root of the class hierarchy:
CREATE TABLE Vehicles
(vin CHAR(17) NOT NULL,
vehicle_type CHAR(3) NOT NULL
CHECK(vehicle_type IN ('SUV', 'SED')),
PRIMARY KEY (vin, vehicle_type),
..);
Now start hiding all this stuff in VIEWs immediately and add an
INSTEAD OF trigger to those VIEWs.|||jlanfield2003@.yahoo.com (Jeff Lanfield) wrote in message news:<235c483f.0406021038.13a1b83b@.posting.google.com>...
[...]
> So say the tree is specified like this:
> (nodeId int, parentId int, color varchar, phone varchar)
> 5, 0,"GREEN", "555-1212"
> 7, 5, NULL, "777-5555"
> 8, 7 NULL, NULL
> 9, 7 "BLUE", NULL
>
> That is: node 7 inherits values from 5. Nodes 8,9 inherit values from
> 7. Node 5 is the top level node.
> I want to run a query that would give the following result set:
> select nodeId, color, phone from ...
> 5,"GREEN", "555-1212"
> 7,"GREEN", "777-5555"
> 8,"GREEN", "777-5555"
> 9,"BLUE", "777-5555"
> Can such a query be constructed?
If you dont have a maximum depth of your tree (and cannot use
recursion), then no. The reason is that you cannot express queries
like "gimmie the ancestors of x". What you need to do is to inform
your system that 5 is an ancestor of 9. There are several ways of
doing this. Troels Arvin has a page with links to articles on the
subject:
http://troels.arvin.dk/db/rdbms/links/#hierarchical
1. Nested set: google for Celko and nested. There are also variants of
this.
2. Store the path in each node. In your case something like:
5,'',"GREEN", "555-1212"
7,'5',"GREEN", "777-5555"
8,'5.7',"GREEN", "777-5555"
9,'5.7',"BLUE", "777-5555"
In your case I dont think this is an option
3. Store a separate ancestor relation. In your case
create table ancestor (
nodeid int not null
references tree,
ancestorid int not null
references tree,
primary key (nodeid, ancestorid)
);
insert into ancestor values (7,5), (8,7), (8,5), (9,7), (9,5);
Main drawback is that you have to maintain this relation as you
modifies your tree. I have some notes on how that can be achieved:
http://fungus.teststation.com/~jon/...reeHandling.htm
Anyhow, once you have a way of asking for ancestors for a given node,
you can do what you want in a single query, namely:
find the ancestor at the largest depth with property p
Assuming the following ddl
create table tree (
nodeid int not null primary key,
parent int not null
references tree
on delete restrict
);
create table data (
nodeid int not null primary key
references tree
on delete cascade,
color varchar(10),
phone varchar(10)
);
insert into tree values (5,5), (7,5), (8,7), (9,7);
insert into data values (5, 'GREEN', '555-1212'), (7, NULL,
'777-5555'), (8, NUL
L, NULL), (9, 'BLUE', NULL);
create table ancestor (
nodeid int not null
references tree,
ancestorid int not null
references tree,
primary key (nodeid, ancestorid)
);
insert into ancestor values (7,5), (8,7), (8,5), (9,7), (9,5);
You could use a query like:
with suspects (nodeid, suspectid, depth) as (
select
a.nodeid, a.ancestorid,
(select count(1) from ancestor where nodeid = a.ancestorid) as
depth
from ancestor a, data d
where a.ancestorid = d.nodeid
and d.color is not null
union all
select
d.nodeid, d.nodeid,
(select count(1) from ancestor where nodeid = d.nodeid) as
depth
from data d
where d.color is not null
and not exists (
select 1 from ancestor where ancestorid = d.nodeid
)
)
select s.nodeid, d.color from suspects s, data d
where d.nodeid = s.suspectid
and depth = (select max(depth) from suspects where nodeid =
s.nodeid)
NODEID COLOR
---- ----
7 GREEN
8 GREEN
9 BLUE
3 record(s) selected.
As you can see node 5 is missing, but I'll leave that for you ;-)
HTH
/Lennart
> - Jeff|||Thanks --CELKO--, this is a very useful suggestion and I will keep it
in mind. However, it is designed to handle an hiearchy of types (e.g.
representing class hierarchy). My case is slightly different: I don't
have types and subtypes, I have only one table representing one
entity. The inheritance of values is in the following sense: if an
entity instance (one row) does not specify a value for one of its
fields it should inherit the values from its nearest parent that does
have a value for this field. Note that the immediate parent may have a
NULL in that field too, so you'd have to go the parent's parent and so
on till you find a non-NULL value for the field in question. I think I
should be able to use COALESCE to do it somehow but I can't come up
with a *single* query.
So the exact example is this. I have a tree structure of values, there
is only one type of entity. Per Lennart's suggestion I'm keeping the
full path pre-computed for the sake of simplicity. There is only this
one table:
(nodeId int, varchar parent_path, color varchar, phone varchar)
5, "0", "GREEN", "555-1212"
7, "0,5", NULL, "777-5555"
8, "0,5,7" NULL, NULL
9, "0,5,7" "BLUE", NULL
I want to run a query that would give the following result set:
select nodeId, color, phone from ...
5,"GREEN", "555-1212"
7,"GREEN", "777-5555"
8,"GREEN", "777-5555"
9,"BLUE", "777-5555"
Is it possible to have such a query?
- Jeff
jcelko212@.earthlink.net (--CELKO--) wrote in message news:<18c7b3c2.0406021241.119e23ec@.posting.google.com>...
> >> Question 2: Is there a better way to structure such data in SQL as
> to make answer to question 1 possible? <<
> Here is the link on Amazon.com for my new book on "Trees & Hierarchies
> in SQL"
> http://www.amazon.com/exec/obidos/t...product-details
> The classic scenario calls for a root class with all the common
> attributes and then specialized sub-classes under it. As an example,
> let's take the class of Vehicles and find an industry standard
> identifier (VIN), and add two mutually exclusive sub-classes, Sport
> utility vehicles and sedans ('SUV', 'SED').
> CREATE TABLE Vehicles
> (vin CHAR(17) NOT NULL PRIMARY KEY,
> vehicle_type CHAR(3) NOT NULL
> CHECK(vehicle_type IN ('SUV', 'SED')),
> UNIQUE (vin, vehicle_type),
> ..);
> Notice the overlapping candidate keys. I then use a compound candidate
> key (vin, vehicle_type) and a constraint in each sub-class table to
> assure that the vehicle_type is locked and agrees with the Vehicles
> table. Add some DRI actions and you are done:
> CREATE TABLE SUV
> (vin CHAR(17) NOT NULL PRIMARY KEY,
> vehicle_type CHAR(3) DEFAULT 'SUV' NOT NULL
> CHECK(vehicle_type = 'SUV'),
> UNIQUE (vin, vehicle_type),
> FOREIGN KEY (vin, vehicle_type)
> REFERENCES Vehicles(vin, vehicle_type)
> ON UPDATE CASCADE
> ON DELETE CASCADE,
> ..);
> CREATE TABLE Sedans
> (vin CHAR(17) NOT NULL PRIMARY KEY,
> vehicle_type CHAR(3) DEFAULT 'SED' NOT NULL
> CHECK(vehicle_type = 'SED'),
> UNIQUE (vin, vehicle_type),
> FOREIGN KEY (vin, vehicle_type)
> REFERENCES Vehicles(vin, vehicle_type)
> ON UPDATE CASCADE
> ON DELETE CASCADE,
> ..);
> I can continue to build a hierarchy like this. For example, if I had
> a Sedans table that broke down into two-door and four-door sedans, I
> could a schema like this:
> CREATE TABLE Sedans
> (vin CHAR(17) NOT NULL PRIMARY KEY,
> vehicle_type CHAR(3) DEFAULT 'SED' NOT NULL
> CHECK(vehicle_type IN ('2DR', '4DR', 'SED')),
> UNIQUE (vin, vehicle_type),
> FOREIGN KEY (vin, vehicle_type)
> REFERENCES Vehicles(vin, vehicle_type)
> ON UPDATE CASCADE
> ON DELETE CASCADE,
> ..);
> CREATE TABLE TwoDoor
> (vin CHAR(17) NOT NULL PRIMARY KEY,
> vehicle_type CHAR(3) DEFAULT '2DR' NOT NULL
> CHECK(vehicle_type = '2DR'),
> UNIQUE (vin, vehicle_type),
> FOREIGN KEY (vin, vehicle_type)
> REFERENCES Sedans(vin, vehicle_type)
> ON UPDATE CASCADE
> ON DELETE CASCADE,
> ..);
> CREATE TABLE FourDoor
> (vin CHAR(17) NOT NULL PRIMARY KEY,
> vehicle_type CHAR(3) DEFAULT '4DR' NOT NULL
> CHECK(vehicle_type = '4DR'),
> UNIQUE (vin, vehicle_type),
> FOREIGN KEY (vin, vehicle_type)
> REFERENCES Sedans (vin, vehicle_type)
> ON UPDATE CASCADE
> ON DELETE CASCADE,
> ..);
> The idea is to build a chain of identifiers and types in a UNIQUE()
> constraint that go up the tree when you use a REFERENCES constraint.
> Obviously, you can do variants of this trick to get different class
> structures.
> If an entity doesn't have to be exclusively one subtype, you play with
> the root of the class hierarchy:
> CREATE TABLE Vehicles
> (vin CHAR(17) NOT NULL,
> vehicle_type CHAR(3) NOT NULL
> CHECK(vehicle_type IN ('SUV', 'SED')),
> PRIMARY KEY (vin, vehicle_type),
> ..);
> Now start hiding all this stuff in VIEWs immediately and add an
> INSTEAD OF trigger to those VIEWs.|||Thanks Lennart. I think your third suggestion might do the trick. If
you have a chance to answer I'm curious:
1) Why do you think storing the path (suggestion #2) is not an option?
Or if it is option, why is it a bad idea compared to storing a
separate ancestor relation? (aside from the usual normal form
reasoning)
2) How would you structure the query if you stored the path
(suggestion #2).
Thanks!
- Jeff
lennart@.kommunicera.umea.se (Lennart Jonsson) wrote in message news:<6dae7e65.0406022337.338870@.posting.google.com>...
> jlanfield2003@.yahoo.com (Jeff Lanfield) wrote in message news:<235c483f.0406021038.13a1b83b@.posting.google.com>...
> [...]
> > So say the tree is specified like this:
> > (nodeId int, parentId int, color varchar, phone varchar)
> > 5, 0,"GREEN", "555-1212"
> > 7, 5, NULL, "777-5555"
> > 8, 7 NULL, NULL
> > 9, 7 "BLUE", NULL
> > That is: node 7 inherits values from 5. Nodes 8,9 inherit values from
> > 7. Node 5 is the top level node.
> > I want to run a query that would give the following result set:
> > select nodeId, color, phone from ...
> > 5,"GREEN", "555-1212"
> > 7,"GREEN", "777-5555"
> > 8,"GREEN", "777-5555"
> > 9,"BLUE", "777-5555"
> > Can such a query be constructed?
> If you dont have a maximum depth of your tree (and cannot use
> recursion), then no. The reason is that you cannot express queries
> like "gimmie the ancestors of x". What you need to do is to inform
> your system that 5 is an ancestor of 9. There are several ways of
> doing this. Troels Arvin has a page with links to articles on the
> subject:
> http://troels.arvin.dk/db/rdbms/links/#hierarchical
> 1. Nested set: google for Celko and nested. There are also variants of
> this.
> 2. Store the path in each node. In your case something like:
> 5,'',"GREEN", "555-1212"
> 7,'5',"GREEN", "777-5555"
> 8,'5.7',"GREEN", "777-5555"
> 9,'5.7',"BLUE", "777-5555"
> In your case I dont think this is an option
> 3. Store a separate ancestor relation. In your case
> create table ancestor (
> nodeid int not null
> references tree,
> ancestorid int not null
> references tree,
> primary key (nodeid, ancestorid)
> );
> insert into ancestor values (7,5), (8,7), (8,5), (9,7), (9,5);
> Main drawback is that you have to maintain this relation as you
> modifies your tree. I have some notes on how that can be achieved:
> http://fungus.teststation.com/~jon/...reeHandling.htm
>
> Anyhow, once you have a way of asking for ancestors for a given node,
> you can do what you want in a single query, namely:
> find the ancestor at the largest depth with property p
> Assuming the following ddl
> create table tree (
> nodeid int not null primary key,
> parent int not null
> references tree
> on delete restrict
> );
> create table data (
> nodeid int not null primary key
> references tree
> on delete cascade,
> color varchar(10),
> phone varchar(10)
> );
> insert into tree values (5,5), (7,5), (8,7), (9,7);
> insert into data values (5, 'GREEN', '555-1212'), (7, NULL,
> '777-5555'), (8, NUL
> L, NULL), (9, 'BLUE', NULL);
> create table ancestor (
> nodeid int not null
> references tree,
> ancestorid int not null
> references tree,
> primary key (nodeid, ancestorid)
> );
> insert into ancestor values (7,5), (8,7), (8,5), (9,7), (9,5);
> You could use a query like:
> with suspects (nodeid, suspectid, depth) as (
> select
> a.nodeid, a.ancestorid,
> (select count(1) from ancestor where nodeid = a.ancestorid) as
> depth
> from ancestor a, data d
> where a.ancestorid = d.nodeid
> and d.color is not null
> union all
> select
> d.nodeid, d.nodeid,
> (select count(1) from ancestor where nodeid = d.nodeid) as
> depth
> from data d
> where d.color is not null
> and not exists (
> select 1 from ancestor where ancestorid = d.nodeid
> )
> )
> select s.nodeid, d.color from suspects s, data d
> where d.nodeid = s.suspectid
> and depth = (select max(depth) from suspects where nodeid =
> s.nodeid)
> NODEID COLOR
> ---- ----
> 7 GREEN
> 8 GREEN
> 9 BLUE
> 3 record(s) selected.
> As you can see node 5 is missing, but I'll leave that for you ;-)
>
> HTH
> /Lennart
>
> > - Jeff|||lennart@.kommunicera.umea.se (Lennart Jonsson) wrote in message news:<6dae7e65.0406022337.338870@.posting.google.com>...
> jlanfield2003@.yahoo.com (Jeff Lanfield) wrote in message news:<235c483f.0406021038.13a1b83b@.posting.google.com>...
[...]
Hmmm, I must have been just a little bit tired there ;-) Should read
(diff in snd part of union):
with suspects (nodeid, suspectid, depth) as (
select
a.nodeid, a.ancestorid,
(select count(1) from ancestor where nodeid = a.ancestorid)
as depth
from ancestor a, data d
where a.ancestorid = d.nodeid
and d.color is not null
union all
select
t.nodeid, t.nodeid,
(select count(1) from ancestor where nodeid = t.nodeid) as
depth
from data d, tree t
where d.nodeid = t.nodeid
and d.color is not null
)
select s.nodeid, d.color from suspects s, data d
where d.nodeid = s.suspectid
and depth = (select max(depth) from suspects where nodeid =
s.nodeid)
;
NODEID COLOR
---- ----
5 GREEN
7 GREEN
8 GREEN
9 BLUE
/Lennart|||jlanfield2003@.yahoo.com (Jeff Lanfield) wrote in message news:<235c483f.0406031837.62f2701@.posting.google.com>...
> Thanks Lennart. I think your third suggestion might do the trick. If
> you have a chance to answer I'm curious:
> 1) Why do you think storing the path (suggestion #2) is not an option?
> Or if it is option, why is it a bad idea compared to storing a
> separate ancestor relation? (aside from the usual normal form
> reasoning)
Since youre mainly concerned in retrieving ancestors, (not
investigating subtree
), I think it is a bit clumsy. Assume that you have a node x with path
1.2.3.56.89.112 and want to investigate which nodes in the path that
have property p. How would you do that? Subtree queries are much
easier since you can query: where path like '1.2.4.6.%'
> 2) How would you structure the query if you stored the path
> (suggestion #2).
I would figure out a way to retrive ancestors and then use the same
way.
If I where in your shoes I would encapsulate the ancestor stuff in
views or table functions, and then use those in my queries. If you
decide to go for another aproach, you rewrite the functions/views and
can continue to use the queries. Simple example using table functions
in db2:
-- ancestor table aproach
CREATE FUNCTION SUBTREE (ID INTEGER)
RETURNS TABLE (NODEID INTEGER)
LANGUAGE SQL
READS SQL DATA
NO EXTERNAL ACTION
DETERMINISTIC
RETURN
select nodeid from ancestor where ancestorid = ID
@.
CREATE FUNCTION SUBTREE_SELF (ID INTEGER)
RETURNS TABLE (NODEID INTEGER)
LANGUAGE SQL
READS SQL DATA
NO EXTERNAL ACTION
DETERMINISTIC
RETURN
select s.nodeid from table(subtree(ID)) as s
union all
values (ID)
@.
-- changed my mind decided to go for path aproach instead
drop ...
CREATE FUNCTION SUBTREE (ID INTEGER)
RETURNS TABLE (NODEID INTEGER)
LANGUAGE SQL
READS SQL DATA
NO EXTERNAL ACTION
DETERMINISTIC
RETURN
select nodeid from tree where path like (ID || '.%')
etc
The same goes for depth
As a bonus it will simplify your original query. I.e
with suspects (nodeid, ancestorid, depth) as (
select
ps.nodeid, ps.ancestorid, depth_func(ps.nodeid) as depth
from data d, tree t, table(path_self(t.nodeid)) ps
where ps.ancestorid = d.nodeid and d.color is not null
)
select ...
HTH
/Lennart
> Thanks!
> - Jeff
>
> lennart@.kommunicera.umea.se (Lennart Jonsson) wrote in message news:<6dae7e65.0406022337.338870@.posting.google.com>...
> > jlanfield2003@.yahoo.com (Jeff Lanfield) wrote in message news:<235c483f.0406021038.13a1b83b@.posting.google.com>...
> > [...]
> > > > So say the tree is specified like this:
> > > > (nodeId int, parentId int, color varchar, phone varchar)
> > > > 5, 0,"GREEN", "555-1212"
> > > 7, 5, NULL, "777-5555"
> > > 8, 7 NULL, NULL
> > > 9, 7 "BLUE", NULL
> > > > > That is: node 7 inherits values from 5. Nodes 8,9 inherit values from
> > > 7. Node 5 is the top level node.
> > > > I want to run a query that would give the following result set:
> > > > select nodeId, color, phone from ...
> > > > 5,"GREEN", "555-1212"
> > > 7,"GREEN", "777-5555"
> > > 8,"GREEN", "777-5555"
> > > 9,"BLUE", "777-5555"
> > > > Can such a query be constructed?
> > > If you dont have a maximum depth of your tree (and cannot use
> > recursion), then no. The reason is that you cannot express queries
> > like "gimmie the ancestors of x". What you need to do is to inform
> > your system that 5 is an ancestor of 9. There are several ways of
> > doing this. Troels Arvin has a page with links to articles on the
> > subject:
> > http://troels.arvin.dk/db/rdbms/links/#hierarchical
> > 1. Nested set: google for Celko and nested. There are also variants of
> > this.
> > 2. Store the path in each node. In your case something like:
> > 5,'',"GREEN", "555-1212"
> > 7,'5',"GREEN", "777-5555"
> > 8,'5.7',"GREEN", "777-5555"
> > 9,'5.7',"BLUE", "777-5555"
> > In your case I dont think this is an option
> > 3. Store a separate ancestor relation. In your case
> > create table ancestor (
> > nodeid int not null
> > references tree,
> > ancestorid int not null
> > references tree,
> > primary key (nodeid, ancestorid)
> > );
> > insert into ancestor values (7,5), (8,7), (8,5), (9,7), (9,5);
> > Main drawback is that you have to maintain this relation as you
> > modifies your tree. I have some notes on how that can be achieved:
> > http://fungus.teststation.com/~jon/...reeHandling.htm
> > Anyhow, once you have a way of asking for ancestors for a given node,
> > you can do what you want in a single query, namely:
> > find the ancestor at the largest depth with property p
> > Assuming the following ddl
> > create table tree (
> > nodeid int not null primary key,
> > parent int not null
> > references tree
> > on delete restrict
> > );
> > create table data (
> > nodeid int not null primary key
> > references tree
> > on delete cascade,
> > color varchar(10),
> > phone varchar(10)
> > );
> > insert into tree values (5,5), (7,5), (8,7), (9,7);
> > insert into data values (5, 'GREEN', '555-1212'), (7, NULL,
> > '777-5555'), (8, NUL
> > L, NULL), (9, 'BLUE', NULL);
> > create table ancestor (
> > nodeid int not null
> > references tree,
> > ancestorid int not null
> > references tree,
> > primary key (nodeid, ancestorid)
> > );
> > insert into ancestor values (7,5), (8,7), (8,5), (9,7), (9,5);
> > You could use a query like:
> > with suspects (nodeid, suspectid, depth) as (
> > select
> > a.nodeid, a.ancestorid,
> > (select count(1) from ancestor where nodeid = a.ancestorid) as
> > depth
> > from ancestor a, data d
> > where a.ancestorid = d.nodeid
> > and d.color is not null
> > union all
> > select
> > d.nodeid, d.nodeid,
> > (select count(1) from ancestor where nodeid = d.nodeid) as
> > depth
> > from data d
> > where d.color is not null
> > and not exists (
> > select 1 from ancestor where ancestorid = d.nodeid
> > )
> > )
> > select s.nodeid, d.color from suspects s, data d
> > where d.nodeid = s.suspectid
> > and depth = (select max(depth) from suspects where nodeid =
> > s.nodeid)
> > NODEID COLOR
> > ---- ----
> > 7 GREEN
> > 8 GREEN
> > 9 BLUE
> > 3 record(s) selected.
> > As you can see node 5 is missing, but I'll leave that for you ;-)
> > HTH
> > /Lennart
> > > - Jeff|||e.g. in Oracle 9i,
you can:
Creating demo data
CREATE TABLE T
(
CHILD_ID NUMBER,
PARENT_ID NUMBER,
ATTR1 VARCHAR2(10),
ATTR2 VARCHAR2(10)
)
INSERT INTO T VALUES (1 NULL,'A','000');
INSERT INTO T VALUES (2,1, NULL, '111');
INSERT INTO T VALUES (3,1, 'B', NULL);
INSERT INTO T VALUES (4,2, NULL, NULL);
INSERT INTO T VALUES (5, 2, 'C', '999');
INSERT INTO T VALUES (6, 5, NULL, NULL);
and the query is (it is too ugly with INSTR/SUBSTR, but maybe faster
then an inline query per each attribute with another CONNECT BY):
------
SELECT child_id,PARENT_ID,attr1,attr2,tree_path,
RTRIM(SUBSTR(all_attr1,INSTR(all_attr1,'/',-1,2)+1),'/')
inherit_attr1,
RTRIM(SUBSTR(all_attr2,INSTR(all_attr2,'/',-1,2)+1),'/')
inherit_attr2
FROM
(SELECT child_id,parent_id,attr1,attr2,
SYS_CONNECT_BY_PATH(TO_CHAR(child_id), '/') tree_path,
CASE
WHEN attr1 IS NOT NULL THEN '/'||attr1||'/'
ELSE REPLACE('/'||SYS_CONNECT_BY_PATH(attr1,'/'),'//','/')
END all_attr1,
CASE
WHEN attr2 IS NOT NULL THEN '/'||attr2||'/'
ELSE REPLACE('/'||SYS_CONNECT_BY_PATH(attr2,'/'),'//','/')
END all_attr2
FROM T
START WITH parent_id IS NULL
CONNECT BY parent_id=PRIOR child_id) v
------------------
jlanfield2003@.yahoo.com (Jeff Lanfield) wrote in message news:<235c483f.0406011716.37d00399@.posting.google.com>...
> First of all, I apologize if coalescing is not the right term to
> describe my problem. I have a tree where each node has the same set of
> attributes (is the same entity) but child nodes should inherit
> attribute values from parent node.
> for example, say I have the following table:
> (nodeId int , color varchar, phone varchar) with two rows
> 5, "GREEN", "555-1212"
> 7, NULL, "777-5555"
> 8, NULL, NULL
> 9, "BLUE", NULL
> in addition there is a tree structure that specifies that node 5 is
> the parent of node 7, and 7 is the parent of nodes 8 and 9. I know
> there is many ways to make trees in SQL but as a simple example let's
> say the tree is:
> id, parentid
> 8, 7
> 9, 7
> 7, 5
> Thus in this case, node 7 inherits the value "GREEN" from node 5 for
> attribute "color", but provides its own value "777-5555" for attribute
> "phone". Node 8, in turn, inherits "GREEN" for "color" from node 7
> (really from node 5 since 7 did not specify its own) and "777-5555"
> for "phone" from node 7. Node 9 provides its own value for "color" and
> inherits the one for "phone" from Node 7.
> So the runtime values in the application are:
> Node 5: "GREEN", "555-1212"
> Node 7: "GREEN", "777-5555"
> Node 8: "GREEN", "777-5555"
> Node 9: "BLUE", "777-5555"
> Question 1: Is there a single SQL statement that for a given node can
> replace the NULLs with inherited values from the parent node?
> Question 2: Is there a better way to structure such data in SQL as to
> make answer to question 1 possible?
> I would restate the problem as follows:
> In a nested structure child nodes inherit values from parent nodes _by
> reference_ or specify their own. "By reference" is the key word here.
> If it wasn't for that you could just duplicate the necessary values
> from the parent entitity upon creation.
> Thanks!
> - Jeff|||"Lennart Jonsson" <lennart@.kommunicera.umea.se> wrote in message
news:6dae7e65.0406032300.77888608@.posting.google.c om...
> jlanfield2003@.yahoo.com (Jeff Lanfield) wrote in message
news:<235c483f.0406031837.62f2701@.posting.google.com>...
> Since youre mainly concerned in retrieving ancestors, (not
> investigating subtree
> ), I think it is a bit clumsy. Assume that you have a node x with path
> 1.2.3.56.89.112 and want to investigate which nodes in the path that
> have property p. How would you do that? Subtree queries are much
> easier since you can query: where path like '1.2.4.6.%'
Small procedural part -- table function -- which returns ancestors
materialized path encodings set
1.2.3.56.89
1.2.3.56
1.2.3
1.2
1
for the input encoding 1.2.3.56.89.112 could be plugged in into the query
returning all the ancestors. This idea translated into the nested intervals
encoding (if you prefer to work with numbers instead of string parsing) is
implemented at the end of
http://arxiv.org/html/cs.DB/0401014
BTW, http://arxiv.org/abs/cs.DB/0402051 is significantly rewritten.
Essentially, it is Euclidean algorithm (the mother of all algorithms?) that
is employed in the numerical counterpart of the Materialized Path.|||lennart@.kommunicera.umea.se (Lennart Jonsson) wrote in message news:<6dae7e65.0406031939.1ed5fa49@.posting.google.com>...
> lennart@.kommunicera.umea.se (Lennart Jonsson) wrote in message news:<6dae7e65.0406022337.338870@.posting.google.com>...
> > jlanfield2003@.yahoo.com (Jeff Lanfield) wrote in message news:<235c483f.0406021038.13a1b83b@.posting.google.com>...
> [...]
> Hmmm, I must have been just a little bit tired there ;-) Should read
> (diff in snd part of union):
and as you probably discovered already, it can be further simplified as
[...]
union all
select
d.nodeid, d.nodeid,
(select count(1) from ancestor where nodeid = d.nodeid) as
depth
from data d
where d.color is not null
)
[...]
/L|||>> if an entity instance (one row) does not specify a value for one of
its field [sic]s it should inherit the values from its nearest parent
that does have a value for this field [sic]. <<
Rows are not records; fields are not columns; tables are not files.
It drives me nuts to see people screw up the terms and therefore their
mental model of how SQL works. Let's put this into a simplified
nested set model which has more NULL-able columns than the payroll
system of a major auto company:
CREATE TABLE Nodes
(node_id INTEGER NOT NULL PRIMARY KEY,
col_1 CHAR(5),
col_2 CHAR(5),
..);
CREATE TABLE Tree
(node_id INTEGER NOT NULL UNIQUE
REFERENCES Nodes(node_id),
lft INTEGER NOT NULL,
rgt INTEGER NOT NULL,
PRIMARY KEY (lft, rgt),
<< other tree constraints here >>
..);
Now update all the rows where any column has a NULL. You will have to
run this update once for every level in the tree at most.
UPDATE Nodes
SET col_1
= COALESCE(Nodes.col_1, -- keep non-nul
(SELECT B.col_1 -- find parent
FROM Tree AS E
LEFT OUTER JOIN
Tree AS B
ON B.lft
= (SELECT MAX(lft)
FROM Tree AS S
WHERE E.lft > S.lft
AND E.lft < S.rgt)),
col_2
= COALESCE(Nodes.col_2,
(SELECT B.col_2
FROM ..)),
Etc.
WHERE (col_1 || col_2 || ..||col_n) IS NULL;
This probably scared you. The WHERE clause uses the fact that NULLs
propagate; we don't care *which* column is NULL, so why look for
useless details? That left outer join scalar subquery is how to get
the immediate superiors (B= boss, E= employee) in a hierarchy. The
COALESCE() will retain an existing non-NULL value.