Implementing some kind of simple loop in SQLite - sql

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

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.

SQL: Looping over query result

Regarding company tree in PostgreSQL
I am using the following to get the subsidiaries of the company with id = 11.
SELECT * FROM "OwnershipTable"
WHERE "Parent_ID" = 11;
giving me the following output
Company_ID
Company_Name
Parent_ID
Parent_Name
111
Holdco 1
11
Topco
112
Holdco 2
11
Topco
113
Holdco 3
11
Topco
114
Holdco 4
11
Topco
However, I would like to investigate if any of the Holdco-companies has any subsidiaries. My question is therefore: Is it possible insert the column "Company_ID" as "Parent_ID" in the query using some sort of loop?
Yes. This is called a recursive CTE:
with recursive cte as (
select company_id as parent_company_id, company_id as child_id
from OwnershipTable ot
where parent_id = 11
union all
select cte.parent_company_id, ot.company_id
from cte join
OwnershipTable ot
on ot.parent_id = cte.child_id
)
select *
from cte;
If you want additional information about the companies, you can join it in or include it in the recursive CTE definitions.
This should work:
SELECT * FROM "OwnershipTable" ot
WHERE EXISTS(SELECT 1 FROM "OwnershipTable" where Parent_ID = ot.Company_ID)

Creating a category tree table from an array of categories in PostgreSQL

How to generate ids and parent_ids from the arrays of categories. The number or depth of subcategories can be anything between 1-10 levels.
Example PostgreSQL column. Datatype character varying array.
data_column
character varying[] |
----------------------------------
[root_1, child_1, childchild_1] |
[root_1, child_1, childchild_2] |
[root_2, child_2] |
I would like to convert the column of arrays into the table as shown below that I assume is called the Adjacency List Model. I know there is also the Nested Tree Sets Model and Materialised Path model.
Final output table
id | title | parent_id
------------------------------
1 | root_1 | null
2 | root_2 | null
3 | child_1 | 1
4 | child_2 | 2
5 | childchild_1 | 3
6 | childchild_2 | 3
Final output tree hierarchy
root_1
--child_1
----childchild_1
----childchild_2
root_2
--child_2
step-by-step demo: db<>fiddle
You can do this with a recursive CTE
WITH RECURSIVE cte AS
( SELECT data[1] as title, 2 as idx, null as parent, data FROM t -- 1
UNION
SELECT data[idx], idx + 1, title, data -- 2
FROM cte
WHERE idx <= cardinality(data)
)
SELECT DISTINCT -- 3
title,
parent
FROM cte
The starting query of the recursion: Get all root elements and data you'll need within the recursion
The recursive part: Get element of new index and increase the index
After recursion: Query the columns you finally need. The DISTINCT removes tied elements (e.g. two times the same root_1).
Now you have created the hierarchy. Now you need the ids.
You can generate them in many different ways, for example using the row_number() window function:
WITH RECURSIVE cte AS (...)
SELECT
*,
row_number() OVER ()
FROM (
SELECT DISTINCT
title,
parent
FROM cte
) s
Now, every row has its own id. The order criterion may be tweaked a little. Here we have only little chance to change this without any further information. But the algorithm stays the same.
With the ids of each column, we can create a self join to join the parent id by using the parent title column. Because a self join is a repetition of the select query, it makes sense to encapsulate it into a second CTE to avoid code replication. The final result is:
WITH RECURSIVE cte AS
( SELECT data[1] as title, 2 as idx, null as parent, data FROM t
UNION
SELECT data[idx], idx + 1, title, data
FROM cte
WHERE idx <= cardinality(data)
), numbered AS (
SELECT
*,
row_number() OVER ()
FROM (
SELECT DISTINCT
title,
parent
FROM cte
) s
)
SELECT
n1.row_number as id,
n1.title,
n2.row_number as parent_id
FROM numbered n1
LEFT JOIN numbered n2 ON n1.parent = n2.title

SQL Server - Get Parent values from Child recursively

So I've been struggling with this for the past hour or so now
I'm writing a script to extract some data in a nice format for me to use in another process but I can't quite figure out this particular bit (I don't use SQL Server much)
I've got several tables that are all involved in this script that link together to get all the information. All of these tables are just using fake data and names but it shows the structure hopefully
tblCategories
cat_id cat_name
1 Trousers
2 Tracksuits
3 Woolen
tblCategoryHierarchy
ch_parentid ch_childid
0 1
1 2
2 3
I've also got my product table which has the cat_id in it
tblProduct
p_id p_name p_cat_id
1 Red Trouser 3
So from this, I want to display the product id, name and the hierarchy of all categories linked to the one in the tblProduct.
So for this example, it would display:
id name cats
1 Red Trouser Trousers > Tracksuits > Woolen
Hopefully somebody can give me a hand here! Thanks
Try this:
;WITH CTE AS (
SELECT p.p_id AS id, p.p_cat_id, 1 AS level,
p.p_name, CAST(c.cat_name AS VARCHAR(200)) AS cat
FROM tblProduct AS p
JOIN tblCategories AS c ON p.p_cat_id = c.cat_id
UNION ALL
SELECT c.id, ch.ch_parentid AS cat_id, level = c.level + 1,
c.p_name, CAST(c2.cat_name AS VARCHAR(200)) AS cat
FROM tblCategoryHierarchy AS ch
JOIN CTE AS c ON ch.ch_childid = c.p_cat_id
JOIN tblCategories AS c2 ON ch.ch_parentid = c2.cat_id
)
SELECT id, p_name,
STUFF(REPLACE((SELECT CONCAT('>', cat)
FROM CTE
ORDER BY level DESC
FOR XML PATH('')), '>', '>'), 1, 1, '') AS cat
FROM CTE
The recursive part of the query returns all categories from child up to parent level. The query uses FOR XML PATH in order to concatenate category names in reverse level order (i.e. from parent to child).
REPLACE is used because '>' character is rendered as 'gt;' from FOR XML PATH.

Sql query for 3 level tree?

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.