I have a parent/child relation with the following child table:
CHILD_ID PARENT_ID CHILD_VALUE
------------------------------
1 1 x
2 1 y
3 2 y
Now I want to select all existing distinct parents where the CHILD_VALUE is:
child has value = x (there will be only 1 or none)
if no child with value x exists - NULL
In other words show all parents with "matching" child or NULL if no child "matching" the value exists.
So the result shall look like this:
PARENT_ID CHILD_ID CHILD_VALUE
------------------------------
1 1 x
2 NULL NULL
Question would be how I can narrow down the join in this case.
This does the trick:
select distinct a.parent_id, b.child_id, b.child_value
from test1 a
left outer join test1 b
on a.parent_id = b.parent_id
and b.child_value = 'x'
See also this SQL fiddle.
Related
I have table with following schema and contains records-
id parent_id active
1 NULL Y
2 1 Y
3 1 N
4 NULL Y
5 4 N
6 NULL N
7 6 N
I need to write a SQL for following use case:
Need to find all records whose active not equals to Y and whose parent_id active equals to Y.
Example output of above should be as follows:
output should be-
id parent_id active
3 1 N
5 4 N
You could do it using self join as below:
SELECT *
FROM mytab t1 INNER JOIN mytab t2
ON t1.id = t2.parent_id
WHERE t1.active != 'Y'
AND t2.active = 'Y'
You can do this using self-join as following:
select t1.id,t1.parent_id,t1.active
from test t1 inner join test t2 on t1.parent_id=t2.id
where t1.active <> 'Y' and t2.active='Y';
Results can be seen here:
DB Fiddle Demo
use subquery 1st take all the id's who have active=Y then check with parent_id and active column
select * from tbale_name where
parent_id in (
select id from table_name where active='Y'
) and active='N'
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=4a450a50a2ea6c3f4e1a19e2657ae35d
id parent_id active
3 1 N
5 4 N
I have two tables, structures of the same is as below:
Table 1. Transactional data table
trx id.
1
2
3
4
5..etc
Table 2
table 2 has the parent child relationship as below .
id subject_id (Child) object_id (Parent)
1 2 1
2 3 1
3 4 1
4 5 1
Now using above tables, the expected output is as follow:
1
2
3
4
5
Please let me know how can I achieve the same. I need to get the details from the Table 1 along with parent and its all children in the hierarchy. I just have one level of hierarchy.
As you just have a one level hierarchy you can get it to work just ordering the results correctly. Try this one:
select obj.object_id, t.*
from
(
select
object_id,
object_id as parent_id
from table2
union
select
subject_id as object_id,
object_id as parent_id
from table2
) obj
inner join table1 t
on t.id = obj.object_id
order by
obj.parent_id,
case when obj.object_id = obj.parent_id then 0 else 1 end,
obj.object_id
;
I light variation on the comment of the #a_horse...
select * from table1
where id in
(select object_id from table2
union all
select subject_id from table2)
order by id;
as expected
1
2
3
4
5
If you want to constraint the parent, simple add WHERE predicates in the subquery.
I only have 1 table called Posts with 5 columns (much like StackExchange Data Explorer)
Id, Score, PostTypeId, ParentId, AcceptedAnswerId
1 5 1 null 3
2 3 1 null 5
3 9 2 1 null
4 3 2 1 null
....
As you can see, the rows have two levels of hierarchy (Parents, Childs). Let's say I want to select the childs (PostTypeId = 2) and for each one I would like to left join the score of the sibling that is currently accepted by the parent. The accepted sibling Id can only be found in the AcceptedAnswerId column of the parent row. The parent row can be found through the ParentId of the child row I am selecting. Is there an elegant way to do this?
The expected results is the following
Id, Score, AcceptedScore
3 9 9
4 3 9
....
My attempt (although it doesn't work and hurts my eyes as well as my brain):
select top 50 a.Id, a.Score, b.Score as AcceptedScore
from Posts as a
left join (
select c.Score from Posts as c
where c.Id = (
select d.AcceptedAnswerId from Posts as d
where d.Id = a.ParentId
)
) as b
on b.ParentId = a.ParentId
where a.PostTypeId = 2
The error I'm getting:
The multi-part identifier "a.ParentId" could not be bound. Invalid column name 'ParentId'.
Here's one that works:
SELECT p1.Id, p1.Score, p3.Score AS AcceptedScore
FROM Posts p1 LEFT JOIN Posts p2
ON p1.ParentId = p2.Id LEFT JOIN Posts p3
ON p2.AcceptedAnswerId = p3.Id
WHERE p1.PostTypeId = 2
See the SQLFiddle
I have a self reference table to store hierarchical values in order to show up them in a TreeView or so on ,according to James Crowley article (Tree structures in ASP.NET and SQL Server)
Our table would look something like this:
Id ParentId Name Depth Lineage
1 NULL Root Node 0 /1/
2 1 Child A 1 /1/2/
3 1 Child B 1 /1/3/
4 1 Child C 1 /1/4/
5 2 Child D 2 /1/2/5/
To get path of a node (for example id=5) he suggest following query against table
SELECT *
FROM dfTree
WHERE (SELECT lineage
FROM dfTree
WHERE id = 5) LIKE lineage + '%'
result would be :
Id ParentId Name Depth Lineage
1 NULL Root Node 0 /1/
2 1 Child A 1 /1/2/
5 2 Child D 2 /1/2/5/
And Is Acceptable
But how about to have a result set when there are multiple IDs which i want to have their path ? therefore for example in above example instead of Id=5 i would like to pass multiple values something like this :
SELECT *
FROM dfTree
WHERE (SELECT lineage
FROM dfTree
WHERE id IN (5,6,8,9)) LIKE lineage + '%'
But the above statement make no sense and it is invalid sql server expression
How could i solve this problem ?
Thanks in advance
This query...
SELECT DISTINCT T2.*
FROM
(
SELECT lineage
FROM dfTree
WHERE id IN (4, 5)
) T1
JOIN dfTree T2
ON
T1.Lineage LIKE T2.Lineage + '%'
...returns the following result on your test data:
Id ParentId Name Depth Lineage
1 NULL Root Node 0 /1/
2 1 Child A 1 /1/2/
4 1 Child C 1 /1/4/
5 2 Child D 2 /1/2/5/
As you can see, all paths are "merged" together - for example, the path component Id=1 belongs to both the path: /1/4/ and the path: /1/2/5/, yet exists in the result set only once.
One the other hand, if you do need to distinguish between different paths, you'd need to do something like this:
SELECT T2.*, T1.Id LeafId
FROM
(
SELECT id, lineage
FROM dfTree
WHERE id IN (4, 5)
) T1
JOIN dfTree T2
ON
T1.Lineage LIKE T2.Lineage + '%'
Result:
Id ParentId Name Depth Lineage LeafId
1 NULL Root Node 0 /1/ 4
4 1 Child C 1 /1/4/ 4
1 NULL Root Node 0 /1/ 5
2 1 Child A 1 /1/2/ 5
5 2 Child D 2 /1/2/5/ 5
In this case, each path is identified by its leaf. This assumes there are no diamond-shaped dependencies (i.e. this is a real tree and not just any DAG); if there are, then you'd need to use T1.Lineage instead of T1.Id to identify the path.
If you are running SQL Server 2005 or higher you could move the subquery to a CTE:
; with cte as (
SELECT lineage
FROM dfTree
WHERE id IN (5,6,8,9)
)
SELECT d.*
FROM dfTree d
inner join cte on cte.lineage like d.lineage + '%'
Or just restructure the subquery:
SELECT d.*
FROM dfTree d
inner join (
SELECT lineage
FROM dfTree
WHERE id IN (5,6,8,9)
) s on s.lineage like d.lineage + '%'
Given a simple (id, description) table t1, such as
id description
-- -----------
1 Alice
2 Bob
3 Carol
4 David
5 Erica
6 Fred
And a parent-child relationship table t2, such as
parent child
------ -----
1 2
1 3
4 5
5 6
Oracle offers a way of traversing this as a tree with some custom syntax extensions:
select parent, child, sys_connect_by_path(child, '/') as "path"
from t2
connect by prior parent = child
The exact syntax is not important, and I've probably made a mistake in the above. The
important thing is that the above will produce something that looks like
parent child path
------ ----- ----
1 2 /1/2
1 3 /1/3
4 5 /4/5
4 6 /4/5/6
5 6 /5/6
My question is this: is it possible to join another table within the sys_connect_by_path(), such as the t1 table above, to produce something like:
parent child path
------ ----- ----
1 2 /Alice/Bob
1 3 /Alice/Carol
... and so on...
In your query, replace T2 with a subquery that joins T1 and T2, and returns parent, child and child description. Then in the sys_connect_by_path function, reference the child description from your subquery.
Based on Mike McAllister's idea, the following uses a derived table to achieve the desired result:
select
T.PARENT
,T.CHILD
,sys_connect_by_path(T.CDESC, '/')
from
(
select
t2.parent as PARENT
,t2.child as CHILD
,t1.description as CDESC
from
t1, t2
where
t2.child = t1.id
) T
where
level > 1 and connect_by_isleaf = 1
connect by prior
T.CHILD = T.PARENT
In my problem, all the parents are anchored under a "super-parent" root, which means that the paths can be fully described with SYS_CONNECT_BY_PATH, thereby obviating the need for cagcowboy's technique of concatenating the parent with the path.
SELECT parent, child, parents.description||sys_connect_by_path(childs.description, '/') AS "path"
FROM T1 parents, T1 childs, T2
WHERE T2.parent = parents.id
AND T2.child = childs.id
CONNECT BY PRIOR parent = child