Tree view using SQL Query - sql

I have a regions table of which I want a tree view (table simple ordered as tree) is it possible using sql queries help is appreciated, I tried to do it using self joins but i did not get the desired result.
tree view is something like this
Indiv
Div1
Zon1
div2
zon2
div3
zon3
EDIT:
as per Charles Bretana suggetion I tried CTE in below query and it did not give me desired result.
WITH Emp_CTE (id, ParentID, name)
AS (
SELECT id, ParentID, name
FROM eQPortal_Region
WHERE ParentID=0
UNION ALL
SELECT e.id, e.ParentID, e.name
FROM eQPortal_Region e
INNER JOIN Emp_CTE ecte ON ecte.id = e.ParentID
)
SELECT *
FROM Emp_CTE
GO
This is the result .. what went wrong ?
InDiv1
Div1
Div2
Div3
Zon3
Zon2
zon1

This guy Maulik Dhorajia answers the question perfectly ...
http://maulikdhorajia.blogspot.com/2012/06/sql-server-using-ctecommon-table.html
Made a replica of the query ..
;WITH CTECompany
AS
(
SELECT
ID,
ParentID,
Name ,
0 AS HLevel,
CAST(RIGHT(REPLICATE('_',5) + CONVERT(VARCHAR(20),ID),20) AS VARCHAR(MAX)) AS OrderByField
FROM Region
WHERE ParentID = 0
UNION ALL
SELECT
C.ID,
C.ParentID,
C.Name ,
(CTE.HLevel + 1) AS HLevel,
CTE.OrderByField + CAST(RIGHT(REPLICATE('_',5) + CONVERT(VARCHAR(20),C.ID),20) AS VARCHAR(MAX)) AS OrderByField
FROM Region C
INNER JOIN CTECompany CTE ON CTE.ID = C.ParentID
WHERE C.ParentID IS NOT NULL
)
-- Working Example
SELECT
ID
, ParentID
, HLevel
, Name
, (REPLICATE( '----' , HLevel ) + Name) AS Hierachy
FROM CTECompany
ORDER BY OrderByField

