ParentChild to get correct ID in T-SQL - sql

I have two tables toplevel and parentchild. parentchild table is a tree which is related to each other. Tree looks like this.
TREE
1
-11
2
-12
--13
3
-14
--15
---16
drop table #TopLevel
create table #TopLevel
(
TopLevelID INT,
createdate DateTime
)
insert into #TopLevel
(TopLevelID,createdate
)
select 1,'2013-03-01 00:00:00' union all
select 2,'2013-03-07 00:00:00' union all
select 3,'2013-03-06 00:00:00' union all
select 4,'2013-03-03 00:00:00' union all
select 5,'2013-03-08 00:00:00' union all
select 6,'2013-03-09 00:00:00' union all
select 7,'2013-03-10 00:00:00'
drop table #parentchild
create table #parentchild
(
parentchildID INT,Parent INT,Child INT
)
insert into #parentchild
(
parentchildID,Parent, Child
)
select 1,1,11 union all
select 2,12,13 union all
select 4,15,16 union all
select 5,14,15 union all
select 3,2,12 union all
select 6,3,14
;with abc as
(
select * From #parentchild
left outer join #TopLevel on #parentchild.Parent=#TopLevel.TopLevelID
)
select * from abc
I need to find toplevelid for each row in #parentchild table. For examble in #parentchild table parent=12 is not in #toplevel table because its child too. then if we see child =12 and parent is 2, that is in #toplevel table.
please help. Thanks.
Data should be look like this in #parentchild table. * one i added manually.
parentchildID Parent Child TopLevelID createdate
1 1 11 1 2013-03-01 00:00:00.000
2 12 13 *2 *2013-03-07 00:00:00.000
4 15 16 *3 *2013-03-06 00:00:00.000
5 14 15 *3 *2013-03-06 00:00:00.000
3 2 12 2 2013-03-07 00:00:00.000
6 3 14 3 2013-03-06 00:00:00.000
Where I am doing wrong?
;with abc as
(
select ParentChildID,Parent,Child,TopLevelID,CreateDate From #parentchild
left outer join #TopLevel on #parentchild.Parent=#TopLevel.TopLevelID
)
,xyz as
(
select ParentChildID,Parent,Child,TopLevelID,CreateDate from abc where TopLevelID IS NULL
union all
select a.ParentChildID,a.Parent,a.Child,a.TopLevelID,a.CreateDate from abc a
inner join abc e on e.TopLevelID=a.Parent
)
select * from xyz
This CTE is for first #parentchild table but giving nothing.
;with abc as
(
select ParentChildID,Parent,Child from #parentchild
where parent is null
union all
select a.ParentChildID,a.Parent,a.Child from #parentchild a
inner join abc e on e.child=a.parent
)
select * from abc

