Want to know all possible Parent and Child Rows against specific Id? - sql

How to know all possible Parent and Child Rows against specific Id?
e.g. have following table:
MyTable:
-----------------------------------------------------
| Id | PId | Description |
-----------------------------------------------------
| 1 | NULL | A is Parent |
| 2 | 1 | B is Child of A |
| 3 | 2 | C is Child of B |
| 4 | NULL | D is Parent |
| 5 | NULL | E is Parent |
| 6 | 5 | F is Child of E |
-----------------------------------------------------
want to know all possible parent and child when pass spesific id
e.g.
CASE-01:
When #MyLookupId=2 OR #MyLookupId=1 OR #MyLookupId=3 One of from them Then Result Should Be,
-------
| Id |
-------
| 1 |
| 2 |
| 3 |
-------
CASE-02:
When #MyLookupId=4 Then Result Should Be,
-------
| Id |
-------
| 4 |
-------
CASE-03:
When #MyLookupId=6 Then Result Should Be,
-------
| Id |
-------
| 5 |
| 6 |
-------
Here is SQL for table:
IF OBJECT_ID('tempdb.dbo.#MyTable', 'U') IS NOT NULL DROP TABLE #MyTable;
SELECT * INTO #MyTable FROM (
SELECT (1)Id, (NULL)PId, ('A IS Parent')Description UNION ALL
SELECT (2)Id, (1)PId, ('B IS Child of A')Description UNION ALL
SELECT (3)Id, (2)PId, ('C IS Child of B')Description UNION ALL
SELECT (4)Id, (NULL)PId, ('D IS Parent')Description UNION ALL
SELECT (5)Id, (NULL)PId, ('E IS Parent')Description UNION ALL
SELECT (6)Id, (5)PId, ('F IS Child of E')Description ) AS tmp
SELECT * FROM #MyTable

You could use recursive cte
-- temp returns full tree of each rootId (parentid = null)
;WITH temp AS
(
SELECT sd.Id, sd.PId, sd.Id AS RootId
FROM #MyTable sd
WHERE sd.PId IS NULL
UNION ALL
SELECT sd.Id, sd.PId, t.RootId
FROM temp t
INNER JOIN #MyTable sd ON t.Id = sd.PId
)
SELECT t2.Id
FROM temp t
INNER JOIN temp t2 ON t2.RootId = t.RootId
WHERE t.Id = #Id
OPTION (MAXRECURSION 0)
Demo link: http://rextester.com/RAITMT72805

The answer given by TriV works, but requires a calculation of the entire hierarchy of your source table each time the query is run, which may not perform well at larger scale.
A more narrow approach is to find the Parent and Child records that only relate to the ID you are searching for:
declare #t table(ID int, PID int);
insert into #t values(1,null),(2,1),(3,2),(4,null),(5,null),(6,5);
declare #ID int = 2;
with c as
(
select ID
,PID
from #t
where ID = #ID
union all
select t.ID
,t.PID
from #t t
join c
on(t.PID = c.ID)
)
,p as
(
select ID
,PID
from #t
where ID = #ID
union all
select t.ID
,t.PID
from #t t
join p
on(t.ID = p.PID)
)
select ID
from p
union all
select ID
from c
where c.ID <> #ID
order by ID;
Output:
ID
````
1
2
3

Related

SQL query to get list of all records that are placed higher in hierarchy

Table:
+-----+------------+-------------+
| Id | DocumentNo | ParentCCID |
+-----+------------+-------------+
| 10 | CC001 | NULL |
| 20 | CC002 | CC001 |
| 33 | CC003 | CC002 |
+-----+-------------+-------------+
Value passed to the query: CC003
Expected Output:
CC003
CC002
CC001
Failed Attempt:
select b2.documentno,b2.ParentCCID from basicdetails b1
inner join basicdetails b2 on b1.documentno = b2.ParentCCID
where b2.documentno='CC003'
Note: DocumentNo is unique primary key. ParentCCID could have null values if there is no parent record.
EDIT:
create table basicdetails2
(
id int identity,
documentno varchar(30),
parentccid varchar(30)
)
insert into basicdetails2 values('CC001', null)
insert into basicdetails2 values('CC002', 'CC001')
insert into basicdetails2 values('CC003', 'CC002')
insert into basicdetails2 values('CC004', 'CC003')
You want a recursive cte:
with cte as (
select bd.documentno, bd.ParentCCID
from basicdetails bd
where bd.documentno = 'CC003'
union all
select cte.documentno, cte.ParentCCID
from cte join
basicdetails bd
on bd.documentno = cte.ParentCCID
)
select bd.documentno
from cte;
Just a minor twist on Gordon's answer (already +1).
I like to track the level and see the parents for each record
Example
Declare #Fetch varchar(25) = 'CC003'
;with cte as (
Select DocumentNo
,ParentCCDocumentNo
,Lvl=1
From YourTable
Where DocumentNo=#Fetch
Union All
Select R.DocumentNo
,R.ParentCCDocumentNo
,P.Lvl+1
From YourTable R
Join cte P on P.ParentCCDocumentNo = R.DocumentNo)
Select Lvl = Row_Number() over (Order By Lvl Desc)
,DocumentNo
,ParentCCDocumentNo
From cte
Order By 1 desc
Returns
Lvl DocumentNo ParentCCDocumentNo
3 CC003 CC002
2 CC002 CC001
1 CC001 NULL

