Sql query for 3 level tree? - sql

I have table and I want get 3 lvl tree.
Example
Node
Id ParentId Name
1 -1 Test 1
2 -1 Test 2
3 1 Test 1.1
4 1 Test 1.2
5 3 Test 1.1.1
6 3 Test 1.1.2
7 5 Test 1.1.1.1
If I filtered ParentId = -1 I want get rows ParentId = -1 and children's +2
lvl.
If I filtered Id = 2 I want get row Id = 2 and children's +2
lvl.
UPDATE
I use MS SQL Server 2008, Entity Framework 6.1.3.
I understand, I can use 3 selects. But I looking effective method

You can use recursive SQL to do this in SQL server.
WITH recCTE (childID, parentID, Name, Depth) Assuming
(
Select
yourTable.id as childid,
yourTable.parentID,
CAST('Test ' + yourTable.id as varchar(20)) as Name
0 as Depth
FROM
yourTable
WHERE
parentID = -1
UNION ALL
Select
yourTable.id as childID,
yourTable.ParentID as ParentID,
recCTE.path + '.' + yourTable.id AS Name
recCTE.depth + 1 as Depth
FROM
recCTE
INNER JOIN yourTable on
recCTE.childID = yourTable.parentID
Where
recCTE.Depth + 1 <= 2
)
SELECT * FROM recCTE;
The bit inside the CTE on top of the UNION is your seed query for the recursive sql. It's the place where your recursive lookup will start. You wanted to start at parentID = -1, so it's here in the WHERE statement.
The bit inside the CTE below the UNION is the recursive term. This joins the recursive CTE back to itself and brings in more data from your table. Joining the id from your table to the childID from the recursive resultset.
The recursive term is where we test to see how deep we've gotten. If the Depth from the CTE + 1 is less than or equal to 2 then we stop adding children to the loop up, ending the loop for that particular leg of the hierarchy.
The last little bit below the CTE is just the part that runs the CTE so you get results back.
These recursive queries are confusing as hell at first, but spend some time with them and you'll find a lot of use for them. You'll also find that they aren't too difficult to write once you have all the parts sussed out.

Related

SQL - first common ancestor in hierarachy

