Filtering SQL Tree Query - sql

if I have a tree query like the one below and I want to filter the Name = 'Son' and also select all of its parent records and so the result should yield the first 3 rows. How would I construct my query? I've read that I should use Common Table Expression (CTE) but I'm a newbie on CTE. Can anyone help me? Thanks.
select 1 AS id, NULL AS parent, 'God' AS name
UNION
select 2 AS id, 1 AS parent, 'Father' AS name
UNION
select 3 AS id, 2 AS parent, 'Son' AS name
UNION
select 4 AS id, NULL AS parent, 'Godmother' AS name
UNION
select 5 AS id, 4 AS parent, 'Mother' AS name

Sounds like you could store the tree in a table (or define a view using the SQL above), and then if you are using Oracle, you could use the CONNECT BY function to filter records.

Is this what you're looking for?
;with SomeCTE as
(
select *
from (
select 1 AS id, NULL AS parent, 'God' AS name
UNION
select 2 AS id, 1 AS parent, 'Father' AS name
UNION
select 3 AS id, 2 AS parent, 'Son' AS name
UNION
select 4 AS id, NULL AS parent, 'Godmother' AS name
UNION
select 5 AS id, 4 AS parent, 'Mother' AS name ) as a
)
select *
from SomeCTE a
left join SomeCTE b
on a.parent = b.id
left join SomeCTE c
on b.parent = c.id
where a.name = 'son'

Related

Oracle SQL - Count based on a condition to include distinct rows with zero matches

Is there a "better" way to refactor the query below that returns the number occurrences of a particular value (e.g. 'A') for each distinct id? The challenge seems to be keeping id = 2 in the result set even though the count is zero (id = 2 is never related to 'A'). It has a common table expression, NVL function, in-line view, distinct, and left join. Is all of that really needed to get this job done? (Oracle 19c)
create table T (id, val) as
select 1, 'A' from dual
union all select 1, 'B' from dual
union all select 1, 'A' from dual
union all select 2, 'B' from dual
union all select 2, 'B' from dual
union all select 3, 'A' from dual
;
with C as (select id, val, count(*) cnt from T where val = 'A' group by id, val)
select D.id, nvl(C.cnt, 0) cnt_with_zero from (select distinct id from T) D left join C on D.id = C.id
order by id
;
ID CNT_WITH_ZERO
---------- -------------
1 2
2 0
3 1
A simple way is conditional aggregation:
select id,
sum(case when val = 'A' then 1 else 0 end) as num_As
from t
group by id;
If you have another table with one row per id, you I would recommend:
select i.id,
(select count(*) from t where t.id = i.id and t.val = 'A') as num_As
from ids i;

print name of parents who have children with same name

PARENT table PK is PID and PID is FK in CHILDREN table. How do I print names of Parents from PARENT table that have children who share the same name as another child in CHILDREN table? I think a recursive join should be used to find the same name but I can't get it to work. I am able to join the PARENT and CHILDREN tables using below query:
select PARENT.NAME as ParentName
from PARENT inner join CHILDREN
on PARENT.PID=CHILDREN.PID
group by NAME;
I have tried this query to complete the recursive join but it isn't working:
select CHILDREN.NAME
from CHILDREN e, CHILDREN m
where e.CHILDREN.PID=m.CHILDREN.PID
order by CHILDREN.PID;
Group by child name and evaluate, if at least two different parents exist.
-- TEST DATA
with parent(pid, name) as
(select 1, 'Parent1' from dual
union all
select 2, 'Parent2' from dual
union all
select 3, 'Parent3' from dual
union all
select 4, 'Parent4' from dual),
children(name, pid) as
(select 'Tom', 1 from dual
union all
select 'Tim', 1 from dual
union all
select 'Steven', 2 from dual
union all
select 'Tim', 2 from dual
union all
select 'Marta', 2 from dual
union all
select 'Jess', 3 from dual
union all
select 'Jim', 4 from dual
union all
select 'Jess', 4 from dual)
--> SELECT
select c.name, listagg(p.name, ',') within group(order by p.name)
from parent p
join children c
on c.pid = p.pid
group by c.name -- group by child name
having min (p.pid) <> max (p.pid) -- at least two different parents
--> RESULT
Jess Parent3,Parent4
Tim Parent1,Parent2
AND Parent.Name = Children.Name
So wouldn't this print names of the parents where it matches with Children's names?

