SQL Query Parent Child - sql

Can you help me?
Table: LOS
ID Name ParentID
1 Item Null
2 Pharmacy 1
3 Consumable 1
4 Ethical 2
5 Non Ethical 2
6 MCCP 4
7 Nurse 3
Table: Item
id ItemName lOS_Id Los_Name
123 Panadol 6 MCCP
321 Nacl 7 Nurse
Expected Result.
ID ItemName ParentID ParentName LOS_Id Los_Name
123 Panadol 2 Pharmacy 6 MCCP
321 Nacl 3 Consumable 7 Nurse
Explanation : ParentID from los_id join LOS Table using ID THEN Get parentID to find Parent

Just see this example.
Declare #LOS table (ID int, Name nvarchar(50), ParentID int)
Declare #Item table (ID int, ItemName nvarchar(50), lOS_ID int, lOS_Name nvarchar(50) )
Insert into #LOS values (1, 'Item',null), (2 ,'Pharmacy', 1), (3 ,'Consumable', 1), (4 ,'Ethical', 2), (5 ,'Non Ethical', 2), (6 ,'MCCP', 4)
, (7 ,'Nurse', 3)
Insert into #Item values (123, 'Panadol',6 ,'MCCP'), (321, 'Nacl',7 ,'Nurse')
select * from #LOS
select * from #Item
;with cte as
(
select i.id, ItemName, lOS_ID, lOS_Name, lOS_ID parentid, lOS_ID parentparentid, lOS_Name parentname from #Item i
join #los l on i.lOS_ID = l.id
-- where i.id = 123
union all
select
c.id, ItemName, lOS_ID, lOS_Name, l.ID parentid, l.ParentID parentparentid, l.Name parentname
from
cte c join #los l on c.parentparentid = l.id --and c.ParentID <> null
)
select * from cte where parentparentid = 1
order by id

I hope I understood you correctly and this query will fix your problem :)
select I.ID, I.ItemName, A.ParentID, A.ParentName, I.LOS_ID, I.LOS_Name from Item AS I join (
select l1.id, l1.name, l1.ParentID, l2.name as ParentName from LOS as l1 join LOS as l2 on l1.id = l2.ParentID
) as A join on I.LOS_ID = A.ID

Related

Recursive CTE to find Total for all children parent and depth

I hope use Recursive CTE to find Total for all childrencount and parentcount and path and level and currentlevel like this
Id ParentId Name Path Level CurrentLevel ChildrenCount ParentCount
1 NULL a 1 4 1 3 0
3 1 c 3,1 3 2 2 1
4 3 d 4,3,1 2 3 1 2
5 4 f 5,4,3,1 1 4 0 3
2 NULL b 2 5 1 5 0
6 2 g 6,2 4 2 4 1
7 6 h 7,6,2 3 3 3 2
8 7 i 8,7.6.2 2 4 2 3
9 8 j 9,8,7,6,2 1 5 0 4
10 8 k 10,8,7,6,2 1 5 0 4
I tried the following code, but I don't know how to get childrencount and parentcount and path and level and currentlevel, how to code it dynamically calculate it.
CREATE TABLE #temp([id] int, [parentid] int null,[name] varchar(5));
INSERT INTO #temp ([id], [parentid], [name])
VALUES ('1', null,'a')
, ('2', null,'b')
, ('3', '1','c')
, ('4', '3','d')
, ('5', '4','e')
, ('6', '2','f')
, ('7', '6','h')
, ('8', '7','i')
, ('9', '8','j')
, ('10', '8','k')
WITH AllChildrens as
(
SELECT p.*, CAST(P.Id AS VarChar(Max)) as [Path]
FROM Department P where p.ParentId is null
UNION ALL
SELECT P1.*, CAST(P1.Id AS VarChar(Max)) + ',' + M.[Path]
FROM Department P1
INNER JOIN AllChildrens M
ON M.Id = P1.ParentId
)
SELECT Id,ParentId,Name,Path From AllChildrens order by Id
One option is to use the data type hierarchyid
Example
;with cteP as (
Select ID
,parentid
,Name
,HierID = convert(hierarchyid,concat('/',ID,'/'))
From #Temp
Where parentid is null
Union All
Select ID = r.ID
,parentid = r.parentid
,Name = r.Name
,HierID = convert(hierarchyid,concat(p.HierID.ToString(),r.ID,'/'))
From #Temp r
Join cteP p on r.parentid = p.ID)
Select ID
,parentid
,Name
,Path = HierID.ToString()
,Depth = ( Select max(HierID.GetLevel() ) from cteP where HierID.ToString() like A.HierID.ToString()+'%') - HierID.GetLevel()
,Lvl = HierID.GetLevel()
,ChildCnt = ( Select count(*) from cteP where HierID.ToString() like A.HierID.ToString()+'%') -1
,ParentCnt = len(HierID.ToString()) - len(replace(HierID.ToString(),'/','')) - 2
From cteP A
Order By A.HierID
Results