I am attempting to use a CTE to find the first common ancestor in a hierarchy for all values in a source table. I am able to return the entire hierarchy, but cannot seem to find a way to get the first common ancestor.
The ultimate goal is to update the source table to use only common ancestors.
Source table:
Type_Id Id Parent_Id Group_Id
A 3 2 1
A 4 2 1
A 5 4 1
After the CTE the table I get is:
Type_Id Id Child_Id Parent_Id Group_Id Level
A 3 3 2 1 1
A 4 4 2 1 1
A 5 5 4 1 1
A 5 4 2 1 2
What I'm looking to do is update the id of the source so that for each type and group combination, the new id is to be the first common parent. In this case, it would be set all ids to 2 as that's the first common entry.
You can try to use cte recursive self-join by your logic.
;WITH CTE as (
SELECT Type_Id,Id,id Child_Id,Parent_Id,Group_Id,1 Level
FROM T
UNION ALL
SELECT c.Type_Id,c.Id,t.id Child_Id,t.Parent_Id,t.Group_Id,c.Level+1
FROM CTE c
INNER JOIN T t
ON c.Parent_Id = t.ID AND c.Type_Id = t.Type_Id
)
SELECT *
FROM CTE
sqlfiddle
By building a list of all ancestor relationships (including self) and identifying the depth of each node from the root, you can then query all ancestors for the N selected nodes of interest. Any ancestors occurring N times are candidate common ancestors. The one with the deepest depth is the answer.
The following is what I came up with using simplified Node data containing only Id and ParentId. (The initial CTE was adapted from D-Shih's post.)
;WITH Ancestry as (
-- Recursive list of ancestors (including self) for each node
SELECT ChildId = N.Id, AncestorLevel = 0, AncestorId = N.Id, AncestorParentId = N.ParentId
FROM #Node N
UNION ALL
SELECT C.ChildId, AncestorLevel = C.AncestorLevel + 1, AncestorId = P.Id, AncestorParentId = P.ParentId
FROM Ancestry C
INNER JOIN #Node P ON P.Id = C.AncestorParentId
),
AncestryDepth AS (
-- Calculate depth from root (There may be a quicker way)
SELECT *, Depth = COUNT(*) OVER(PARTITION BY ChildId) - AncestorLevel
FROM Ancestry
),
Answer AS (
-- Answer is deepest node that is an ancestor of all selected nodes
SELECT TOP 1 Answer = A.AncestorId
FROM #Selected S
JOIN AncestryDepth A ON A.ChildId = S.Id
GROUP BY A.AncestorId, A.Depth
HAVING COUNT(*) = (SELECT COUNT(*) FROM #Selected) -- Ancestor covers all
ORDER BY A.Depth DESC -- Deepest
)
SELECT *
FROM Answer
There may be room for improvement, possibly a more direct calculation of depth, but this appears to give the correct result.
See this db<>fiddle for a working demo with data and several test scenarios.

Find children of a most top level parent

There are similar question asking how to find the top level parent of a child (this, this and this). I have a similar question but I want to find all childern of a top level parent. This is similar question but uses wordpress predefined functions.
sample table:
id parent
1 0
2 0
3 1
4 2
5 3
6 3
7 4
I want to select ID with most top parent equals 1. The output should be 3 and all children of 3 I mean (5,6) and even more deep level children if available.
I know I can select them using two times of inner join but the hirearchy may be more complex with more levels.
A simple "Recursive CTE" will do what you want:
with n as (
select id from my_table where id = 1 -- starting row(s)
union all
select t.id
from n
join my_table t on t.parent_id = n.id
)
select id from n;
This CTE will go down all levels ad infinitum. Well... by default SQL Server limits it to 128 levels (that you can increase to 65k).
Since you aren't climbing the entire ladder...
select *
from YourTable
where parent = (select top 1 parent from YourTable group by parent order by count(parent) desc)
If you were wanting to return the parent of 3, since 3 was listed most often, then you'd use a recursive CTE.

sql complex order by multiple fields

I have a table category that has an int field that can reference the primary key in the same table.
like this:
ID category isSubCategoryOf orderingNumber
3 "red t-shirts" 2 2
1 "clothes" NULL 1
4 "cars" NULL 1
6 "Baby toys" 5 1
5 "Toys" NULL 1
2 "t-shirt" 1 1
I want the table to be order such that under each category all sub-categories are listed and under that category all of that sub-category.
ID category isSubCategoryOf orderingNumber
1 "clothes" NULL 1
2 "t-shirt" 1 1
3 "red t-shirts" 2 2
4 "cars" NULL 1
5 "Toys" NULL 1
6 "Baby toys" 5 1
Is such a thing possible to do with SQL or do I have to order this later by hand?
I I understand your needs correctly, you'll need a recursive query to deal with your hierarchical data:
WITH recCTE AS
(
--recursive seed
SELECT
category,
ID,
isSubCategoryOf as ParentID,
orderingNumber,
CAST(ID as VARCHAR(100)) as Path,
1 as depth
FROM table
WHERE isSubCategoryOf IS NULL
UNION ALL
--recursive term
SELECT
table.category,
table.id,
table.isSubCategoryOf,
table.orderingNumber,
recCTE.path + '>' + table.id,
recCTE.depth + 1
FROM recCTE
INNER JOIN table ON
recCTE.ID = table.isSubCategoryOf
)
SELECT * FROM recCTE ORDER BY path
Recursive queries are made up of three parts.
The recursive seed, which is the starting point of the recursive look ups. In your case it's any record with a NULL isSubCategoryOf. You can think of these as the Root of your hierarchy.
The recursive term, which is the part of the recursive CTE that refers back to itself. It iterates until it comes up with no records for each leg of the hierarchy
The final Select statement that selects from the recursive CTE.
Here I made a path field that stitches together each ID that is part of the hieararchy. This gives you the field you can sort on to get your hierarchical sort as asked.
It seems like, with your data, your orderingNumber is akin to the depth field that I added to the recursive CTE above. If that's the case, then you can remove that field from the CTE and save a bit of processing.
if you just want to sotrt and print you can make calculated column
select ID, category, isSubCategoryOf, orderingNumber, ID + '_' + isSubCategoryOf as SortOrder
from table
order by ID + '_' + isSubCategoryOf

Implementing some kind of simple loop in SQLite

I have a table which has these columns: id, text, parentid. And when a row is a root item (doesn't have any parent item), then parentid = 0.
What I want to do, is find text of the first root (root of root of ... root of item) of a specific item.
Here's an example:
SELECT parentid FROM cat WHERE id = 1234 --returns 1120
SELECT parentid FROM cat WHERE id = 1120 --returns 1011
SELECT parentid FROM cat WHERE id = 1011 --returns 0. So this the first root.
SELECT text FROM cat WHERE id = 1011 --returns what I want.
I know it's easily possible with Loops, but I'm using sqlite which doesn't support loops.
So, the question is, is there any way to implement this in sqlite without using any other scripts?
This recursive CTE will give you the desired result. Please note that the CTEs are available only in the latest versions of SQLite starting from version 3.8.3
;with cte as (
select id, parentid, text, 1 level
from t where id = 1234
UNION all
select t.id, t.parentid, t.text, cte.level + 1
from cte inner join t on cte.parentid = t.id
where cte.parentid <> 0)
select * from cte where parentid = 0

How to get second parent with recursive query in Common Table

I am using SQL Server 2008. I have a table like this:
UnitId ParentId UnitName
---------------------------
1 0 FirstUnit
2 1 SecondUnit One
3 1 SecondUnit Two
4 3 B
5 2 C
6 4 D
7 6 E
8 5 F
I want to get second parent of the record. For example:
If I choose unit id that equal to 8, It will bring unit id is equal to 2 to me. It needs to be SecondUnit One. or If I choose unit id that equal to 7, It will bring unit id is equal to 3 to me. It needs to be SecondUnit Two.
How can I write a SQL query this way?
It took me a while, but here it is :)
with tmp as (
select unitId, parentId, unitName, 0 as iteration
from t
where unitId = 7
union all
select parent.unitId, parent.parentId, parent.unitName, child.iteration + 1
from tmp child
join t parent on child.parentId = parent.unitId
where parent.parentId != 0
)
select top 1 unitId, parentId, unitName from tmp
order by iteration desc
Here is also a fiddle to play with.
SELECT t.*, tParent1.UnitId [FirstParent], tParent2.UnitId [SecondParent]
FROM Table t
LEFT JOIN Table tParent1 ON t.ParentId = tParent1.UnitId
LEFT JOIN Table tParent2 ON tParent1.ParentId = tParent2.UnitId
WHERE t.UnitId = <Unit ID search here>
AND NOT tParent2.UnitId IS NULL
Edit: And leave out second part of the WHERE clause if you want results returned even if they don't have a second parent.