See the following sample SQL script:
DECLARE #lV NCHAR(1)=NCHAR(9474),#lR NCHAR(1)=NCHAR(9500),#lU NCHAR(1)=NCHAR(9492),#spc NCHAR(1)=' ';
WITH data AS(SELECT * FROM(VALUES(1,'0','In Div1',1),(2,'1','Div1',1),(3,'2','zon1',1),(4,'1','Div2',2),(5,'1','Div3',3),(6,'4','Zon2',1),(7,'5','Zon3',1))t([ID],[ParentID],[Name],[SeqOrder])
),d_root AS(SELECT CAST('/'AS NVARCHAR(MAX))[n_path],* FROM data WHERE ParentID='0'
),d_cte AS(SELECT b.n_path+CAST(ROW_NUMBER()OVER(ORDER BY a.ID)AS NVARCHAR(MAX))+'/'[n_path],a.* FROM data a INNER JOIN d_root b ON a.ParentID=b.ID UNION ALL SELECT b.n_path+CAST(ROW_NUMBER()OVER(ORDER BY a.ID)AS NVARCHAR(MAX))+'/'[n_path],a.* FROM data A INNER JOIN d_cte b ON a.ParentID=b.ID
),Tree_BASE AS (SELECT CAST(n_path AS HIERARCHYID)[h_id],* FROM d_root UNION ALL SELECT CAST(n_path AS HIERARCHYID)[h_id],* FROM d_cte
),cte_o AS(SELECT a.h_id,CASE WHEN EXISTS(SELECT * FROM Tree_BASE WHERE h_id>b.h_id AND h_id.GetAncestor(1)=b.h_id.GetAncestor(1))THEN #lV ELSE #spc END[t_l] FROM Tree_BASE a INNER JOIN Tree_BASE b ON a.h_id.IsDescendantOf(b.h_id)=1 AND NOT b.h_id=HIERARCHYID::GetRoot()
),cte_m AS(SELECT x.*,(SELECT t_l+'' FROM cte_o WHERE h_id=x.h_id FOR XML PATH(''))[b_tree] FROM Tree_BASE x
),cte_h AS(SELECT ISNULL(LEFT(b_tree,LEN(b_tree)-1)+CASE WHEN EXISTS(SELECT * FROM Tree_BASE WHERE h_id>a.h_id AND h_id.GetAncestor(1)=a.h_id.GetAncestor(1))THEN #lR ELSE #lU END,'')+' '+RTRIM(Name) [TreeV],a.h_id FROM cte_m a
)SELECT b.TreeV,ROW_NUMBER()OVER(ORDER BY a.h_id ASC)[row_id]FROM Tree_BASE a INNER JOIN cte_h b ON a.h_id=b.h_id ORDER BY row_id

Related

Re-writting the query without "Connect By "

I am rewriting the query to replace to remove CONNECT BY:
SELECT *
FROM ADM_TRT AT
INNER JOIN UTILISATEUR U
ON U.UTI_ID = AT.UTI_ID
INNER JOIN
(
SELECT CM.MAI_ID
FROM CON_MAI CM
CONNECT BY CM.MAI_PER_RES = PRIOR CM.MAI_ID
START WITH CM.MAI_ID IN (
SELECT MAJ_ID
FROM DROIT_LOGIN
WHERE LOG_ID = 21543
)
) CON_MAI_FILTERED_ON_LOGIN
ON AT.TRT_MAI_ID = CON_MAI_FILTERED_ON_LOGIN.MAI_ID;
For CONNECT BY Part , I wrote this
WITH tree (MAI_ID,MAI_PER_RES, level1) AS (
SELECT MAI_PER_RES, MAI_ID, 1 as level1 FROM CON_MAI
UNION ALL
SELECT child.MAI_ID, child.MAI_PER_RES, parent.level1 + 1
FROM CON_MAI child --Line 20
JOIN tree parent
on parent.MAI_PER_RES = child.MAI_ID
)
SELECT MAI_ID FROM tree
But I am stuck to integrate this in subquery in the CONNECT BY sub-query. Can someone please help to integrate this?
It looks like you have the recursion reversed in the recursive sub-query and can use:
WITH tree (MAI_ID) AS (
SELECT MAI_ID
FROM CON_MAI
WHERE MAI_ID IN ( SELECT MAJ_ID
FROM DROIT_LOGIN
WHERE LOG_ID = 21543 )
UNION ALL
SELECT c.MAI_ID
FROM CON_MAI c
JOIN tree p
on c.MAI_PER_RES = p.MAI_ID
)
SELECT *
FROM ADM_TRT AT
INNER JOIN UTILISATEUR U
ON U.UTI_ID = AT.UTI_ID
INNER JOIN tree CON_MAI_FILTERED_ON_LOGIN
ON AT.TRT_MAI_ID = CON_MAI_FILTERED_ON_LOGIN.MAI_ID;
(untested as I do not have your tables or data)

Recursive Parent/Child in same table query in SQL where parent is PK

I have seen a lot of examples about how to implement a recursive query where there is the parent and the child in the same table, but in the examples, the child has a parent and I need at the contrary, when a parent has a child.
I would like to obtain all children in recursive mode just like in the image.
In the image, you can see, I have a parent with id 1, it has a child with id 2. The child 2 is a parent too who has a child with id 3, etc.
I don't know how to create a recursive query to obtain all the childs from a parent.
You can access to the next link to execute the sql online: http://www.sqlfiddle.com/#!18/dbed2/1
This produces the results you are asking for:
with cte as (
select idchild, idparent,
convert(varchar(max), idchild) as children
from family f
where not exists (select 1 from family f2 where f2.idparent = f.idchild)
union all
select f.idchild, f.idparent,
concat(f.idchild, ',', cte.children)
from cte join
family f
on cte.idparent = f.idchild
)
select *
from cte
order by idchild;
Here is the SQL Fiddle.
Here you go:
with
n as (
select idparent, idchild, 1 as lvl,
cast(concat('', idchild) as varchar(255)) as children from family
union all
select n.idparent, f.idchild, lvl + 1,
cast(concat(children, ',', f.idchild) as varchar(255))
from n
join family f on f.idparent = n.idchild
)
select n.idparent, f.idchild, n.children
from n
join (
select idparent, max(lvl) as maxlvl from n group by idparent
) m on n.idparent = m.idparent and n.lvl = m.maxlvl
join family f on f.idparent = n.idparent
order by n.idparent
See SQL Fiddle.
if you are using SQL Server 2017 or newer you can use the following:
WITH CTE
AS (SELECT *
FROM dbo.Table_1
UNION ALL
SELECT Child.idParent,
Parent.idChild
FROM CTE AS Parent
INNER JOIN dbo.Table_1 AS Child
ON Parent.idParent = Child.idChild)
SELECT CTE.idParent,
STRING_AGG(CTE.idChild, ', ') AS Childs
FROM CTE
GROUP BY CTE.idParent;
but if you have older version use the following :
WITH CTE
AS (SELECT *
FROM dbo.Table_1
UNION ALL
SELECT Child.idParent,
Parent.idChild
FROM CTE AS Parent
INNER JOIN dbo.Table_1 AS Child
ON Parent.idParent = Child.idChild)
SELECT DISTINCT
B.idParent,
STUFF(
(
SELECT ',' + CONVERT(VARCHAR(10), CTE.idChild)
FROM CTE
WHERE B.idParent = CTE.idParent
ORDER BY CTE.idChild
FOR XML PATH('')
),
1,
1,
''
) AS Childs
FROM CTE AS B

Existing query optimization

We have 5 tables and we are trying to create a view to get the results.
Below is the view which is working fine.
I need suggestions. Is it a good practice to write this query in this way or it can be optimized in a better way.
SELECT p.Pid, hc.hcid, hc.Accomodation, ghc.ghcid, ghc.ProductFeatures, wp.existing, wp.acute, mc.cardiaccover, mc.cardiaclimitationperiod
FROM TableA p
LEFT JOIN TableB hc
ON p.pid = hc.pid
LEFT JOIN TableC ghc
ON p.pid = ghc.pid
LEFT JOIN (SELECT *
FROM (SELECT hcid,
title,
wperiodvalue + '-' + CASE WHEN
wperiodvalue > 1 THEN
unit +
's' ELSE
unit END wperiod
FROM TableD) d
PIVOT ( Max(wperiod)
FOR title IN (acute,
existing
) ) piv1) wp
ON hc.hcid = wp.hcid
LEFT JOIN (SELECT *
FROM (SELECT hcid,
title + col new_col,
value
FROM TableE
CROSS apply ( VALUES (cover,
'Cover'),
(Cast(limitationperiod AS
VARCHAR
(10)),
'LimitationPeriod') ) x (value, col
)) d
PIVOT ( Max(value)
FOR new_col IN (cardiaccover,
cardiaclimitationperiod,
cataracteyelenscover,
cataracteyelenslimitationperiod
) ) piv2) mc
ON hc.hcid = mc.hcid
Any suggestions would be appreciated.
Thanks
My suggestion is to break down the query using temporary table, create stored procedure then dump data in the one new table and with the help of that table you can create view:
Store both PIVOT result in tow seperate temp tables as
SELECT * INTO #pvtInfo FROM ( --first PIVOT query
SELECT * INTO #pvtInfoTwo FROM ( --second PIVOT query
Then your final query will be as :
SELECT p.Pid,
hc.hcid,
hc.Accomodation,
ghc.ghcid,
ghc.ProductFeatures,
wp.existing,
wp.acute,
mc.cardiaccover,
mc.cardiaclimitationperiod
FROM TableA p
LEFT JOIN TableB hc ON p.pid = hc.pid
LEFT JOIN TableC ghc ON p.pid = ghc.pid
LEFT JOIN #pvtInfo wp ON hc.hcid = wp.hcid
LEFT JOIN #pvtInfoTwo mc ON hc.hcid = mc.hcid
First you can try then only go with SP and VIEW.
Hope, It will help.

Troubles isolating target cell in recursive sql query

I have a table, let's say it looks like this:
c | p
=====
|1|3|
|2|1|
|7|5|
c stands for current and p stands for parent
Given a c value of 2 I would return its top most ancestor (which has no parent) this value is 3. Since this is a self referencing table, I figured using CTE would be the best method however I am very new to using it. Nevertheless, I gave it a shot:
WITH Tree(this, parent) AS
( SELECT c ,p
FROM myTable
WHERE c = '2'
UNION ALL
SELECT M.c ,M.p
FROM myTable M
JOIN Tree T ON T.parent = M.c )
SELECT parent
FROM Tree
However this returns:
1
3
I only want 3 though. I have tried putting WHERE T.parent <> M.c but that doesn't entirely make sense. Neadless to say, I am a little confused for how to isolate the grandparent.
DECLARE #Table AS TABLE (Child INT, Parent INT)
INSERT INTO #Table VALUES (1,3),(2,1),(7,5)
;WITH cteRecursive AS (
SELECT
OriginalChild = Child
,Child
,Parent
,Level = 0
FROM
#Table
WHERE
Child = 2
UNION ALL
SELECT
c.OriginalChild
,t.Child
,t.Parent
,Level + 1
FROM
cteRecursive c
INNER JOIN #Table t
ON c.Parent = t.Child
)
SELECT TOP 1 TopAncestor = Parent
FROM
cteRecursive
ORDER BY
Level DESC
Use a recursive cte to Recuse up the tree until you cannot. Keep track of the Level of recursion, then take the last level of recursions parent and you have the top ancestor.
And just because I wrote it I will add in if you wanted to find the top ancestor of every child. The concept is still the same but you would need to introduce a row_number() to find the last level that was recursed.
DECLARE #Table AS TABLE (Child INT, Parent INT)
INSERT INTO #Table VALUES (1,3),(2,1),(7,5),(5,9)
;WITH cteRecursive AS (
SELECT
OriginalChild = Child
,Child
,Parent
,Level = 0
FROM
#Table
UNION ALL
SELECT
c.OriginalChild
,t.Child
,t.Parent
,Level + 1
FROM
cteRecursive c
INNER JOIN #Table t
ON c.Parent = t.Child
)
, cteTopAncestorRowNum AS (
SELECT
*
,TopAncestorRowNum = ROW_NUMBER() OVER (PARTITION BY OriginalChild ORDER BY Level DESC)
FROM
cteRecursive
)
SELECT
Child = OriginalChild
,TopMostAncestor = Parent
FROM
cteTopAncestorRowNum
WHERE
TopAncestorRowNum = 1

Want to fetch the data in the form of a table

I have written this query:
SELECT
d.DetailId,
i.ItemId,
d.fieldId,
d.Fieldvalue,
f.Name
FROM
EntityItemDetails d
inner join EntityItems i
on i.ItemId = d.ItemId
inner join Fields f
on f.Id = d.FieldId
WHERE
i.EntityId = 1
Output is:
DetailId ItemId FieldId FieldValue FieldName
1 1 9 Defect1 Name
2 1 10 abcdef Description
5 1 11 testing123 Status
I want result in this way:
Name Description Status
TestField abcdef testing123
Please suggest how to get this result by writing a query in sql.
What you want is called pivoting, and starting from SQL Server 2005, you can use a standard syntax for pivoting in Transact-SQL, with the help of the PIVOT clause.
Your query could be transformed for use with PIVOT like this:
WITH source AS (
/* this CTE is actually your original query */
SELECT
EntityItemDetails.DetailId,
EntityItems.ItemId,
EntityItemDetails.fieldId,
EntityItemDetails.Fieldvalue,
Fields.Name
FROM EntityItemDetails
INNER JOIN EntityItems ON EntityItems.ItemId = EntityItemDetails.ItemId
INNER JOIN Fields ON Fields.Id = EntityItemDetails.FieldId
WHERE EntityItems.EntityId = 1
)
SELECT
ItemId,
Name,
Description,
Status
FROM (
SELECT ItemId, FieldName, FieldValue
FROM source
) s
PIVOT (
MAX(FieldValue) FOR FieldName IN (
Name,
Description,
Status
/* add other possible names as necessary */
)
) p
SELECT
FieldValue,
[Name],
[Description],
[Status]
FROM
myTable -- OR (your posted query) myQuery
PIVOT
(
MIN(FieldValue)
FOR FieldName IN ([Name], [Description], [Status])
) AS myPivot;
Tahnk u so much Andriy M....
with very few changes I could get the desired result. Chenges are as below
WITH source AS (
/* this CTE is actually your original query */
SELECT EntityItemDetails.CreatedDate, EntityItems.EntityItemId,
EntityItemDetails.fieldId,
EntityItemDetails.Fieldvalue,
Fields.Name FieldName FROM EntityItemDetails
INNER JOIN EntityItems ON EntityItems.EntityItemId = EntityItemDetails.EntityItemId
INNER JOIN Fields ON Fields.Id = EntityItemDetails.FieldId WHERE EntityItems.EntityId = 1 and (FieldId=9 or FieldId=10 or FieldId=11) )
SELECT EntityItemId, [Name], [Description], [Status]
FROM ( SELECT EntityItemId, FieldName, FieldValue FROM source ) s
PIVOT ( MAX(FieldValue) FOR FieldName IN ( [Name], [Description], [Status] ) ) p