SQL: Get Parent Tree Hierarchy from child

I have a table with Id, ParentId, Tree, TopParentId. Here's an example structure:
0
___/ \___
/ \
1 4
/ \ / \
2 7 5 8
/ /
3 6
Input Table:
Id ParentId Tree TopParentId
--- ---------- -------------------- --------
1 0 NULL NULL
2 1 NULL NULL
7 1 NULL NULL
3 2 NULL NULL
4 0 NULL NULL
5 4 NULL NULL
6 5 NULL NULL
8 4 NULL NULL
This is the output I am looking for when I pass Id = 3
OUTPUT:
Id ParentId Tree TopParentId
--- ---------- -------------------- --------
1 2 3 > 2 > 1 > 0 0
The CTE query should be able to handle multiple ids like 3,7
OUTPUT:
Id ParentId Tree TopParentId
--- ---------- -------------------- --------
3 2 3 > 2 > 1 > 0 0
7 1 7 > 1 > 0 0
The end goal is to take the Tree and TopParentId columns and update the corresponding Id
Here's the query I have tried till now:
WITH CTE AS (
SELECT Id, ParentId,0 AS [Level], CAST(Id AS varchar(1000)) AS Heirarchy,Id AS TopParentId
FROM dbo.table
WHERE Id IN (SELECT Id FROM table WHERE ParentId IS NULL)
UNION ALL
SELECT mgr.Id, mgr.ParentId, TASKCTE.[Level] +1 AS [Level],
CAST(( CAST(mgr.Id AS VARCHAR(1000)) + '>' + CTE.Heirarchy) AS varchar(1000)) AS Heirarchy, CTE.TopParentId
FROM CTE
INNER JOIN dbo.table AS mgr
ON CTE.Id = mgr.ParentId
)
UPDATE t SET t.[LEVEL] = TC.[LEVEL], t.ParentTree = TC.Heirarchy, t.TopParentId = TC.TopParentId
FROM dbo.table AS t
JOIN (SELECT * FROM CTE WHERE Id IN(SELECT DISTINCT Id FROM INSERTED) AND ParentId IS NOT NULL) TC
ON
t.Id = TC.Id
The above query works but its CPU/RAM intensive as it starts from Parent. I need the CTE to start from the Child but the Tree needs to be exactly same as the example outputs.
This appears to work:
drop table if exists #t;
with cte as (
select * from (values
(1, 0),
(2, 1),
(7, 1),
(3, 2),
(4, 0),
(5, 4),
(6, 5),
(8, 4)
)
as x(ID, ParentID)
)
select *
into #t
from cte;
with cte as (
select * ,
ID as [start] ,
ParentID as [FirstParent] ,
1 as [level] ,
cast(ID as varchar(max)) as Tree
from #t
where ID in (3, 7)
union all
select p.*,
c.[start] ,
c.FirstParent ,
c.[level] + 1 ,
cast(concat(c.Tree, ' > ', p.ID) as varchar(max))
from cte as c
join #t as p
on c.ParentID = p.ID
), top_level as (
select *, row_number() over (partition by [start] order by [level] desc) as rn
from cte
)
select [start] as ID ,
FirstParent as ParentID ,
concat(Tree, ' > ', ParentID) ,
ParentID as TopParentID
from top_level
where rn = 1;
By way of exposition, the first part just creates your test data (pro-tip: if you do this, people are more likely to be able to help since you've lowered the friction to do so!). The meat of the solution just uses the desired IDs as the "base case" for the recursion and the recursive step says "take the previous level's ParentID to find the next level's ID". The rest is just to keep track of the starting and ending point.
You simply need to create procedure to get the desired result.
Try this or variant of this will help.
Create proc CalculateLevel
( #passid nvarchar(max) )
As
Begin
declare #query nvarchar(max)
set #query = '
WITH CTE AS (
SELECT Id, ParentId,0 AS [Level], CAST(Id AS varchar(1000)) AS Heirarchy,Id AS TopParentId
FROM dbo.tab
WHERE Id IN (SELECT Id FROM tab WHERE ParentId IS NULL)
UNION ALL
SELECT mgr.Id, mgr.ParentId, CTE.[Level] +1 AS [Level],
CAST(( CAST(mgr.Id AS VARCHAR(1000)) + ''>'' + CTE.Heirarchy) AS varchar(1000)) AS Heirarchy, CTE.TopParentId
FROM CTE
INNER JOIN dbo.tab AS mgr
ON CTE.Id = mgr.ParentId
)
UPDATE t SET t.[LEVEL] = TC.[LEVEL], t.ParentTree = TC.Heirarchy, t.TopParentId = TC.TopParentId
FROM dbo.tab AS t
JOIN (SELECT * FROM CTE WHERE ParentId IS NOT NULL) TC
ON
t.Id = TC.Id where t.id in ( ' + #passid + ')'
print #query
exec sp_executesql #query
End

How to join tables, concatenate some data

I have two tables:
1. indexes and quantity of indexes
2. indexes and quantity of indexes with specified boxcodes. Boxcode is a number of box, which box contains indexes.
1. input table 1
item_id quantity
1 10
2 15
3 5
1 5
1 5
2 5
3 5
sum:
1 - 20
2 - 20
3 - 10
2. input table 2
item_id quantity boxcode
1 3 abc
2 2 abc
1 8 def
3 10 ghi
1 9 ghi
2 9 def
2 8 ghi !!!!!!!
1 item_id once on 1 boxcode
I want to get result:
3. result
item_id quantity boxcodes
1 10 abc/3, def/7
2 15 abc/2, def/9, ghi/4
3 5 ghi/5
1 5 def/1, ghi/4
1 5 ghi/5
2 5 ghi/4 !!!!!!!!
3 5 ghi/5
Records from table 1 must be in the same order.
I have no idea how it can be done.
Any suggestion?
CREATE TABLE #input1
(
rownum int,
item_id int,
quantity int
)
CREATE TABLE #input2
(
item_id int,
quantity int,
boxcode varchar(10)
)
INSERT INTO #input1 VALUES (1,1,10)
INSERT INTO #input1 VALUES (2,2,15)
INSERT INTO #input1 VALUES (3,3,5)
INSERT INTO #input1 VALUES (4,1,5)
INSERT INTO #input1 VALUES (5,1,5)
INSERT INTO #input1 VALUES (6,2,5)
INSERT INTO #input1 VALUES (7,3,5)
INSERT INTO #input2 VALUES (1,3, 'abc')
INSERT INTO #input2 VALUES (2,2, 'abc')
INSERT INTO #input2 VALUES (1,8, 'def')
INSERT INTO #input2 VALUES (3,10, 'ghi')
INSERT INTO #input2 VALUES (1,9, 'ghi')
INSERT INTO #input2 VALUES (2,9, 'def')
INSERT INTO #input2 VALUES (2,8, 'ghi')
select * from #input1
select * from #input2
drop table #input1
drop table #input2
result
Thanks,
Weird, but it works:
;WITH rec1 AS (
SELECT rownum,
item_id,
1 as q,
1 as [Level],
quantity
from #input1
UNION ALL
SELECT r.rownum,
r.item_id,
1,
[Level] + 1,
i.quantity
FROM rec1 r
INNER JOIN #input1 i
ON r.rownum = i.rownum AND r.item_id = i.item_id
WHERE [Level] < i.quantity
), rec2 AS (
SELECT boxcode,
item_id,
1 as q,
1 as [Level],
quantity
from #input2
UNION ALL
SELECT r.boxcode,
r.item_id,
1,
[Level] + 1,
i.quantity
FROM rec2 r
INNER JOIN #input2 i
ON r.boxcode = i.boxcode AND r.item_id = i.item_id
WHERE [Level] < i.quantity
), cte1 AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY item_id, rownum) as rn
FROM rec1
), cte2 AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY item_id, boxcode) as rn
FROM rec2
), final AS (
SELECT c1.rownum,
c1.item_id,
c1.quantity,
c2.boxcode+'/'+CAST(SUM(c2.q) as nvarchar(10)) as boxcodes
FROM cte1 c1
INNER JOIN cte2 c2
ON c1.item_id = c2.item_id and c1.rn = c2.rn
GROUP BY c1.rownum, c1.item_id, c1.quantity, c2.boxcode
)
SELECT DISTINCT
f.rownum,
f.item_id,
f.quantity,
STUFF((
SELECT ', '+f1.boxcodes
FROM final f1
WHERE f1.rownum = f.rownum
AND f1.item_id = f.item_id
AND f1.quantity = f.quantity
FOR XML PATH('')
),1,2,'') boxcodes
FROM final f
Output for dataset you have provided:
rownum item_id quantity boxcodes
1 1 10 abc/3, def/7
2 2 15 abc/2, def/9, ghi/4
3 3 5 ghi/5
4 1 5 def/1, ghi/4
5 1 5 ghi/5
6 2 5 ghi/4
7 3 5 ghi/5
The main idea is to spread quantity in both tables for a small parts 1. Than add row number, then join and get result.
A solution (but it's totally based on gofr1's answer, to be honest !), to simplify a bit, would be to create a Numbers table, with as many numbers as you want.
CREATE TABLE Numbers(Number INT PRIMARY KEY);
INSERT Numbers
SELECT TOP 1000 ROW_NUMBER() OVER (ORDER BY name)
FROM sys.all_columns;
That would just avoid the 2 recursive CTEs.
You could then use the same logic as gofr1 :
with rec1 AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY item_id, rownum) as rn,
rownum,
item_id,
case when quantity = 0 then 0 else 1 end as q,
quantity
from #input1
join Numbers n on n.Number <= quantity
)
, rec2 AS (
SELECT
ROW_NUMBER() OVER (PARTITION BY item_id ORDER BY item_id, boxcode) as rn,
boxcode,
item_id,
case when quantity = 0 then 0 else 1 end as q,
quantity
from #input2
join Numbers n on n.Number <= quantity
),
final AS (
SELECT c1.rownum,
c1.item_id,
c1.quantity,
c2.boxcode+'/'+CAST(SUM(c2.q) as nvarchar(10)) as boxcodes
FROM rec1 c1
INNER JOIN rec2 c2
ON c1.item_id = c2.item_id and c1.rn = c2.rn
GROUP BY c1.rownum, c1.item_id, c1.quantity, c2.boxcode
),
stuffed as (
SELECT
distinct rownum,
f.item_id,
f.quantity,
STUFF((
SELECT ', '+f1.boxcodes
FROM final f1
WHERE f1.rownum = f.rownum
AND f1.item_id = f.item_id
AND f1.quantity = f.quantity
FOR XML PATH('')
),1,2,'') boxcodes
FROM final f
group by item_id, quantity, boxcodes, rownum)
select *
from stuffed
order by rownum

