pl/sql hierachical data arrangement - sql

Table tMain:
+----+-------+---------+
| ID | name | id_ref |
+----+-------+---------+
| 1 | amine | 4 |
| .. | | |
+----+-------+---------+
Table tTree:
+----+--------+-----------+
| ID | name | id_parent |
+----+--------+-----------+
| 1 | root | null |
| 2 | child1 | 1 |
| 3 | child2 | 2 |
| 4 | child3 | 3 |
+----+--------+-----------+
So I have to tables which i would like to "connect". Table "tMain" contains some data and a foreign key referencing the tTree table (reference to the lowest child item). What I would like is to use one query to get all the data from tMain together with the assembled path from tTree. It would look like this:
+----+-------+--------+---------------------------+
| ID | name | id_ref | Path |
+----+-------+--------+---------------------------+
| 1 | amine | 4 | root/child1/child2/child3 |
| .. | | | |
+----+-------+--------+---------------------------+

WITH tree$ AS (
SELECT T.id, ltrim(sys_connect_by_path(name, '/'), '/') AS path
FROM tTree T
START WITH id_parent IS NULL
CONNECT BY PRIOR ID = id_parent
)
SELECT T.id, M.name, M.id_ref, T.path
FROM tMain M
JOIN tree$ T ON T.id = M.id_ref
;

Related

Sqlite select from multiple tables

I have five tables such as
Base Table
| group_id | group_name |
|----------|-------------|
| 1 | gn1 |
| 2 | gn2 |
| 3 | gn3 |
"Tags" Table
| tag_id | tag_name |
|--------|----------|
| 1 | tgn1 |
| 2 | tgn2 |
| 3 | tgn3 |
"Theme" Table
| theme_id | theme_name |
|----------|------------|
| 1 | thn1 |
| 2 | thn2 |
| 3 | thn3 |
"Tags" Mapping Table
| rec_id | group_id | tag_id |
|--------|----------|--------|
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 2 | 1 |
"Theme" Mapping Table
| rec_id | group_id | theme_id |
|--------|----------|----------|
| 1 | 1 | 2 |
| 2 | 2 | 3 |
| 3 | 2 | 1 |
I am having some trouble creating a SQLite query to get a table like this:
| group_id | group_name | tags | themes |
|----------|------------|------------|------------|
| 1 | gn1 | tgn2, tgn3 | thn2 |
| 2 | gn2 | tgn1 | thn3, thn1 |
| 3 | gn3 | | |
The group_concat function will do the trick - join all the tables, group by the group id and name, and group_concat the other details:
SELECT g.group_id,
g.group_name,
GROUP_CONCAT(t.tag_name, ', ') AS tags
GROUP_CONCAT(th.theme_name, ', ') AS theme
FROM groups g
JOIN tags_map tg ON g.group_id = tm.group_id
JOIN tags t ON t.tag_id = tm.tag_id
JOIN themes_map thm ON g.group_id = thm.group_id
JOIN themes the ON th.theme_id = thm.theme_id
GROUP BY g.group_id, g.group_name
A pretty simple method uses correlated subqueries:
select b.*,
(select group_concat(t.tag_name)
from tag_mapping tm join
tags t
on tm.tag_id = t.tag_id
where tm.group_id = b.group_id
) as tags,
(select group_concat(t.theme_name)
from theme_mapping tm join
themes t
on tm.theme_id = t.theme_id
where tm.group_id = b.group_id
) as themes
from base b;

T-SQL Query to Pull a Parent's Tags and all Children's Tags

Backgroud
I have a DB Schema in SQL Server that looks like the following:
http://sqlfiddle.com/#!18/dc3cf/3
| id | tag | child_id |
|---- |----- |---------- |
| 1 | A | |
| 1 | | 4 |
| 2 | C | |
| 3 | C | |
| 4 | B | |
| 4 | | 5 |
| 5 | D | |
| 5 | E | |
Each 'id' record may have a child (which is stored in the same table). Each child may have any number of sub children. I won't know the number of hierarchy levels but it will probably be no more than 10 levels deep.
In the example, there are:
3 root elements: id's 1, 2, 3
1 child and 1 child's child of id 1: id's 4 and 5 (respectively)
Question
I need to be able to query to get a result of all of an id's tags including all of it's children's tags. For example, I would need my output to be the following based on the table data above:
| id | tag |
|---- |----- |
| 1 | A |
| 1 | B |
| 1 | D |
| 1 | E |
| 2 | C |
| 3 | C |
| 4 | B |
| 5 | D |
| 5 | E |
Note that I need the children to still appear.
Is this possible without joining the table to itself 'n' many times where 'n' is the number of hierarchy levels?
Edit
To clarify, each id is also a root elements. So the only way to know if an ID is also a child is to look and see if the id has another record where it has a child_id. I made another version of the SQL Fiddle that demonstrates this point. Note that id 2 now has a child:
http://sqlfiddle.com/#!18/422f9/1
| id | tag | child_id |
|---- |----- |---------- |
| 1 | A | |
| 1 | | 4 |
| 2 | C | |
| 2 | | 5 |
| 3 | C | |
| 4 | B | |
| 4 | | 5 |
| 5 | D | |
| 5 | E | |
Yes, it is possible without joining the table n-times, you can use a CTE (Common Table Expression), see Docs
In your situation the code should be something like:
WITH cte (id, tag, child_id, parent) AS
(
SELECT id, tag, child_id, id
FROM demo
WHERE id NOT IN(SELECT child_id FROM demo WHERE child_id IS NOT NULL)
UNION ALL
SELECT demo.id, demo.tag, demo.child_id, cte.parent
FROM demo
JOIN cte
ON cte.child_id = demo.id
)
SELECT parent, tag
FROM cte
WHERE tag IS NOT NULL
UNION
SELECT id, tag
FROM demo
WHERE tag IS NOT NULL
ORDER BY parent, tag
Edit: Determine base elements dynamically.

