Selecting children from an infinite hierarchy - sql

I have an SQL table like this
ID | ParentID
1 | null
2 | null
3 | null
4 | 1
5 | 4
6 | 5
7 | 6
8 | 7
9 | 8
Now, as you can see, a child parent relationship is maintained. I want a query to select "all level" children of a given ID.
Suppose if I input ID = 1, the result should be
ID | ParentID
1 | null
4 | 1
5 | 4
6 | 5
7 | 6
8 | 7
9 | 8
So all immediate children, as well as children of their children at any level, should come up.
Is it possible to do this in MS SQL server? I have MS-SQL server 2012.

Here is an example with an extra field Name, but with CTE the recursion is simple:
DECLARE #ID int
SET #ID = 1;
WITH CTE_Table_1
(
ID,
Name,
ParentID,
TreeLevel
)
AS(
SELECT
ID,
Name,
ParentID,
0 AS TreeLevel
FROM Table_1
WHERE ID = #ID
UNION ALL
SELECT
T.ID,
T.Name,
T.ParentID,
TreeLevel + 1
FROM Table_1 T
INNER JOIN CTE_Table_1 ON CTE_Table_1.ID = T.ParentID
)
SELECT * FROM CTE_Table_1

Try this workin fine : http://www.sqlteam.com/Forums/topic.asp?TOPIC_ID=101053
-- Structure
create table dbo.MyPeople (Id int, Name varchar(30), ParentId int)
-- Data
insert dbo.MyPeople
select 1, 'P1', null
union all select 2, 'P2', null
union all select 3, 'P1C1', 1
union all select 4, 'P1C2', 1
union all select 5, 'P2C1', 2
union all select 6, 'P1C2C1', 4
union all select 7, 'P1C1C1', 3
union all select 8, 'P1C1C1C1', 7
union all select 9, 'P2C1C1', 5
union all select 10, 'P1C3', 1
go
-- Function
create function dbo.AncestorPath(#Id int) returns varchar(100) as
begin
declare #Path varchar(100)
while 0 = 0
begin
select #Path = cast(Id as varchar(5)) + isnull('/' + #Path, ''), #Id = ParentId
from dbo.MyPeople where Id = #Id
if ##rowcount = 0 break
end
return #Path
end
go
-- Calculation
select * from (
select *, dbo.AncestorPath(Id) as AncestorPath from dbo.MyPeople) a
where '/' + AncestorPath + '/' like '%/1/%'
or
Try recursive procedure like this
ALTER PROCEDURE dbo.GetChildren
#ParentId int
AS
SET NOCOUNT ON
SELECT *
FROM MainTable
WHERE ChildId IN
(
SELECT ParentId
FROM f_GetChildren(#ParentId)
UNION
SELECT ChildId
FROM f_GetChildren(#ParentId)
)
ORDER BY ParentId
SET NOCOUNT OFF
RETURN

From SQL Server 2005, Common Table Expressions have been added to T-SQL of SQL Server that help you in this kind of hierarchial queries. That's what you are looking for!

Related

Get Records depend on their sum value

I have a SQL Server table which have records like this
ID | Value
1 | 100
2 | 150
3 | 250
4 | 600
5 | 1550
6 | 50
7 | 300
I need to select random records, but the only condition is that the total sum of this records value achieve a specific number or percentage i define.
let's say i need a total value of 300 or 10%, so here are the chances
1 | 100
2 | 150
6 | 50
or
3 | 250
6 | 50
or
7 | 300
can any one help me to do this.
Think this recursive CTE works, no idea what the performance will be like though once you get past a trivial amount of rows:
DECLARE #Test TABLE
(
ID INT NOT NULL,
VAL INT NOT NULL
);
INSERT INTO #Test
VALUES (1,100),
(2,150),
(3,250),
(4,600),
(5,1550),
(6,50),
(7,300);
DECLARE #SumValue INT = 300,
#Percentage INT = 10;
WITH GetSums
AS
(
SELECT T.ID,
T.Val,
CAST(T.ID AS VARCHAR(MAX)) AS IDs
FROM #Test AS T
UNION ALL
SELECT T1.ID,
T1.Val + GS.Val AS Val,
CAST(T1.ID AS VARCHAR(MAX)) + ',' + GS.IDs AS IDs
FROM #Test AS T1
INNER
JOIN GetSums AS GS
ON T1.ID > GS.ID
)
SELECT GS.IDs,
GS.Val
FROM GetSums AS GS
WHERE (GS.Val = #SumValue OR GS.VAL = (SELECT SUM(Val) FROM #Test AS T) / #Percentage)
OPTION (MAXRECURSION 50);
Similar found here:
find all combination where Total sum is around a number
Try this...we will get the correct answer if the 6th value is 250...
SELECT 1 ID, 100 Value
INTO #Temp_1
UNION ALL SELECT 2 , 150
UNION ALL SELECT 2 , 150
UNION ALL SELECT 3 , 250
UNION ALL SELECT 4 , 600
UNION ALL SELECT 5 , 1550
UNION ALL SELECT 6 , 250
UNION ALL SELECT 7 , 300
CREATE TABLE #Temp_IDs
(
ID Int,
Value Numeric(18,2)
)
DELETE
FROM #Temp_IDs
DECLARE #ID Int,
#Vale Numeric(18,2),
#ContinueYN Char(1)
SET #ContinueYN = 'Y'
IF EXISTS (SELECT TOP 1 1 FROM #Temp_1
WHERE Value <= 300
AND ID NOT IN (SELECT ID FROM #Temp_IDs )
AND Value <= (SELECT 300 - ISNULL( SUM(Value),0) FROM #Temp_IDs)
ORDER BY NEWID())
BEGIN
WHILE (#ContinueYN = 'Y')
BEGIN
SELECT #ID = ID,
#Vale = Value
FROM #Temp_1
WHERE Value <= 300
AND ID NOT IN (SELECT ID FROM #Temp_IDs )
AND Value <= (SELECT 300 - ISNULL( SUM(Value),0) FROM #Temp_IDs)
ORDER BY NEWID()
INSERT INTO #Temp_IDs
SELECT #ID,#Vale
IF (SELECT SUM(Value) FROM #Temp_IDs) = 300
BREAK
ELSE IF #ID IS NULL
BEGIN
DELETE FROM #Temp_IDs
END
SET #ID = NULL
SET #Vale = NULL
END
END
SELECT *
FROM #Temp_IDs
DROP TABLE #Temp_IDs
DROP TABLE #Temp_1

How can I get all the parent element by providing child element ID in oracle?

My Oracle table looks like this
ID | ParentID
-----------------
1 | 0
2 | 1
3 | 2
4 | 3
5 | 3
If I know only ID and need to get all parent elements in oracle, what is the query I need to use?
ex:- If I pass 5, need to get 5 > 3 > 2 > 1
For example:
SQL> with test (id, parent) as
2 (select 1, 0 from dual union
3 select 2, 1 from dual union
4 select 3, 2 from dual union
5 select 4, 3 from dual union
6 select 5, 3 from dual
7 )
8 select listagg(id, '->') within group (order by level) result
9 from test
10 start with id = &par_id
11 connect by prior parent = id;
Enter value for par_id: 5
RESULT
---------------------------------------------------------------------
5->3->2->1
SQL>
You may use a recursive CTE
WITH cte (id, parentid, p)
AS (SELECT id,
parentid,
To_char(id) AS p
FROM t
WHERE id = :p_id --enter 5
UNION ALL
SELECT t.id,
t.parentid,
c.p
|| '>'
|| t.id AS p
FROM t
JOIN cte c
ON ( c.parentid = t.id ))
SELECT p
FROM cte
WHERE parentid = 0 --Highest parent.
Demo

SQL recursive id nodes

I have a table structure like so
Id Desc Node
---------------------
1 A
2 Aa 1
3 Ab 1
4 B
5 Bb 4
6 Bb1 5
these Desc values are presented in a listview to the user, if the user chooses Bb, I want the ID 5 and also the ID 4 becuase thats the root node of that entry, simular to that if the user chooses Bb1, I need ID 6, 5 and 4
I am only able to query one level up, but there could be n levels, so my query at the moment looks like this
SELECT Id
FROM tbl
WHERE Desc = 'Bb1'
OR Id = (SELECT Node FROM tbl WHERE Desc = 'Bb1');
You can do this with Recursive CTE like below
Schema:
CREATE TABLE #TAB (ID INT, DESCS VARCHAR(10), NODE INT)
INSERT INTO #TAB
SELECT 1 AS ID, 'A' DESCS, NULL NODE
UNION ALL
SELECT 2 , 'AA', 1
UNION ALL
SELECT 3, 'AB', 1
UNION ALL
SELECT 4, 'B', NULL
UNION ALL
SELECT 5, 'BB', 4
UNION ALL
SELECT 6, 'BB1', 5
Now do recursive CTE for picking node value and apply it again on #TAB with a Join.
;WITH CTE AS(
SELECT ID, DESCS, NODE FROM #TAB WHERE ID=6
UNION ALL
SELECT T.ID, T.DESCS, T.NODE FROM #TAB T
INNER JOIN CTE C ON T.ID = C.NODE
)
SELECT * FROM CTE
When you pass 6 to the first query in CTE, the result will be
+----+-------+------+
| ID | DESCS | NODE |
+----+-------+------+
| 6 | BB1 | 5 |
| 5 | BB | 4 |
| 4 | B | NULL |
+----+-------+------+

get all products that are in nested categories

I would really appreciate some help on this. my teacher couldn't help me.
Anyway I have 3 tables
tbl_product:
PID | productname
1 | product 1
2 | product 2
3 | product 3
4 | product 4
..
tbl_categories, motherCategory allows me to nest categories:
CID | categoriename | motherCategory
1 | electronics | NULL
2 | clothing | NULL
3 | Arduino | 1
4 | Casings, extra's | 3
..
tbl_productInCategory PID and CID are foreign keys to PID and CID in tbl_product and tbl_categories respectively. A product can have multiple categories assigned to it so PID can occur more than once in this table.
PID | CID
1 | 3
2 | 3
3 | 4
4 | 4
I want to select all products that are in a given category + it's subcategories. For instance if I give it the parameter CID = 1 (Electronics) it should also return the products in arduino and Casings, extra's.
I can't figure out how to do this and any help or pointers are appreciated.
something like a recursive WITH
with recur AS
(
SELECT CID,motherCategory FROM tbl_categories WHERE CID = #YourId
UNION ALL
SELECT r2.CID, r2.motherCategory FROM tbl_categoriesr2 WHERE r2.motherCategory = recur.CID
)
SELECT * FROM tbl_product WHERE PID IN (SELECT CID FROM recur)
You could use a Common Table Expression like this:
declare #tbl_product table (
PID int,
productname nvarchar(50)
)
insert into #tbl_product(PID, productname)
select 1, 'product 1'
union
select 2, 'product 2'
union
select 3, 'product 3'
union
select 4, 'product 4'
union
select 5, 'product 5'
declare #tbl_categories table (
CID int,
categoriename nvarchar(50),
motherCategory int
)
insert into #tbl_categories(CID, categoriename, motherCategory)
select 1,'electronics', NULL
union
select 2, 'clothing', NULL
union
select 3, 'Arduino', 1
union
select 4, 'Casings, extra''s', 3
declare #tbl_productInCategory table (
PID int,
CID int
)
insert into #tbl_productInCategory(PID, CID)
select 1, 3
union
select 2, 3
union
select 3, 4
union
select 4, 4
-- COMMON TABLE EXPRESSION
;with category_cte (CID, categoriname, motherCategory)
AS
(
select CID, categoriename, motherCategory
from #tbl_categories
where CID = 1 -- THE CID YOU WANT TO USE
UNION ALL
select c.CID, c.categoriename, c.motherCategory
from #tbl_categories c
inner join category_cte cte on c.motherCategory = cte.CID
)
select p.*
from #tbl_product p
inner join #tbl_productInCategory pic on p.PID = pic.PID
Note that there is a comment in the SQL where the CID is being used.

Selecting all parents in the order of relation from hierarchical table SQL

I've a table like this with a parent child relation in the same table
AccountID| ParentID | AccountName
----------------------------------------------
1 | 0 | Root
2 | 1 | Child1
3 | 1 | Child2
4 | 2 | Child3
5 | 4 | Child1
6 | 5 | Child1
7 | 6 | Child1
8 | 6 | Child1
So when I send the account ID 7 I have to get the tables in the order like child,father,grandfather.. that way.. So for 7, I need to get all parets like this
AccountID
---------
7
6
5
4
2
1
So the most important point is the order. It should be from the bottom level to its next higher then to the next...
You can use a recursive CTE:
declare #childAccID int
set #childAccID = 7
;WITH Rec_CTE
AS(
SELECT 1 AS Level,
tChild.*
FROM dbo.TableName tChild
WHERE tChild.AccountID = #childAccID
UNION ALL
SELECT Level + 1 AS Level,
parent.*
FROM Rec_CTE tParent
INNER JOIN dbo.TableName parent
ON parent.AccountID = tParent.ParentID
)
SELECT * FROM Rec_CTE
ORDER BY Level
DEMO
Try this:
create table DemoTable
(
accountid bigint
,parentid bigint
,accountname nvarchar(128)
)
insert DemoTable(accountid,parentid,accountname)
select 1, null, 'Root'
union select 2, 1, 'Child1'
union select 3, 1, 'Child2'
union select 4, 1, 'Child3'
union select 5, 2, 'Child1.1'
union select 6, 2, 'Child1.2'
go
declare #findMe bigint = 6;
with myCTE as
(
select accountid,parentid,accountname,1 hierarchyLevel
from DemoTable
where accountid = #findMe
union all
select b.accountid,b.parentid,b.accountname, a.hierarchyLevel + 1
from myCTE a
inner join DemoTable b
on b.accountid = a.parentid
)
select * from myCTE
order by hierarchyLevel