SQL select all rows in a single row's "history"

I have a table that looks like this:
ID | PARENT_ID
--------------
0 | NULL
1 | 0
2 | NULL
3 | 1
4 | 2
5 | 4
6 | 3
Being an SQL noob, I'm not sure if I can accomplish what I would like in a single command.
What I would like is to start at row 6, and recursively follow the "history", using the PARENT_ID column to reference the ID column.
The result (in my mind) should look something like:
6|3
3|1
1|0
0|NULL
I already tried something like this:
SELECT T1.ID
FROM Table T1, Table T2
WHERE T1.ID = 6
OR T1.PARENT_ID = T2.PARENT_ID;
but that just gave me a strange result.
With a recursive cte.
If you want to start from the maximum id:
with recursive cte (id, parent_id) as (
select t.*
from (
select *
from tablename
order by id desc
limit 1
) t
union all
select t.*
from tablename t inner join cte c
on t.id = c.parent_id
)
select * from cte
See the demo.
If you want to start specifically from id = 6:
with recursive cte (id, parent_id) as (
select *
from tablename
where id = 6
union all
select t.*
from tablename t inner join cte c
on t.id = c.parent_id
)
select * from cte;
See the demo.
Results:
| id | parent_id |
| --- | --------- |
| 6 | 3 |
| 3 | 1 |
| 1 | 0 |
| 0 | |

SQL 1 row twice

I have SQL table what looks like:
+----------+-----------+
| ID | Direction |
+----------+-----------+
| 1 | left |
| 1 | null |
| 2 | left |
| 2 | null |
| 3 | null |
| 4 | left |
| 4 | null |
| 5 | null |
+----------+-----------+
I want to show each value only once:
If there will be ID 1 with Direction null and left, then show only ID 1 with direction left.
If there will be ID 1 only with null value, show it with null value.
Use a common table expression (cte):
with cte as
(
Your huge select...
)
select *
from cte t1
where t1.Direction = 'left'
or not exists (select * from cte t2
where t2.kanbanid = t1.kanbanid
and t2.Direction = 'left')
I.e. if your select has Direction 'left' for a kanbanid, return that row. Also return that row if same kanbanid has no Direction 'left' at all.
Why wont below query work:
select id,max(dir)
from #temp
group by id
below is test data:
create table #temp
(
id int,
dir char(10)
)
insert into #temp
select 1,'left'
union all
select 1,null
union all
select 2,null
union all
select 3,'right'
union all
select 3,null
union all
select 3,null
select id,max(dir)
from #temp
group by id
aggregate functions will ignore null,below is the output:
select distinct *,
row_number() over (partition by id order by ,Direction )as row1 into #any_table
from #your_table_name
select * from #any_table
where row1 =1

SQL Select and Count Statement

I have a table as follows
--------------------------------
ChildId | ChildName | ParentId |
--------------------------------
1 | A | 0 |
--------------------------------
2 | B | 1 |
--------------------------------
3 | C | 1 |
--------------------------------
I would like to select data as -
---------------------------------------
Id | Name | Childs |
---------------------------------------
1 | A | 2 |
---------------------------------------
2 | B | 0 |
---------------------------------------
3 | C | 0 |
---------------------------------------
The pseudo SQL statement should be like this-
SELECT ChildId AS Id, ChildName as Name, (Count (ParentId) Where ParentId=ChildId)
Any Help?
This will do the trick:
select t1.childid, t1.childname, count(*) as childs
from table t1
join table t2 on t1.childid = t2.parentid
group by t1.childid, t1.childname
Something like this?
SELECT ChildId AS Id, ChildName as Name, (SELECT COUNT(*) FROM TestCountOver T WHERE T.ParentID = O.ChildID) FROM TestCountOver O
but that would give you all the nodes plus children who shouldn't be part of the hierarchy
If you want only nodes with children then use a cte
;WITH CTE AS (
SELECT ChildId AS Id, ChildName as Name, (SELECT COUNT(*) FROM TestCountOver T WHERE T.ParentID = O.ChildID) Cnt FROM TestCountOver O
)
SELECT *
FROM CTE
WHERE Cnt > 0
If there is only one level you can use the approach in your pseudo code. It should look like this:
SELECT
ChildId AS Id,
ChildName as Name,
(select Count(ParentId) from t t_inner Where ParentId=t_outer.ChildId) children
from t t_outer
-- optionally where clause to limit result to parents that have children
where exists (select 1 from t where ParentId in (t_outer.ChildId))
but if there can be more than one level you can solve it using a recursive query if you want the total number of children for a parent (including grand-children etc).