Merge columns on two left joins

I have 3 tables as shown:
Video
+----+--------+-----------+
| id | name | videoSize |
+----+--------+-----------+
| 1 | video1 | 1MB |
| 2 | video2 | 2MB |
| 3 | video3 | 3MB |
+----+--------+-----------+
Survey
+----+---------+-----------+
| id | name | questions |
+----+---------+-----------+
| 1 | survey1 | 1 |
| 2 | survey2 | 2 |
| 3 | survey3 | 3 |
+----+---------+-----------+
Sequence
+----+---------+-----------+----------+
| id | videoId | surveyId | sequence |
+----+---------+-----------+----------+
| 1 | null | 1 | 1 |
| 2 | 2 | null | 2 |
| 3 | null | 3 | 3 |
+----+---------+-----------+----------+
I would like to query Sequence and join on both of video and survey tables and merge common columns without specifying the column names (in this case name) like this:
Query Result:
+----+---------+-----------+----------+---------+-----------+-----------+
| id | videoId | surveyId | sequence | name | videoSize | questions |
+----+---------+-----------+----------+---------+-----------+-----------+
| 1 | null | 1 | 1 | survey1 | null | 1 |
| 2 | 2 | null | 2 | video2 | 2MB | null |
| 3 | null | 3 | 3 | survey3 | null | 3 |
+----+---------+-----------+----------+---------+-----------+-----------+
Is this possible?
BTW the below sql doesn't work as it doesn't merge on the name field:
SELECT * FROM "Sequence"
LEFT JOIN "Survey" ON "Survey"."id" = "Sequence"."surveyId"
LEFT JOIN "Video" ON "Video"."id" = "Sequence"."videoId"
This query will show what you want:
select
s.*,
coalesce(y.name, v.name) as name, -- picks the right column
v.videoSize,
y.questions
from sequence s
left join survey y on y.id = s.surveyId
left join video v on v.id = s.videoId
However, the SQL standard requires you to name the columns you want. The only exception being * as shown above.

copy rows recursively in self-referencing table in sql

I have a table like this :
CREATE TABLE IF NOT EXISTS THING(
Id int NOT NULL IDENTITY(1, 1),
IdParent int,
randomtext varchar(255)
)
I would like to copy given list of ids with their children, grandchildren, but when I try it keeps ancient parent Id so the hierarchy is not kept
given something like this :
| Id | IdParent | random text |
-------------------------------
| 1 | 0 | "aaaaaaaaa" |
| 2 | 1 | "tt" |
| 3 | 2 | "third" |
| 4 | 0 | "fourth" |
| 5 | 0 | "randOther" |
If I give the ids list (1, 4), and another parntid value like "10", it copies rows with id 1 and 4 with the new parent id as 10 but the duplicated children get new inserted parents so it keeps the hierarchy
| Id | IdParent | random text |
-------------------------------------
| 1 | 0 | "aaaaaaaaa" |
| 2 | 1 | "tt" |
| 3 | 2 | "third" |
| 4 | 0 | "fourth" |
| 5 | 0 | "randOther" |
| 6 | 10 | "aaaaaaaaa -copy" |
| 7 | 6 | "tt- copy" |
| 8 | 7 | "third- copy" |
| 9 | 10 | "fourth- copy" |
All i managed to do for now is place all copies into the new parent with this query but I don't want to place all children in the new parent
WITH HIERARCHY (Id)
AS (SELECT Id FROM THING
WHERE Id IN (1,4)
UNION ALL
SELECT e.Id
FROM THING e
INNER JOIN HIERARCHY h ON e.IdParent = h.Id
)
INSERT INTO THING (IdParent, randomtext)
SELECT 10, randomtext
FROM THING
WHERE Id IN (SELECT * FROM HIERARCHY)
(the query(ies) should work in sql server 2008 and oracle)

Oracle sql recursive

I have the following table:
+----+-----------+------+
| ID | Parent_ID | Name |
+----+-----------+------+
| 1 | null | A |
| 2 | null | B |
| 3 | null | C |
| 4 | 1 | D |
| 5 | 4 | E |
| 6 | 2 | F |
+----+-----------+------+
And I need to get table like this:
+----+------+
| ID | Name |
+----+------+
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | AD |
| 5 | ADE |
| 6 | BF |
+----+------+
I checked the ORACLE related questions here, but I did't find anything useful.
Try this.
SELECT ID, REPLACE (SYS_CONNECT_BY_PATH (NAME, ' '), ' ')
FROM TABLE1
START WITH PARENT_ID IS NULL
CONNECT BY PRIOR ID = PARENT_ID
ORDER BY ID;
SQL Fiddle
Try this
WITH b(ID, Parent_ID) AS (
-- Anchor member.
SELECT ID,
Parent_ID
FROM DataTable
WHERE Parent_ID IS NULL
UNION ALL
-- Recursive member.
SELECT a.ID,
a.Parent_ID
FROM DataTable a, b
WHERE a.Parent_ID = b.id
)
SELECT ID,
Parent_ID
FROM b;