How to all possible child rows in a SQL table when using id parentid relationship in single table

I have the following table schema:
ID | PARENT ID | NAME
-------------------------
1 | NULL | A - ROOT
2 | 1 | B
3 | 2 | C
4 | 1 | D
5 | 4 | E
6 | 5 | F
The hierarchy look like:
A
-- B
-- -- C
-- D
-- -- E
-- -- -- F
I want to get all child recursively in all descendant levels.
For example when I have A and query for it, I would like to get back A, B, C, D, E and F.
When I have E I want to get E and F.
When I have D I want to get D, E and F.
I am not SQL expert and as a developer normally I would build programmatically loops with DB query and check whether I have children or not and recursively get the children. But this i definitely a very expensive/unperformant approach.
Is there an elegant/better way by using a SQL statement?
Here is a generic hierarchy build. This one will maintain presentation sequence and also includes RANGE KEYS (optional and easy to remove if not needed)
Declare #YourTable table (ID int,Parent_ID int,Name varchar(50))
Insert into #YourTable values (1,null,'A - Root'),(2,1,'B'),(3,2,'C'),(4,1,'D'),(5,4,'E'),(6,5,'F')
Declare #Top int = null --<< Sets top of Hier Try 4
Declare #Nest varchar(25) =' ' --<< Optional: Added for readability
;with cteHB (Seq,ID,Parent_ID,Lvl,Name) as (
Select Seq = cast(1000+Row_Number() over (Order by Name) as varchar(500))
,ID
,Parent_ID
,Lvl=1
,Name
From #YourTable
Where IsNull(#Top,-1) = case when #Top is null then isnull(Parent_ID,-1) else ID end
Union All
Select Seq = cast(concat(cteHB.Seq,'.',1000+Row_Number() over (Order by cteCD.Name)) as varchar(500))
,cteCD.ID
,cteCD.Parent_ID,cteHB.Lvl+1
,cteCD.Name
From #YourTable cteCD
Join cteHB on cteCD.Parent_ID = cteHB.ID)
,cteR1 as (Select Seq,ID,R1=Row_Number() over (Order By Seq) From cteHB)
,cteR2 as (Select A.Seq,A.ID,R2=Max(B.R1) From cteR1 A Join cteR1 B on (B.Seq like A.Seq+'%') Group By A.Seq,A.ID )
Select B.R1
,C.R2
,A.ID
,A.Parent_ID
,A.Lvl
,Name = Replicate(#Nest,A.Lvl) + A.Name
From cteHB A
Join cteR1 B on A.ID=B.ID
Join cteR2 C on A.ID=C.ID
Order By B.R1 --<< Or use A.Seq
Returns
R1 R2 ID Parent_ID Lvl Name
1 6 1 NULL 1 A - Root
2 3 2 1 2 B
3 3 3 2 3 C
4 6 4 1 2 D
5 6 5 4 3 E
6 6 6 5 4 F
you can use Common table expressions as below:
;with cte as
(
select id, name from hierarchy where parentid is null
union all
select h.id, h.name from hierarchy h inner join cte c
on h.parentid = c.id
)
select * from cte
This will give you the results you want:
DECLARE #search nvarchar(1) = 'D'
;WITH cte AS (
SELECT ID,
[PARENT ID],
NAME
FROM YourTable
WHERE NAME = #search
UNION ALL
SELECT y.ID,
y.[PARENT ID],
y.NAME
FROM YourTable y
INNER JOIN cte c
ON y.[PARENT ID] = c.ID
)
SELECT *
FROM cte
ORDER BY NAME

Join get wrong information on query

I'm having some problems with join query. What I want is select rows of two tables that have some similar states, others no. And I have a state-equivalence view
Table Sales:
ID ..... StateID
1 1
2 1
3 6
Table Orders
ID ..... StateID
11 2
12 2
15 3
Table StatesEquivalence
ID SalesState OrdersState StateName
1 1 2 Attended
2 2 3 Declined
I've made a union of both tables(Sales and Orders) and I want a column with the EquivalentStateID and the StateName by doing something like this:
SELECT sales.ID as ID,equivalence.ID as State,equivalence.StateName
FROM Sales
INNER JOIN StatesEquivalence as equivalence
ON sales.StateID = equivalence.SalesState
WHERE sales.ID = 1
UNION
SELECT orders.ID as ID,equivalence.ID as State,equivalence.StateName
FROM Orders as orders
INNER JOIN StatesEquivalence as equivalence
ON orders.StateID = equivalence.OrdersState
Somehow I am obtaining wrong information on the equivalentID
ID State StateName
1 2 Attended
2 2 Attended
11 2 Attended
...
I don't know what happend.. because the statename is correct, but the StateID is showing wrong information
The table might show this:
ID State StateName
1 1 Attended
2 1 Attended
11 1 Attended
...
I don't think you are showing us the real problem code. I did the following:
begin transaction
create table sales (id int, stateid int)
create table orders (id int, stateid int)
create table statesequivalence (id int, salesstate int, ordersstate int, statename varchar(20))
insert into sales values (1, 1)
insert into sales values (2, 1)
insert into sales values (3, 6)
insert into orders values (11, 2)
insert into orders values (12, 2)
insert into orders values (15, 3)
insert into statesequivalence values (1, 1, 2, 'Attended')
insert into statesequivalence values (2, 2, 3, 'Declined')
SELECT sales.ID as ID,equivalence.ID as State,equivalence.statename
FROM Sales
INNER JOIN statesequivalence as equivalence
ON sales.StateID = equivalence.SalesState
WHERE sales.ID = 1
UNION
SELECT orders.ID as ID,equivalence.ID as State,equivalence.StateName
FROM Orders as orders
INNER JOIN statesequivalence as equivalence
ON orders.StateID = equivalence.OrdersState
rollback
Result:
ID State statename
----------- ----------- --------------------
1 1 Attended
11 1 Attended
12 1 Attended
15 2 Declined
So, it works. Can you try the above code?
Following is a testscript and the output from your statement.
Perhaps it's easier to work from here and you tell us what is wrong with this output.
The testscript can be adjusted and executed here
Output
ID State StateName
----------- ----------- ---------
1 1 Attended
11 1 Attended
12 1 Attended
15 2 Declined
Test Script
;WITH Sales AS (
SELECT * FROM (VALUES
(1, 1)
, (2, 1)
, (3, 6)
) AS Sales (ID, StateID)
)
, Orders AS (
SELECT * FROM (VALUES
(11, 2)
, (12, 2)
, (15, 3)
) AS Orders (ID, StateID)
)
, StatesEquivalence AS (
SELECT * FROM (VALUES
(1, 1, 2, 'Attended')
, (2, 2, 3, 'Declined')
) AS StatesEquivalence (ID, SalesState, OrdersState, StateName)
)
SELECT sales.ID as ID
, equivalence.ID as State
, equivalence.StateName
FROM Sales
INNER JOIN StatesEquivalence as equivalence ON sales.StateID = equivalence.SalesState
WHERE sales.ID = 1
UNION
SELECT orders.ID as ID
, equivalence.ID as State
, equivalence.StateName
FROM Orders as orders
INNER JOIN StatesEquivalence as equivalence ON orders.StateID = equivalence.OrdersState
I didn't understand what you need exactly, but maybe is this?
select *
from StatesEquivalence EQ
full join Sales S on EQ.SalesState = S.StateId
full join Orders O on EQ.OrdersState = O.StateId
This is a starting point maybe...