How can I group children and parents in a single query? - sql

I have a table that represents a simple parent->child hierarchy, something like:
Table "public.transactions"
Column | Type | Modifiers
-----------+---------------+-----------------------------------------------------------
id | integer | not null default nextval('transactions_id_seq'::regclass)
parent_id | integer | not null default 0
amount | numeric(15,4) | not null default 0.0000
I'd like to display the table with child transactions (those with a parent_id > 0) grouped below their respective parents. e.g.,
parent
child
child
parent
child
parent
parent
(note: nested spaces are only here to visually represent hierarchy, they're not needed for the query results)
Can I do this in a single query? I'm running Postgresql 9.3 in case it matters.

For a single level of nesting, this may seem almost trivial:
SELECT *
FROM transactions
ORDER BY COALESCE(parent_id, id), id

Related

SQL query to select nodes with no parent

Suppose I've got a table node with two fields : id, and child_id :
id | name | child_id |
----------------------------
1 | node1 | NULL |
2 | node2 | 3 |
3 | node3 | NULL |
It means that "node1" has no parent and no children, "node2" has no parent and child "node2", and "node2" has parent "node2" and no children.
Now I want to select all "node" that have no parent.
In the example above I should get rows :
id | name | child_id |
----------------------------
1 | node1 | NULL |
2 | node2 | 3 |
How would you implement this query ?
A traditional anti-join will produce the rows you want. For example:
select c.*
from node c
left join node p on c.id = p.child_id
where p.id is null
I had to think about this, because it has the parent-child-relationships in opposite direction compared to traditional tree structures (where a node has a reference to its parent, not its child).
If you want all nodes without parents in your data structure, you want all nodes whose id is not used as a child id in other nodes. Right? (Because such other nodes would be the parents of that node.)
That could result in a query like this:
select *
from node
where id not in (select child_id
from node
where child_id is not null)
(Note that you have to be careful with three-valued logic in SQL, which could mess up a NOT IN clause. That's why I included an explicit WHERE-clause in the subquery so that that subquery only includes "valid" rows.)
By the way, the alternative query in the answer of The Impaler (which uses a join instead of a subquery) is quite fine as well. I'm not sure, but that query might even be (marginally) more efficient. However, I guess that a subquery expresses the intention of the main query more clearly, making it somewhat easier to read and to maintain. So I would personally stick with the subquery-query at first and move to the join-query only if that turns out to have a substantial and relevant performance benefit. But that's just my own humble opinion, of course. (And in such a case, I would keep the subquery-query as a comment in the code/script for documentation purposes.)

How to get all siblings in SQL, know one child id I want to get all rows that have the same parent id

I have a "parent sector" that have "children sectors". This is done in practice using a table sector that have and id_sector and id_parent columns.
I can make a self join as expected. My situation is as follow I have an object that represents a "child sector".
I want to write a query that returns all children sectors with the same parent as of that object id_sector. Is there a simple way to do this, desirably in the join. I am using an ORM and a complex query wouldn't help much because I would have to translate it to suite the ORM.
sector object
id_sector id_sector
id_parent name
name
The object has a sector id
We can use a sub-query to find the parent secteur of our record. We then return all secteurs having the same parent secteur.
create table t(id_secteur int, id_parent int);
insert into t values(1,1),(2,1),(3,1),(4,2);
select * from t where id_parent = (select id_parent from t where id_secteur = 1);
id_secteur | id_parent
---------: | --------:
1 | 1
2 | 1
3 | 1
db<>fiddle here

How to concatenate field values with recursive query in postgresql?

I have a table in PostgreSQL database that contains parts of addresses in a form of a tree and looks like this:
Id | Name | ParentId
1 | London | 0
2 | Hallam Street| 1
3 | Bld 26 | 2
4 | Office 5 | 3
I would like to make a query to return an address, concatenated from all ancestor names. I need the result table to be like this:
Id | Address
1 | London
2 | London, Hallam Street
3 | London, Hallam Street, Bld 26
4 | London, Hallam Street, Bld 26, Office 5
I guess I have to use WITH RECURSIVE query, but all the examples I've found use the where clause, so I have to put WHERE name='Office 5' to get the result only for that particular row. But I need a concatenated address for each row of my initial table. How can this be done?
The trick with recursive queries is that you need to specify a seed query. This is the query that determines your root node, or the starting point to descend or ascend the tree that you are building.
The reason the WHERE clause is there is to establish the seed ID=1 or Name=Bld 26. If you want every record to have the tree ascended or descended (depending on what you specify in the unioned select), then you should just scrap the WHERE statement so all records are seeded.
Although, the example you give... you might want to start with WHERE ID=1 in the seed, write out the child ID and parent ID. Then in the Union'd SELECT join your derived Recursive table with your table from which you are selecting and join on the Derived Recursive table's Child to your table's parent.
Something like:
WITH RECURSIVE my_tree AS (
-- Seed
SELECT
ID as Child,
ParentID as Parent,
Name,
Name as Address
FROM <table>
WHERE <table>.ID = 1
UNION
-- Recursive Term
SELECT
table.id as Child,
table.parent_id as Parent,
table.name,
t.address || ', ' || table.name as Address
FROM my_tree as t
INNER JOIN <table> table ON
t.Child = table.Parent_Id
)
SELECT Child, Address from my_tree;
I've not used PostgreSQL before, so you might have to fuss a bit with the syntax, but I think this is pretty accurate for that RDBMS.

Simple SQL statement with parent/child hierarchies

It's been a while since I've needed to write SQL statements (and I don't even know if ever had enough knowledge to make this statement).
So, here's the deal. Table has two column. One is for parent id, other is for child Id.
parent_id | child_id
4 | 2
2 | 5
This is simply for saving composite parent/child hierarchies.
4, 2 line means that structure with id 4 refers to structure id 2 as a child.
2, 5 means structure with id 2 refers to structure with id 5 as a child.
And so on.
This is what I need to do:
I need to extract ALL structures, that are not referenced by any structure as a child (root structures).
What SQL (preferrably postgres) statement will accomplish that?
Finding all structures that are not a child of another structure:
select *
from YourTable
where Parent_Id not in (Select child_id from ...)
Assuming there is no scope for a grandparent, great-grandparent relationsips, I would recommend use a Left-JOIN in this case.
Somethink on the lines of:
Select * from Table
LEFt join Table on Parent_id=child_id
WHERE child_id is null
SELECT *
FROM structures
WHERE id not in ( SELECT child_id FROM Table ) AS dummy

Selecting only rows containing at least the given elements in SQL

Using PostgreSQL, and given the following sample table, how do I select all parents that have at least a child 10 and a child 20?
parent | child
--------+-------
1 | 10
1 | 20
1 | 30
2 | 10
2 | 20
3 | 10
In other words, this is the expected result:
parent
--------
1
2
In general, how do I select all parents that have at least all of the given children x1, x2, ..., xn? What is the most efficient way to do this?
Thanks!
SELECT parent FROM table WHERE child IN(10,20)
GROUP BY parent
HAVING COUNT(DISTINCT child)>=2
Fiddle
It's not completely clear what your asking. However, I shall give it a crack.
If you're going to manually define the children you can do a simple select statement:
SELECT DISTINCT parent
FROM table1
WHERE child IN ('10', '20')
This would select all Parents that have 10 or 20 as there child. To add more, just add the number to the IN() part.
If however you want to do this for a large number of children or perhaps an unknown number of children then you can create a temp table to store the children search values and join it to your main table. Something like:
CREATE TABLE #SearchChildren
(
Child int
)
Then input your search values into #SearchChildren. Need to know more about what your doing to do this bit.
SELECT DISTINCT a.parent
FROM table1 as a
JOIN #SearchChildren as s
ON a.child = s.Child
Without knowing more about what your trying to do it's difficult to give a full answer but hopefully this helps.