Assign weight based on hierarchical depth - sql

Take a table with values like below with two columns. I want the output in such a way that the count should be based on the depth of the hierarchy. Eg. A has a dependency on B,D and B has a dependency on C (C has not dependency) and D has dependencies on B,E and E has dependency on C and hence the weight of A is 6.
A B
A D
B C
D B
D E
E C
A 6
B 1
C 0
D 4
E 1
How can I write a SQL server query to achieve this output.

I think does what you want:
with cte as (
select col1, col2
from t
union all
select cte.col1, t.col2
from cte join
t
on t.col1 = cte.col2
)
select col1, sum(w)
from (select cte.col1, col2, 1 as w
from cte
union all
select col2, null, 0
from t
) t
group by col1;
Here is a db<>fiddle.

You could use cte for this:
select
child=cast(child as varchar(10)),
parent=cast(parent as varchar(10))
into #dat
from (values
('A','B'),('A','D'),('B','C'),('D','B'),('D','E'),('E','C')
) x (child,parent)
;with cte
as (
select distinct
parent as node,
0 as level,
parent as chain,
isleaf = case when exists (select 1 from #dat dpy where dpy.child=dp.parent) then 1 else 0 end
from #dat dp
where not exists (select 1 from #dat dpx where dp.parent=dpx.child)
union all
select
dc.child,
c.level+1,
cast(c.chain+dc.child as varchar(10)),
isleaf = case when exists (select 1 from #dat dcx where dcx.parent=dc.child) then 1 else 0 end
from #dat dc
join cte c
on c.node=dc.parent
)
select
node, sum(level)
from cte
group by node
result:
node weight
A 8
B 1
C 0
D 4
E 1
result of just select * from cte gives you structure overview:
node level chain isleaf
C 0 C 0
B 1 CB 1
E 1 CE 1
D 2 CED 1
A 3 CEDA 0
A 2 CBA 0
D 2 CBD 1
A 3 CBDA 0

Related

SQL get the closest two rows within duplicate rows

I have following table
ID Name Stage
1 A 1
1 B 2
1 C 3
1 A 4
1 N 5
1 B 6
1 J 7
1 C 8
1 D 9
1 E 10
I need output as below with parameters A and N need to select closest rows where difference between stage is smallest
ID Name Stage
1 A 4
1 N 5
I need to select rows where difference between stage is smallest
This query can make use of an index on (name, stage) efficiently:
WITH cte AS (
SELECT TOP 1
a.id AS a_id, a.name AS a_name, a.stage AS a_stage
, n.id AS n_id, n.name AS n_name, n.stage AS n_stage
FROM tbl a
CROSS APPLY (
SELECT TOP 1 *, stage - a.stage AS diff
FROM tbl
WHERE name = 'N'
AND stage >= a.stage
ORDER BY stage
UNION ALL
SELECT TOP 1 *, a.stage - stage AS diff
FROM tbl
WHERE name = 'N'
AND stage < a.stage
ORDER BY stage DESC
) n
WHERE a.name = 'A'
ORDER BY diff
)
SELECT a_id AS id, a_name AS name, a_stage AS stage FROM cte
UNION ALL
SELECT n_id, n_name, n_stage FROM cte;
SQL Server uses CROSS APPLY in place of standard-SQL LATERAL.
In case of ties (equal difference) the winner is arbitrary, unless you add more ORDER BY expressions as tiebreaker.
dbfiddle here
This solution works, if u know the minimum difference is always 1
SELECT *
FROM myTable as a
CROSS JOIN myTable as b
where a.stage-b.stage=1;
a.ID a.Name a.Stage b.ID b.Name b.Stage
1 A 4 1 N 5
Or simpler if u don't know the minimum
SELECT *
FROM myTable as a
CROSS JOIN myTable as b
where a.stage-b.stage in (SELECT min (a.stage-b.stage)
FROM myTable as a
CROSS JOIN myTable as b)

RECURSIVE CTE SQL - Find next available Parent

I have parent child relation SQL table
LOCATIONDETAIL Table
OID NAME PARENTOID
1 HeadSite 0
2 Subsite1 1
3 subsite2 1
4 subsubsite1 2
5 subsubsite2 2
6 subsubsite3 3
RULESETCONFIG
OID LOCATIONDETAILOID VALUE
1 1 30
2 4 15
If i provide Input as LOCATIONDETAIL 6, i should get RULESETCONFIG value as 30
because for
LOCATIONDETAIL 6, parentid is 3 and for LOCATIONDETAIL 3 there is no value in RULESETCONFIG,
LOCATIONDETAIL 3 has parent 1 which has value in RULESETCONFIG
if i provide Input as LOCATIONDETAIL 4, i should get RULESETCONFIG value 15
i have code to populate the tree, but don't know how to find the next available Parent
;WITH GLOBALHIERARCHY AS
(
SELECT A.OID,A.PARENTOID,A.NAME
FROM LOCATIONDETAIL A
WHERE OID = #LOCATIONDETAILOID
UNION ALL
SELECT A.OID,A.PARENTOID,A.NAME
FROM LOCATIONDETAIL A INNER JOIN GLOBALHIERARCHY GH ON A.PARENTOID = GH.OID
)
SELECT * FROM GLOBALHIERARCHY
This will return the next parent with a value. If you want to see all, remove the top 1 from the final select.
dbFiddle
Example
Declare #Fetch int = 4
;with cteHB as (
Select OID
,PARENTOID
,Lvl=1
,NAME
From LOCATIONDETAIL
Where OID=#Fetch
Union All
Select R.OID
,R.PARENTOID
,P.Lvl+1
,R.NAME
From LOCATIONDETAIL R
Join cteHB P on P.PARENTOID = R.OID)
Select Top 1
Lvl = Row_Number() over (Order By A.Lvl Desc )
,A.OID
,A.PARENTOID
,A.NAME
,B.Value
From cteHB A
Left Join RULESETCONFIG B on A.OID=B.OID
Where B.VALUE is not null
and A.OID <> #Fetch
Order By 1 Desc
Returns when #Fetch=4
Lvl OID PARENTOID NAME Value
2 2 1 Subsite1 15
Returns when #Fetch=6
Lvl OID PARENTOID NAME Value
1 1 0 HeadSite 30
This should do the job:
;with LV as (
select OID ID,PARENTOID PID,NAME NAM, VALUE VAL FROM LOCATIONDETAIL
left join RULESETCONFIG ON LOCATIONDETAILOID=OID
), GH as (
select ID gID,PID gPID,NAM gNAM,VAL gVAL from LV where ID=#OID
union all
select ID,PID,NAM,VAL FROM LV INNER JOIN GH ON gVAL is NULL AND gPID=ID
)
select * from GH WHERE gVAL>0
See here for e little demo: http://rextester.com/OXD40496

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

Optimized SQL Query to retrieve a Particular set of records from Microsoft SQL Server DB

I want to retrieve the set of data from a column in the table.
My Scenario is:
Iam having a table with name table1_data , In that table there is a Column with name "clm_Name", The data in the column is like this
a
b
c
a
b
c
a
b
a
b
c
a
a
b
c
I want to retrieve the data when a b c are in order if order changes it should not retrieve.(i.e, If we write a query on given data the output should be a b c a b c a b a b c a a b c) only the bolded letters should be shown in output.
If you have a column to sort you can do as follows:
DECLARE #Tbl TABLE (OrderId INT, val NVARCHAR(1))
INSERT INTO #Tbl
VALUES
(1,'a'),
(2,'b'),
(3,'c'),
(4,'a'),
(5,'b'),
(6,'c'),
(7,'a'),
(8,'b'),
(9,'a'),
(10,'b'),
(11,'c'),
(12,'a'),
(13,'a'),
(14,'b'),
(15,'c')
;WITH CTE
AS
(
SELECT
*,
ROW_NUMBER() OVER (ORDER BY (SELECT OrderId)) RowId
FROM #Tbl
), Result
AS
(
SELECT
CurrRow.RowId
FROM
CTE CurrRow LEFT JOIN
(SELECT CTE.val , CTE.RowId - 1 RowId FROM CTE) NextRow ON CurrRow.RowId = NextRow.RowId LEFT JOIN
(SELECT CTE.val , CTE.RowId + 1 RowId FROM CTE) PrivRow ON CurrRow.RowId = PrivRow.RowId
WHERE
PrivRow.val = 'a' AND
CurrRow.val = 'b' AND
NextRow.val = 'c'
)
SELECT
*
FROM
CTE C
WHERE
C.RowId IN (
SELECT Result.RowId FROM Result
UNION ALL
SELECT Result.RowId - 1 FROM Result
UNION ALL
SELECT Result.RowId + 1 FROM Result
)
ORDER BY C.OrderId
Output:
OrderId val RowId
1 a 1
2 b 2
3 c 3
4 a 4
5 b 5
6 c 6
9 a 9
10 b 10
11 c 11
13 a 13
14 b 14
15 c 15

SQL Server Select Group by can't group

I have this table:
ID TXT VL
----------
1 A 1
2 B 0
4 C 0
6 D 0
10 D 0
13 E 0
14 C 0
15 E 0
I have no idea how I can select only the first appearance of TXT like this:
ID TXT VL
----------
1 A 1
2 B 0
4 C 0
6 D 0
13 E 0
You can do the following:
select t1.*
from tbl t1
join (select min(id) as id from tbl group by txt) t2 on t1.id = t2.id
In the temporary table you find all first occurrences of each uniq txt and then join it with the original table.
You can do it with ROW_NUMBER() OVER, like this:
WITH CTE AS (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY TXT ORDER BY ID) AS RowN
FROM table
)
SELECT ID, TXT, VL
FROM CTE
WHERE RowN = 1
Hope it helps :)
For more reading:
https://msdn.microsoft.com/en-us/library/ms189461.aspx
And https://msdn.microsoft.com/en-us/library/ms186734.aspx