Case on union of multiple unions and issue with alias

I have 2 series of unions which I wish to join by another union. In the first one, I have 3 Selects and in the second one I have 2 different Selects.
Select id, min(value)
from table1 t1
join (Select id, value
Union
Select id, value
Union
Select id, value) as foo
on foo.id=t1.id
Group by id
Select id, max(value)
from table1 t1
join (Select id, value
Union
Select id, value) as bar
on bar.id=t1.id
Group by id
I tried to do a union between these two, but it made things pretty complicated. My biggest issue is with my alias. My second is with the case linked to my value columns, which I wish to name value.
Select (alias).id,
Case
When foo.value= 0 or bar.value=1 THEN 1
Else 0
End as value
from table1 t1
Join (Select id, min(value)
from table1 t1
join (Select id, value
Union
Select id, value
Union
Select id, value) as foo
on foo.id=t1.id
Group by id
UNION
Select id, max(value)
from table1 t1
join (Select id, value
Union
Select id, value) as bar
on bar.id=t1.id
Group by id) as (alias)
on ??.id=??.id
I wrote my case the way I think it should be written, but normally, when there are more than one column with the same name, SQL states it as ambiguous. I am still unsure if I should use UNION or INTERSECT, but I assume either of them would be done the same way. How should I deal with this?
I'm reading this right, you probably want something like this
SELECT ...
FROM ( ... union #1 ) AS u1
JOIN (... union #2 ) AS u2 ON u1.id = u2.id

'SELECT IN' with item list containing duplicates

I'm doing
SELECT Name WHERE Id IN (3,4,5,3,7,8,9)
where in this case the '3' Id is duplicated.
The query automatically excludes the duplicated items while for me would be important to get them all.
Is there a way to do that directly in SQL?
The query doesn't exclude duplicates, there just isn't any duplicates to exclude. There is only one record with id 3 in the table, and that is included because there is a 3 in the in () set, but it's not included twice because the 3 exists twice in the set.
To get duplicates you would have to create a table result that has duplicates, and join the table against that. For example:
select t.Name
from someTable t
inner join (
select id = 3 union all
select 4 union all
select 5 union all
select 3 union all
select 7 union all
select 8 union all
select 9
) x on x.id = t.id
Try this:
SELECT Name FROM Tbl
JOIN
(
SELECT 3 Id UNION ALL
SELECT 4 Id UNION ALL
SELECT 5 Id UNION ALL
SELECT 3 Id UNION ALL
SELECT 7 Id UNION ALL
SELECT 8 Id UNION ALL
SELECT 9 Id
) Tmp
ON tbl.Id = Tmp.Id

mysql - order by frequency with multiple columns

I have a table with 5 columns:
tag 1
tag 2
tag 3
tag 4
tag 5
If I want to show results ordered by the popularity(frequency) of those tags, what kind of query would i use?
Because the table isn't normalized, you'll have to flatten it first:
SELECT a.column, a.tag1 AS tag
FROM TABLE a
UNION ALL
SELECT b.column, b.tag2
FROM TABLE b
UNION ALL
SELECT c.column, c.tag3
FROM TABLE c
UNION ALL
SELECT d.column, d.tag4
FROM TABLE d
UNION ALL
SELECT e.column, e.tag5
FROM TABLE e
...before you can count them:
SELECT t.tag, COUNT(*) tag_popularity
FROM (SELECT a.column, a.tag1 AS tag
FROM TABLE a
UNION ALL
SELECT b.column, b.tag2
FROM TABLE b
UNION ALL
SELECT c.column, c.tag3
FROM TABLE c
UNION ALL
SELECT d.column, d.tag4
FROM TABLE d
UNION ALL
SELECT e.column, e.tag5
FROM TABLE e) x
GROUP BY x.tag
ORDER BY tag_popularity DESC