SQL - Clone a record and its descendants

I would like to be able to clone a record and its descendants in the same table. An example of my table would be the following:
Table1
id | parentid | name
---------------------
1 | 0 | 'Food'
2 | 1 | 'Taste'
3 | 1 | 'Price'
4 | 2 | 'Taste Requirements'
The "id" column is the primary key and auto-increments. The 'Food' record (i.e. where id = 1) has two records underneath it called 'Taste' and 'Price'. The 'Taste' record has a record underneath it called 'Taste Requirements'. I would like to be able to clone the 'Food' record so that Table1 would look like the following:
Table1
id | parentid | name
---------------------
1 | 0 | 'Food'
2 | 1 | 'Taste'
3 | 1 | 'Price'
4 | 2 | 'Taste Requirements'
5 | 0 | 'Cookies'
6 | 5 | 'Taste'
7 | 5 | 'Price'
8 | 6 | 'Taste Requirements'
(where 'Cookies' is the name of the new category that I want to create). I am able to select all the descendants of 'Food' using:
with Table1_CTE( id, parentid, name )
as
(
select t.id, t.parentid, t.name from Table1 t
where t.id = 1
union all
select t.id, t.parentid,t. name from Table1 t
inner join Table1_CTE as tc
on t.parentid = tc.id
)
select id, parentid, name from Table1_CTE
and I am able to clone just the 'Food' record (i.e. where id = 1) using:
insert into Table1 ( parentid, name )
select ( parentid, 'Cookies' )
from Table1 where id = 1
but I am having problems trying to combine the two queries to clone the descendants of 'Food'. Also, I am trying to avoid using stored procedures, triggers, curosrs, etc. Is what I am trying to do possible? I have seen some examples on the web but have been unable to apply them to my requirements.
As Martin suggested, you need to enable IDENTITY_INSERT so that you can push your own identity values. You may also need to acquire a table lock to ensure that Max( Id ) returns the correct value.
If object_id('tempdb..#TestData') is not null
Drop Table #TestData
GO
Create Table #TestData
(
Id int not null identity(1,1) Primary Key
, ParentId int not null
, Name varchar(50) not null
)
GO
Set Identity_Insert #TestData On
GO
Insert #TestData( Id, ParentId, Name )
Values( 1,0,'Food' )
, ( 2,1,'Taste' )
, ( 3,1,'Price' )
, ( 4,2,'Taste Requirement' );
With Data As
(
Select Cast(MaxId.Id + 1 As int) As Id
, T.ParentId
, 'Copy Of ' + T.name As Name
, T.Id As OldId
, 0 As OldParentId
From #TestData As T
Cross Join( Select Max( id ) As Id From #TestData ) As MaxId
Where T.Name = 'Food'
Union All
Select Cast(Parent.id + Row_Number() Over( Order By Child.Id ) + 1 As int)
, Parent.Id
, 'Copy of ' + Child.Name
, Child.Id
, Child.ParentId
From Data As Parent
Join #TestData As Child
On Child.ParentId = Parent.OldId
)
Insert #TestData( Id, ParentId, Name )
Select Id, ParentId, Name
From Data
GO
Set Identity_Insert #TestData Off
GO
Results
id | parentid | name
-- | -------- | -----------------
1 | 0 | Food
2 | 1 | Taste
3 | 1 | Price
4 | 2 | Taste Requirement
5 | 0 | Copy Of Food
7 | 5 | Copy of Taste
8 | 5 | Copy of Price
9 | 7 | Copy of Taste Requirement
Assuming your CTE picks a root record and all it's descendents (it didn't seem to when I reproduced using your data above), then you can clone all selected records and insert like this:
with Table1_CTE( id, parentid, name )
as
(
select t.id, t.parentid, t.name from Table1 t
where c.icategoryid = 1
union all
select t.id, t.parentid,t. name from Table1
inner join Table1_CTE as tc
on t.parentid = tc.id
)
insert into dbo.testinsertheirarchy ( parentid, name )
select parentid, name from Table1_CTE