I think the recursive common table expression will be your friend here. http://www.codeproject.com/Articles/683011/How-to-use-recursive-CTE-calls-in-T-SQL
Try this. It was a bit of a rush but feel free to clean it up and play around with it. There's probably a more elegant way of doing this than using the left function.
With CTE as (
select parentchildID,Parent,Child,1 as lev,cast(parent as varchar(20)) as heirarchy--,parent as TopParent
from #parentchild pc
join #TopLevel tl on tl.TopLevelID = pc.Parent
union all
select pc2.parentchildID,pc2.Parent,pc2.Child,cte.lev + 1,cast(cte.heirarchy + '\'+ cast(pc2.Child as varchar(3)) as varchar(20))
from #parentchild pc2
join CTE on cte.child = pc2.Parent
where pc2.parent not in (select TopLevelID from #TopLevel)
),
NoDate as
(
select parentchildID,parent,Child,heirarchy,
case left(heirarchy,charindex('\',heirarchy)) when '' then heirarchy else left(heirarchy,charindex('\',heirarchy)-1) end as TopLevelParent
from CTE
)
select nd.parentchildID,nd.Parent,nd.Child,nd.TopLevelParent,tl.createdate,nd.heirarchy
from NoDate nd
join #TopLevel tl on tl.TopLevelID = nd.TopLevelParent
order by parentchildID;

Related

Sql Server 2014 - Deep Recursive Parent-Child Self Join

I am trying to build a deep recursive self-join query. Having the table like:
Id | ParentId
1 | NULL
2 | 1
3 | 1
4 | 2
5 | 3
6 | 8
7 | 9
For Id 1 my query should be fetching 1,2,3,4,5 since they are either the children of 1 or children of the children of 1. In the given example 6 and 7 should not be included in the query result.
I tried using CTE but I am getting tons of duplicates:
WITH CTE AS (
SELECT Id, ParentId
FROM dbo.Table
WHERE ParentId IS NULL
UNION ALL
SELECT t.Id, t.ParentId
FROM dbo.Table t
INNER JOIN CTE c ON t.ParentId = c.Id
)
SELECT * FROM CTE
Ideas?
You can try to use DISTINCT to filter duplicate rows.
;WITH CTE AS (
SELECT Id, ParentId
FROM T
WHERE ParentId IS NULL
UNION ALL
SELECT t.Id, t.ParentId
FROM T
INNER JOIN CTE c ON t.ParentId = c.Id
)
SELECT DISTINCT Id, ParentId
FROM CTE
Try the following query with CTE where you can set parentId by #parentID:
DECLARE #parentID INT = 1
;WITH cte AS
(
SELECT
t.ID
, t.ParentId
FROM #table t
),
cteParent AS
(
SELECT
t.ID
, t.ParentId
FROM #table t
WHERE t.ParentId IN (SELECT t1.ID FROM #table t1 WHERE T1.ParentId = #parentID)
)
SELECT
DISTINCT c1.ID
, c1.ParentId
FROM cte c1
INNER JOIN cte c2 ON c2.ParentId = c1.ID
UNION ALL
SELECT *
FROM cteParent
And the sample data:
DECLARE #table TABLE
(
ID INT
, ParentId INT
)
INSERT INTO #table
(
ID,
ParentId
)
VALUES
(1, NULL )
, (2, 1 )
, (3, 1 )
, (4, 2 )
, (5, 3 )
, (6, 8 )
, (7, 9 )
OUTPUT:
ID ParentId
1 NULL
2 1
3 1
4 2
5 3
I don't see duplicates.
Your code returns the following on the data you provided:
Id ParentId
1
2 1
3 1
5 3
4 2
which is what you want.
Here is a db<>fiddle.
Here is the code:
WITH t as (
SELECT *
FROM (VALUES (1, NULL), (2, 1), (3, 1), (4, 2), (5, 3), (6, 8), (7, 9)
) v(id, parentId)
),
CTE AS (
SELECT Id, ParentId
FROM t
WHERE ParentId IS NULL
UNION ALL
SELECT t.Id, t.ParentId
FROM t
INNER JOIN CTE c ON t.ParentId = c.Id
)
SELECT *
FROM CTE;
If you are getting duplicates in your actual result set, then you presumably have duplicates in your original table. I would recommend removing them before doing the recursive logic:
with t as (
select distinct id, parentid
from <your query>
),
. . .
Then run the recursive logic.
Try this sql script which result Parent Child Hierarchy
;WITH CTE(Id , ParentId)
AS
(
SELECT 1 , NULL UNION ALL
SELECT 2 , 1 UNION ALL
SELECT 3 , 1 UNION ALL
SELECT 4 , 2 UNION ALL
SELECT 5 , 3 UNION ALL
SELECT 6 , 8 UNION ALL
SELECT 7 , 9
)
,Cte2
AS
(
SELECT Id ,
ParentId ,
CAST('\'+ CAST(Id AS VARCHAR(MAX))AS VARCHAR(MAX)) AS [Hierarchy]
FROM CTE
WHERE ParentId IS NULL
UNION ALL
SELECT c1.Id ,
c1.ParentId ,
[Hierarchy]+'\'+ CAST(c1.Id AS VARCHAR(MAX)) AS [Hierarchy]
FROM Cte2 c2
INNER JOIN CTE c1
ON c1.ParentId = c2.Id
)
SELECT Id,
RIGHT([Hierarchy],LEN([Hierarchy])-1) AS ParentChildHierarchy
FROM Cte2
GO
Result
Id ParentChildHierarchy
-------------------------
1 1
2 1\2
3 1\3
5 1\3\5
4 1\2\4
This query will help you
CREATE TABLE #table( ID INT, ParentId INT )
INSERT INTO #table(ID,ParentId)
VALUES (1, NULL ), (2, 1 ), (3, 1 ), (4, 2 ), (5, 3 ), (6, 8 ), (7, 9 )
;WITH CTE AS (
SELECT ID FROM #table WHERE PARENTID IS NULL
UNION ALL
SELECT T.ID FROM #table T
INNER JOIN #table T1 ON T.PARENTID =T1.ID
) SELECT * FROM CTE

HiveQL equivalent of !> in SQL

I have currently been trying to extract those values from a table that do not exist in another table. However, as the joining value contains null values - the not in, not exists and left join option do not seem to be working.
Therefore, is there a way to apply the 'not greater than' condition in the HiveQL?
For reference, this is the query that I ran, and similarly with not exists and left join ..
with date_prob as
(
select distinct visit
from t1
where dt=20161124
and dt1!=orig_ts
),
ev_data as
(
select distinct visit
from t1
where dt=20161124
and visit is not null
and origts is not null
and uid is not null
),
fin_data as
(
select x.visit
from ev_data x
where x.visit not in
(
select distinct visit
from date_prob
and visit is not null
)
)
The query that I ran for a left join -
with date_prob as
(
select distinct id
from t1
where dt1='2016-11-24'
and dt1!=orig_ts
and (datediff(dt1,orig_ts) not in ('1','-1'))
),
ev_data as
(
select distinct id
from t1
where dt1='2016-11-24'
and id is not null
)
select x.id
from ev_data x
left join date_prob y
where y.id is null
;
The Data Example -
id dt1 orig_ts
1 2016-11-24 2016-11-10
2 2016-11-24 2016-11-24
3 2016-11-24 2010-01-01
4 2016-11-24 2017-01-01
5 2016-11-24 2016-11-24
6 2016-11-24 2016-11-25
7 2016-11-23 2016-11-23
Therefore, from this table I want to remove those Id's where there is greater than a difference of a day. Thus, the query should return values only where the ID is equal to 2,5 and 6.
If you want to extract those values from a table that do not exist in another table than you can use left join and filter where second_table_key is null.
This will work even there are NULLs in keys:
--this query will return records from table a that do not exist in b
select a.id
from a left join b on a.id=b.id
where b.id is null; --only not joined
Have fixed your example. it works:
drop table if exists t1;
create table t1 (id int,dt1 string, orig_ts string );
insert overwrite table t1
select 1 id, '2016-11-24' dt1, '2016-11-10' orig_ts union all
select 2 id, '2016-11-24' dt1, '2016-11-24' orig_ts union all
select 3 id, '2016-11-24' dt1, '2010-01-01' orig_ts union all
select 4 id, '2016-11-24' dt1, '2017-01-01' orig_ts union all
select 5 id, '2016-11-24' dt1, '2016-11-24' orig_ts union all
select 6 id, '2016-11-24' dt1, '2016-11-25' orig_ts union all
select 7 id, '2016-11-23' dt1, '2016-11-23' orig_ts;
with date_prob as
(
select distinct id
from t1
where dt1='2016-11-24'
and dt1!=orig_ts
and (datediff(dt1,orig_ts) not in ('1','-1'))
),
ev_data as
(
select distinct id
from t1
where dt1='2016-11-24'
and id is not null
)
select x.id
from ev_data x
left join date_prob y on x.id=y.id
where y.id is null
;
OK
2
5
6
Time taken: 14.166 seconds, Fetched: 3 row(s)
hive>
Works as expected

Unite two column tables together within SELECT

How do I unit two tables to a new table that doesn't include a primary key?
For example:
First Table:
Team_Code ID
----------------------------------
433 203994834
436 203994834
436 309228372
435 309228373
434 309228374
433 399064113
Second Table:
number_of_times Team_Code
------------------------------------
3 433
2 434
1 435
1 436
The first lines of the output should look like:
number_of_times Team_Code ID
---------------------------------------------
3 433 203994834
2 434 309228374
1 435 309228373
1 436 203994834
; with
query1 as
(
-- Your Query 1
SELECT * FROM Fireman_Team WHERE Team_Code in (
SELECT Answer.Team_Code FROM Answer WHERE
Answer.Call_Number IN(
SELECT Call_Number FROM (SELECT * FROM Call WHERE Call.Time_Started
BETWEEN ('2016-01-01 00:00:00') AND
('2016-01-31 23:59:59')) AS SSS))
),
query2 as
(
-- Your Query 2
SELECT COUNT(Team_Code) as number_of_times,
Team_Code FROM (SELECT Answer.Team_Code FROM Answer WHERE
Answer.Call_Number IN(
SELECT Call_Number FROM (SELECT * FROM Call WHERE Call.Time_Started
BETWEEN ('2016-01-01 00:00:00') AND
('2016-01-31 23:59:59')) AS SSS)
) AS re GROUP BY Team_Code
)
select *
from query1 q1 inner join query2 q2 on q1.Team_Code = q2.Team_Code
Use CTEs for readability -
WITH CTE1 AS
(
SELECT [Team_Code],[ID] FROM Fireman_Team WHERE Team_Code in (
SELECT Answer.Team_Code FROM Answer WHERE
Answer.Call_Number IN(
SELECT Call_Number FROM (SELECT * FROM Call WHERE Call.Time_Started
BETWEEN ('2016-01-01 00:00:00') AND
('2016-01-31 23:59:59')) AS SSS))
)
;WITH CTE2 AS
(
SELECT COUNT(Team_Code) as number_of_times,
Team_Code FROM (SELECT Answer.Team_Code FROM Answer WHERE
Answer.Call_Number IN(
SELECT Call_Number FROM (SELECT * FROM Call WHERE Call.Time_Started
BETWEEN ('2016-01-01 00:00:00') AND
('2016-01-31 23:59:59')) AS SSS)
) AS re GROUP BY Team_Code
)
SELECT b.[number_of_times], b.[Team_Code], a.[ID]
FROM CTE1 a
INNER JOIN CTE2 b
ON a.[Team_Code] = b.[Team_Code]

accessing parent nodes in same level in T-SQL

Let's say I have a database schema like this:
RowId ParentId Name
------ ---------- ------
1 NULL Level1
2 NULL Level2
3 1 Leaf1
4 1 Leaf2
5 2 Leaf1
6 3 LeafX
Basically, the tree would look as such:
Level1
Leaf1
LeafX
Leaf2
Level2
Leaf1
I need to extract all ancestor LEVEL of LeafX in the most efficient and dynamic way.
So it will output: Leaf1, Leaf2, and Leaf1 (of Level2)
How do I do this in T-SQL? Thanks
This will give you the result you want.
;with C as
(
select T.rowid,
T.parentid,
T.name,
1 as Lvl
from YourTable as T
where T.parentid is null
union all
select T.rowid,
T.parentid,
T.name,
C.Lvl + 1
from YourTable as T
inner join C
on T.parentid = C.rowid
)
select *
from C
where C.Lvl = (
select C.lvl-1
from C
where C.name = 'LeafX'
)
Update
And this might be faster for you. You have to test on your data.
declare #Level int;
with C as
(
select T.rowid,
T.parentid
from #t as T
where T.name = 'LeafX'
union all
select T.rowid,
T.parentid
from #t as T
inner join C
on T.rowid = C.parentid
)
select #Level = count(*) - 1
from C;
with C as
(
select T.rowid,
T.parentid,
T.name,
1 as Lvl
from #t as T
where T.parentid is null
union all
select T.rowid,
T.parentid,
T.name,
C.Lvl + 1
from #t as T
inner join C
on T.parentid = C.rowid
where C.Lvl < #Level
)
select *
from C
where C.Lvl = #Level;
There's a few methods to do that. My favourite is to create special table Trees_Parents, where you will store every parent for evere node.
So if have structure like that
RowId ParentId Name
------ ---------- ------
1 NULL Level1
2 NULL Level2
3 1 Leaf1
4 1 Leaf2
5 2 Leaf1
6 3 LeafX
your Trees_Parents table will looks like
RowId ParentId
------ ----------
1 1
2 2
3 3
3 1
4 4
4 1
5 5
5 2
6 6
6 1
6 3
then when you need to retrieve all children you just write
select RowID from Trees_Parents where ParentId = 1
I'm storing row self in this table to avoid unions, if you don't need it you can write
select RowID from Trees_Parents where ParentId = 1 and ParentId <> RowId
And for all parents you'll write
select ParentId from Trees_Parents where RowId = 6 and ParentId <> RowId
You can also store Table_Name in table Trees_Parents so you can use it for different tables
Another way is to write recursive WITH clause, but if your tree is big and it's not changing frequently I think it's better to store parents data in additional table
Well you can use recursive solution. You need to get all nodes with Depth = Depth of your node - 1
declare #Temp table (RowId int, ParentId int, Name nvarchar(128))
insert into #Temp
select 1, null, 'Level1' union all
select 2, null, 'Level2' union all
select 3, 1, 'Leaf1' union all
select 4, 1, 'Leaf2' union all
select 5, 2, 'Leaf3' union all
select 6, 3, 'LeafX';
with Parents
as
(
select T.RowId, 0 as Depth from #Temp as T where T.ParentId is null
union all
select T.RowId, P.Depth + 1
from Parents as P
inner join #Temp as T on T.ParentId = P.RowId
)
select T.Name
from Parents as P
outer apply (select TT.Depth from Parents as TT where TT.RowId = 6) as CALC
left outer join #Temp as T on T.RowId = P.RowId
where P.Depth = CALC.Depth - 1
declare #t table(rowid int, parentid int, name varchar(10))
insert #t values(1,NULL,'Level1')
insert #t values(2,NULL,'Level2')
insert #t values(3,1,'Leaf1')
insert #t values(4,1,'Leaf2')
insert #t values(5,2,'Leaf1')
insert #t values(6,3,'LeafX')
;with a as
(
select rowid, parentid, 0 level from #t where name = 'leafx'
union all
select t.rowid, t.parentid, level + 1 from #t t
join a on a.parentid = t.rowid
), b as
(
select rowid, parentid,name, 0 level from #t where parentid is null
union all
select t.rowid, t.parentid,t.name, level + 1
from b join #t t on b.rowid = t.parentid
)
select rowid, parentid, name from b
where level = (select max(level)-1 from a)
rowid parentid name
5 2 Leaf1
3 1 Leaf1
4 1 Leaf2

How to get the full Hierarchy with SQL CTE

Hi I am trying to get the full hierarchy of my category. Here is my sample table
ID PARENT_ID NAME DEPTH
------------------------------------------
1 NULL A 1
2 NULL B 1
3 NULL C 1
4 1 D 2
5 4 E 3
The cte output should be this
ID PARENT_ID NAME
---------------------------
1 NULL A
2 NULL B
3 NULL C
4 1 D
5 4 E
5 1 E
As you can see id:5 is parent of 4 and 1. How do i query the whole tree
DECLARE #tmp TABLE(ID INT,ParentID INT,NAME VARCHAR(10),DEPTH INT)
INSERT INTO #tmp VALUES
(1 ,NULL ,'A' ,1 ),
(2 ,NULL ,'B' ,1 ),
(3 ,NULL ,'C' ,1 ),
(4 ,1 ,'D' ,2 ),
(5 ,4 ,'E' ,3 ),
(6 ,5 ,'F' ,4 );
select * from #tmp
;WITH cte AS
(
SELECT A.ID
,A.ParentID
,A.NAME
,A.DEPTH
FROM #tmp A
UNION ALL
SELECT A.ID
,B.ParentID
,A.NAME
,A.DEPTH
FROM cte A
INNER JOIN #tmp B on A.ParentID = B.ID
WHERE B.ParentID IS NOT NULL
)
SELECT * FROM cte
In SQL Server 2008 CTE can be use to query recursively.
Example of CTE from MSDN
-- Sample Solution (Untested)
;With TableCTE(Id, Name, ParentId, Depth)
(
Select ID,Name,ParentId, 1
FROM MyTable
Union All
Select C.Id, C.Name, t.ParentId, c.Depth +1
FROM #tmp t
INNER JOIN TableCTE C on t.Id = c.ParentId
-- Where t.ParentId IS Not Null
)
SELECT Id, Name, ParentId
FROM TableCTE
;WITH cte AS
(
SELECT A.ID
,A.ParentID
,A.NAME
,A.DEPTH
FROM #tmp A
WHERE A.ParentID IS NULL
UNION ALL
SELECT B.ID
,B.ParentID
,B.NAME
,B.DEPTH
FROM cte A
INNER JOIN
#tmp B on B.ParentID = A.ID
WHERE B.ParentID IS NOT NULL
)
SELECT * FROM cte