Is it possible to have a single sql query return a nested structure rather than doing recursive db calls to build up the array or object?
I'm using something similar to this pseudo code to build up :
parentCategory = 'SELECT *
FROM Category
WHERE child_category IS NULL
AND ParentIDNo IS NULL';
while parentCategory do
childCategory = 'SELECT *
FROM Category
WHERE parent_id = parentCategory.id';
if (parentCategory.id)
do recursive 'SELECT *
FROM Category
WHERE parent_id = parentCategory.id';
end
Cat_1
-child_1
-child_2
--grandchild_1
Cat_2
-child_1
-child_2
--grandchild_1
Check out recursive ctes assumes sql 2005 or later
If you modify your table to contain a delimited path to the top of the tree then you don't have to do this recursively.
For example if you had the tree path be "cat_1|child_2|grandchild_1" for the grandchild_1 node of the tree then you would be able to split the string for loading into a tree as well as be able to determine the level in the tree you were at. Also when you select from the table you would be able to order by the path and the tree would come out exactly as you wanted to draw it.
the negative is that you would have to maintain this path on any changes to the tree.
That's very possible if you are using SQL 2005 and up. For a primer on hierarchical query see this: http://www.ienablemuch.com/2010/04/simple-hierarchical-query-display.html
Related
I've been trying for days now to retrieve data from an XML file with a SELECT statement in SQL Developer but I constantly get the 'ORA-00904' when trying to execute the statement. So far, these are the steps I've been following:
Create the table and directory where I want the XML data to be stored in:
CREATE TABLE PLAYER OF XMLTYPE; / CREATE DIRECTORY PLDIR AS 'C:\Users\marta\OneDrive\Escritorio\UOC\Sem3\ABD\PR2'; /
Insert into my PLAYER table said data:
INSERT INTO PLAYER VALUES (XMLTYPE(bfilename('PLDIR', 'InfoPlayersWPT.xml'),nls_charset_id('AL32UTF8')))
/
So far so good. The issue appears when I try to execute the SELECT statement
What could it be? I've changed the $Name parameter a million times as well as the Name field but nothing changes. The thing is that in the XML file, these are the fields:
--Update--
I've modified a little bit the structure and this is the new error I get:
enter image description here
I've reached a point where I don't get if there could be a problem with my database connection or if the variable are incorrect.
Any form of help would be much appreciated.
Your table doesn't have a name or id column. Your query is trying to get those, while also transforming the XML to an info node making the id a node rather than an attribute, but you still don't extract the values from that new XML. You don't need to though.
If the document only has one player (which seems unlikely with the outer node 'Players') then you can get the data with an XMLQuery call for each value:
select XMLQuery('/Players/Player/#id' passing p.object_value returning content) as id,
XMLQuery('/Players/Player/Name/text()' passing p.object_value returning content) as name
from player p
ID
NAME
1
Francinsco Navarro Compán
But it's a bit simpler, particularly if you add more data items, to use a single XMLTable instead:
select x.id, x.name
from player p
cross apply XMLTable(
'/Players/Player'
passing p.object_value
columns id number path '#id',
name varchar2(30) path 'Name'
) x
ID
NAME
1
Francinsco Navarro Compán
fiddle
... which would also handle multiple players in each XML document.
I have a table similar to this:
CREATE TABLE example (
id integer primary key,
name char(200),
parentid integer,
value integer);
I can use the parentid field to arrange data into a tree structure.
Now here's the bit I can't work out. Given a parentid, is it possible to write an SQL statement to add up all the value fields under that parentid and recurse down the branch of the tree ?
UPDATE: I'm using posgreSQL so the fancy MS-SQL features are not available to me. In any case, I'd like this to be treated as a generic SQL question.
BTW, I'm very impressed to have 6 answers within 15 minutes of asking the question! Go stack overflow!
Here is an example script using common table expression:
with recursive sumthis(id, val) as (
select id, value
from example
where id = :selectedid
union all
select C.id, C.value
from sumthis P
inner join example C on P.id = C.parentid
)
select sum(val) from sumthis
The script above creates a 'virtual' table called sumthis that has columns id and val. It is defined as the result of two selects merged with union all.
First select gets the root (where id = :selectedid).
Second select follows the children of the previous results iteratively until there is nothing to return.
The end result can then be processed like a normal table. In this case the val column is summed.
Since version 8.4, PostgreSQL has recursive query support for common table expressions using the SQL standard WITH syntax.
If you want a portable solution that will work on any ANSI SQL-92 RDBMS, you will need to add a new column to your table.
Joe Celko is the original author of the Nested Sets approach to storing hierarchies in SQL. You can Google "nested sets" hierarchy to understand more about the background.
Or you can just rename parentid to leftid and add a rightid.
Here is my attempt to summarize Nested Sets, which will fall woefully short because I'm no Joe Celko: SQL is a set-based language, and the adjacency model (storing parent ID) is NOT a set-based representation of a hierarchy. Therefore there is no pure set-based method to query an adjacency schema.
However, most of the major platforms have introduced extensions in recent years to deal with this precise problem. So if someone replies with a Postgres-specific solution, use that by all means.
There are a few ways to do what you need in PostgreSQL.
If you can install modules, look at the tablefunc contrib. It has a connectby() function that handles traversing trees. http://www.postgresql.org/docs/8.3/interactive/tablefunc.html
Also check out the ltree contrib, which you could adapt your table to use: http://www.postgresql.org/docs/8.3/interactive/ltree.html
Or you can traverse the tree yourself with a PL/PGSQL function.
Something like this:
create or replace function example_subtree (integer)
returns setof example as
'declare results record;
child record;
begin
select into results * from example where parent_id = $1;
if found then
return next results;
for child in select id from example
where parent_id = $1
loop
for temp in select * from example_subtree(child.id)
loop
return next temp;
end loop;
end loop;
end if;
return null;
end;' language 'plpgsql';
select sum(value) as value_sum
from example_subtree(1234);
A standard way to make a recursive query in SQL are recursive CTE. PostgreSQL supports them since 8.4.
In earlier versions, you can write a recursive set-returning function:
CREATE FUNCTION fn_hierarchy (parent INT)
RETURNS SETOF example
AS
$$
SELECT example
FROM example
WHERE id = $1
UNION ALL
SELECT fn_hierarchy(id)
FROM example
WHERE parentid = $1
$$
LANGUAGE 'sql';
SELECT *
FROM fn_hierarchy(1)
See this article:
Hierarchical queries in PostgreSQL
If your using SQL Server 2005, there is a really cool way to do this using Common Table Expressions.
It takes all of the gruntwork out of creating a temporary table, and basicly allows you to do it all with just a WITH and a UNION.
Here is a good tutorial:
http://searchwindevelopment.techtarget.com/tip/0,289483,sid8_gci1278207,00.html
use a common table expression.
May want to indicate this is SQL Server 2005 or above only. Dale Ragan
here's an article on recursion by SqlTeam without common table expressions.
The following code compiles and it's tested OK.
create or replace function subtree (bigint)
returns setof example as $$
declare
results record;
entry record;
recs record;
begin
select into results * from example where parent = $1;
if found then
for entry in select child from example where parent = $1 and child parent loop
for recs in select * from subtree(entry.child) loop
return next recs;
end loop;
end loop;
end if;
return next results;
end;
$$ language 'plpgsql';
The condition "child <> parent" is needed in my case because nodes point to themselves.
Have fun :)
Oracle has "START WITH" and "CONNECT BY"
select
lpad(' ',2*(level-1)) || to_char(child) s
from
test_connect_by
start with parent is null
connect by prior child = parent;
http://www.adp-gmbh.ch/ora/sql/connect_by.html
Just as a brief aside although the question has been answered very well, it should be noted that if we treat this as a:
generic SQL question
then the SQL implementation is fairly straight-forward, as SQL'99 allows linear recursion in the specification (although I believe no RDBMSs implement the standard fully) through the WITH RECURSIVE statement. So from a theoretical perspective we can do this right now.
None of the examples worked OK for me so I've fixed it like this:
declare
results record;
entry record;
recs record;
begin
for results in select * from project where pid = $1 loop
return next results;
for recs in select * from project_subtree(results.id) loop
return next recs;
end loop;
end loop;
return;
end;
is this SQL Server? Couldn't you write a TSQL stored procedure that loops through and unions the results together?
I am also interested if there is a SQL-only way of doing this though. From the bits I remember from my geographic databases class, there should be.
I think it is easier in SQL 2008 with HierarchyID
If you need to store arbitrary graphs, not just hierarchies, you could push Postgres to the side and try a graph database such as AllegroGraph:
Everything in the graph database is stored as a triple (source node, edge, target node) and it gives you first class support for manipulating the graph structure and querying it using a SQL like language.
It doesn't integrate well with something like Hibernate or Django ORM but if you are serious about graph structures (not just hierarchies like the Nested Set model gives you) check it out.
I also believe Oracle has finally added a support for real Graphs in their latest products, but I'm amazed it's taken so long, lots of problems could benefit from this model.
Without using dynamic SQL, and other than an If ELSE is there any way to test if a table has a full text index without causing additional locking.
I'm looking for some way that I can still execute:
SELECT * FROM Cars WHERE <not has full text index> OR (<has full text index> AND CONTAINS(*, 'fast'))
There is no full-text index on this the table Cars ideally I would like for it to just return all rows when we try and specify the contains string.
What I have currently from SQL is:
SELECT * FROM Cars
WHERE NOT EXISTS (SELECT 1 FROM sys.columns WHERE object_id = object_id('Cars') AND COLUMNPROPERTY(object_id, name, 'IsFulltextIndexed') = 1)
OR (EXISTS (SELECT 1 FROM sys.columns WHERE object_id = object_id('Cars') AND COLUMNPROPERTY(object_id, name, 'IsFulltextIndexed') = 1)
AND CONTAINS(*, 'fast'))
Which parses fine but fails because:
Msg 7601, Level 16, State 1, Line 3 Cannot use a CONTAINS or FREETEXT
predicate on table or indexed view 'Cars' because it is not full-text
indexed.
Is there a way to do this without dynamic SQL and without using an IF ELSE?
Bonus points if you can explain why the optimizer doesn't short-circuit the 1=0 and CONTAINS from SELECT * FROM Cars WHERE 1=1 OR (1=0 AND CONTAINS(*, 'fast')).
In practice I will likely create a new class attribute and then use that to ignore the full text indexes in my ORM, but I am interested to see if there is another option.
No.
All single queries are compiled into a single plan, and so all the individual components must always be valid. It is not possible to write a single statement where different components are valid in different circumstances.
To do so require more traditional programatic constructs. These are available in T-SQL, but not within a single SQL statement. As such, the solutions you have listed as innapropriate are actually the ways to resolve such issues.
Using: SQL Server 2008, WCF 4 REST, EF
I have an adjacency list table representing a tree
TABLE Category
(
CatId int IDENTITY(1,1) NOT NULL PRIMARY KEY,
ParentId int NULL,
Name nvarchar(50) NOT NULL
)
I am creating a WCF REST API to allow the client to build the tree in a lazy load way. Doing the query to get the children of a node (nodeid below) is straight-forward. What i'm running into is the need to identify which nodes are leaf nodes. (Note: I've removed all error handling, count = 0 handling, null handling, etc. from the code below)
tree = _context.Categories
.Where(c => c.ParentId == nodeid)
.Select(p => new TreeNode
{
id = p.CatId,
parentId = p.ParentId ?? -1, // -1 = NULL in data struct
name = p.Name,
isleaf = true // how to figure this out?
}).ToList();
Any ideas? I'm OK going to a stored proc for this query and have thought of using CTE, but i don't want to recurse through the entire tree - just get the children of the specified node.
EDIT (20 Jan, 10:40am)
I've decided to alter the DB Schema to add the "IsLeaf" bit column. I then did an update to set the IsLeaf accordingly - which means i don't have to dynamically figure it out at runtime. Probably more efficient, but I'm still curious how i would go about it. Please advise.
There is no recursive functions in LINQ (yet?). So while it's somewhat easy in MSSQL, in LINQ I'd suggest doing the following (pseudocode):
// These two lines is one LINQ statement
level = select all root nodes (ParentID == NULL)
add level nodes to result
// C# loop
while level is not empty
// The loop body except for the assignment in the end is one LINQ statement
next = select all nodes that have parentId in level
for each node in next
find parent node
add to result with updated parent info
// C# assignment
level = next
As you may see it's a combination of LINQ and C#. Also it may worth bringing the content of the table locally before doing all this.
Also you may need to update the algo to check for cycles if necessary.
I have a table similar to this:
CREATE TABLE example (
id integer primary key,
name char(200),
parentid integer,
value integer);
I can use the parentid field to arrange data into a tree structure.
Now here's the bit I can't work out. Given a parentid, is it possible to write an SQL statement to add up all the value fields under that parentid and recurse down the branch of the tree ?
UPDATE: I'm using posgreSQL so the fancy MS-SQL features are not available to me. In any case, I'd like this to be treated as a generic SQL question.
BTW, I'm very impressed to have 6 answers within 15 minutes of asking the question! Go stack overflow!
Here is an example script using common table expression:
with recursive sumthis(id, val) as (
select id, value
from example
where id = :selectedid
union all
select C.id, C.value
from sumthis P
inner join example C on P.id = C.parentid
)
select sum(val) from sumthis
The script above creates a 'virtual' table called sumthis that has columns id and val. It is defined as the result of two selects merged with union all.
First select gets the root (where id = :selectedid).
Second select follows the children of the previous results iteratively until there is nothing to return.
The end result can then be processed like a normal table. In this case the val column is summed.
Since version 8.4, PostgreSQL has recursive query support for common table expressions using the SQL standard WITH syntax.
If you want a portable solution that will work on any ANSI SQL-92 RDBMS, you will need to add a new column to your table.
Joe Celko is the original author of the Nested Sets approach to storing hierarchies in SQL. You can Google "nested sets" hierarchy to understand more about the background.
Or you can just rename parentid to leftid and add a rightid.
Here is my attempt to summarize Nested Sets, which will fall woefully short because I'm no Joe Celko: SQL is a set-based language, and the adjacency model (storing parent ID) is NOT a set-based representation of a hierarchy. Therefore there is no pure set-based method to query an adjacency schema.
However, most of the major platforms have introduced extensions in recent years to deal with this precise problem. So if someone replies with a Postgres-specific solution, use that by all means.
There are a few ways to do what you need in PostgreSQL.
If you can install modules, look at the tablefunc contrib. It has a connectby() function that handles traversing trees. http://www.postgresql.org/docs/8.3/interactive/tablefunc.html
Also check out the ltree contrib, which you could adapt your table to use: http://www.postgresql.org/docs/8.3/interactive/ltree.html
Or you can traverse the tree yourself with a PL/PGSQL function.
Something like this:
create or replace function example_subtree (integer)
returns setof example as
'declare results record;
child record;
begin
select into results * from example where parent_id = $1;
if found then
return next results;
for child in select id from example
where parent_id = $1
loop
for temp in select * from example_subtree(child.id)
loop
return next temp;
end loop;
end loop;
end if;
return null;
end;' language 'plpgsql';
select sum(value) as value_sum
from example_subtree(1234);
A standard way to make a recursive query in SQL are recursive CTE. PostgreSQL supports them since 8.4.
In earlier versions, you can write a recursive set-returning function:
CREATE FUNCTION fn_hierarchy (parent INT)
RETURNS SETOF example
AS
$$
SELECT example
FROM example
WHERE id = $1
UNION ALL
SELECT fn_hierarchy(id)
FROM example
WHERE parentid = $1
$$
LANGUAGE 'sql';
SELECT *
FROM fn_hierarchy(1)
See this article:
Hierarchical queries in PostgreSQL
If your using SQL Server 2005, there is a really cool way to do this using Common Table Expressions.
It takes all of the gruntwork out of creating a temporary table, and basicly allows you to do it all with just a WITH and a UNION.
Here is a good tutorial:
http://searchwindevelopment.techtarget.com/tip/0,289483,sid8_gci1278207,00.html
use a common table expression.
May want to indicate this is SQL Server 2005 or above only. Dale Ragan
here's an article on recursion by SqlTeam without common table expressions.
The following code compiles and it's tested OK.
create or replace function subtree (bigint)
returns setof example as $$
declare
results record;
entry record;
recs record;
begin
select into results * from example where parent = $1;
if found then
for entry in select child from example where parent = $1 and child parent loop
for recs in select * from subtree(entry.child) loop
return next recs;
end loop;
end loop;
end if;
return next results;
end;
$$ language 'plpgsql';
The condition "child <> parent" is needed in my case because nodes point to themselves.
Have fun :)
Oracle has "START WITH" and "CONNECT BY"
select
lpad(' ',2*(level-1)) || to_char(child) s
from
test_connect_by
start with parent is null
connect by prior child = parent;
http://www.adp-gmbh.ch/ora/sql/connect_by.html
Just as a brief aside although the question has been answered very well, it should be noted that if we treat this as a:
generic SQL question
then the SQL implementation is fairly straight-forward, as SQL'99 allows linear recursion in the specification (although I believe no RDBMSs implement the standard fully) through the WITH RECURSIVE statement. So from a theoretical perspective we can do this right now.
None of the examples worked OK for me so I've fixed it like this:
declare
results record;
entry record;
recs record;
begin
for results in select * from project where pid = $1 loop
return next results;
for recs in select * from project_subtree(results.id) loop
return next recs;
end loop;
end loop;
return;
end;
is this SQL Server? Couldn't you write a TSQL stored procedure that loops through and unions the results together?
I am also interested if there is a SQL-only way of doing this though. From the bits I remember from my geographic databases class, there should be.
I think it is easier in SQL 2008 with HierarchyID
If you need to store arbitrary graphs, not just hierarchies, you could push Postgres to the side and try a graph database such as AllegroGraph:
Everything in the graph database is stored as a triple (source node, edge, target node) and it gives you first class support for manipulating the graph structure and querying it using a SQL like language.
It doesn't integrate well with something like Hibernate or Django ORM but if you are serious about graph structures (not just hierarchies like the Nested Set model gives you) check it out.
I also believe Oracle has finally added a support for real Graphs in their latest products, but I'm amazed it's taken so long, lots of problems could benefit from this model.