Recursive relationship on a many to many table

JohnAPaul

I set up a many to many recursive table like this:

CREATE TABLE PROD
(
IDPROD INT NOT NULL PRIMARY KEY,
NAME VARCHAR(3)
);

CREATE TABLE COMP
(
IDPARENT INT REFERENCES PROD(IDPROD),
IDCHILD INT REFERENCES PROD(IDPROD)
);

INSERT INTO PROD (IDPROD, NAME) VALUES (1, 'abc');
INSERT INTO PROD (IDPROD, NAME) VALUES (2, 'def');
INSERT INTO PROD (IDPROD, NAME) VALUES (3, 'ghi');
INSERT INTO PROD (IDPROD, NAME) VALUES (4, 'jkl');
INSERT INTO PROD (IDPROD, NAME) VALUES (5, 'mno');

INSERT INTO COMP (IDPARENT, IDCHILD) VALUES (1, 2);
INSERT INTO COMP (IDPARENT, IDCHILD) VALUES (3, 4);
INSERT INTO COMP (IDPARENT, IDCHILD) VALUES (4, 5);

With a recursivs CTE I can get all the children of a specific node from the second table.

WITH RECURSIVE TEST (IDPARENT, IDCHILD) AS
(SELECT P0.IDPARENT, P0.IDCHILD
FROM COMP AS P0
WHERE P0.IDPARENT = 3
UNION ALL
SELECT P1.IDPARENT, P1.IDCHILD
FROM COMP AS P1, TEST AS T
WHERE T.IDCHILD = P1.IDPARENT)

SELECT * FROM TEST

But I need a query that will give me the entire structure, not just for one node. Like in the classic adjacency list where you get all the root nodes where IDPARENT IS NULL and their children listed below. I use Firebird.

JohnLBevan

I'm not familiar with Firebird, but this works in SQL Server, so is hopefully similar / enough to get you on track:

WITH TEST (IDRoot, IDPARENT, IDCHILD) AS
(

  SELECT P0.IDPROD, C0.IDParent, C0.IDCHILD
  FROM PROD AS P0
  left outer join COMP C0 on C0.IDParent = P0.IDPROD
  WHERE P0.IDProd not in (select IDChild from COMP)

  UNION ALL

  SELECT T.IDRoot, C1.IDPARENT, C1.IDCHILD
  FROM COMP AS C1
  inner join TEST AS T on T.IDCHILD = C1.IDPARENT

)
SELECT * FROM TEST

Hope that helps.

SQL Fiddle Version: http://sqlfiddle.com/#!6/22f84/7

Notes

Includ a column to denote the root of the tree as well as parent/child - since there may be multiple trees if we're not specifying a particular root:

WITH TEST (IDRoot, IDPARENT, IDCHILD) AS

Treat any product which is not a child as a ROOT (i.e. first item in a tree).

WHERE P0.IDProd not in (select IDChild from COMP)

EDIT: Answer to comments

Query on any node to see all of its relatives:

The simple way to filter on any node would be to amend the above statement's WHERE P0.IDProd not in (select IDChild from COMP) with WHERE P0.IDProd = IdImInterestedIn. However if you want to use the CTE for a view, then run queries over this static query you could use the code below - you can then filter on IDProd (select * from test where IDProd = IdImInterestedIn) to see that item's ancestors and descendants.

WITH TEST (IDProd, IDRelation, Generation) AS
(

  SELECT IDPROD
  , IDPROD
  , 0
  FROM PROD 

  UNION ALL

  SELECT T.IDPROD
  , C.IdParent 
  , T.Generation - 1
  FROM TEST AS T
  inner join Comp as C 
  on C.IdChild = T.IDRelation
  where t.Generation <= 0

  UNION ALL

  SELECT T.IDPROD
  , C.IdChild 
  , T.Generation + 1
  FROM TEST AS T
  inner join Comp as C 
  on C.IdParent = T.IDRelation
  where t.Generation >= 0

)
SELECT * 
FROM TEST
order by IDProd, Generation

SQL Fiddle: http://sqlfiddle.com/#!6/22f84/15

See a root node's full tree in a single column

WITH TEST (IDRoot, IDPARENT, IDCHILD, TREE) AS
(

  SELECT P0.IDPROD, C0.IDParent, C0.IDCHILD, cast(P0.IDPROD as nvarchar(max)) + coalesce(', ' + cast(C0.IDCHILD as nvarchar(max)),'')
  FROM PROD AS P0
  left outer join COMP C0 on C0.IDParent = P0.IDPROD
  WHERE P0.IDProd not in (select IDChild from COMP)

  UNION ALL

  SELECT T.IDRoot, C1.IDPARENT, C1.IDCHILD, TREE + coalesce(', ' + cast(C1.IDCHILD as nvarchar(max)),'')
  FROM COMP AS C1
  inner join TEST AS T on T.IDCHILD = C1.IDPARENT

)
SELECT * 
FROM TEST 
order by IDRoot

SQL Fiddle: http://sqlfiddle.com/#!6/22f84/19

EDIT: Answer to Additional Comments

with cte (tree_root_no, tree_row_no, relation_sort, relation_chart, Name, id, avoid_circular_ref) as
(
       select row_number() over (order by p.idprod) 
       , 1 
       , cast(row_number() over (order by p.idprod) as nvarchar(max)) 
       , cast('-' as nvarchar(max)) 
       , p.NAME 
       , p.IDPROD 
       , ',' + cast(p.IDPROD as nvarchar(max)) + ',' 
       from PROD p
       where p.IDPROD not in (select IDCHILD from COMP) --if it's nothing's child, it's a tree's root

       union all

       select cte.tree_root_no
       , cte.tree_row_no + 1
       , cte.relation_sort + cast(row_number() over (order by p.idprod) as nvarchar(max)) 
       , replace(relation_chart,'-','|') + ' -' 
       , p.NAME
       , p.IDPROD
       , cte.avoid_circular_ref + cast(p.IDPROD as nvarchar(max)) + ','
       from cte
       inner join COMP c on c.IDPARENT = cte.id
       inner join PROD p on p.IDPROD = c.IDCHILD
       where charindex(',' + cast(p.IDPROD as nvarchar(max)) + ',', cte.avoid_circular_ref) = 0
)
select tree_root_no, tree_row_no, relation_sort, relation_chart, id, name 
from cte 
order by tree_root_no, relation_sort

SQL Fiddle: http://sqlfiddle.com/#!6/4397f/9

Update to show each path

This one's a nasty hack, but the only way I could think of to solve your puzzle; this gives each path through the trees its own number:

;with inner_cte (parent, child, sequence, treePath) as (

    select null
    , p.IDPROD
    , 1
    , ',' + CAST(p.idprod as nvarchar(max)) + ','
    from @prod p
    where IDPROD not in 
    (
        select IDCHILD from @comp
    )

    union all

    select cte.child
    , c.IDCHILD
    , cte.sequence + 1
    , cte.treePath + CAST(c.IDCHILD as nvarchar(max)) + ','
    from inner_cte cte
    inner join @comp c on c.IDPARENT = cte.child

)
, outer_cte (id, value, pathNo, sequence, parent, treePath) as
(
    select icte.child, p.NAME, ROW_NUMBER() over (order by icte.child), icte.sequence, icte.parent, icte.treePath
    from inner_cte icte
    inner join @prod p on p.IDPROD = icte.child
    where icte.child not in (select coalesce(icte2.parent,-1) from inner_cte icte2) 

    union all

    select icte.child, p.NAME, octe.pathNo,icte.sequence, icte.parent, icte.treePath
    from outer_cte octe
    inner join inner_cte icte on icte.child = octe.parent and CHARINDEX(icte.treePath, octe.treePath) > 0
    inner join @prod p on p.IDPROD = icte.child

)
select id, value, pathNo
from outer_cte
order by pathNo, sequence

SQL Fiddle here: http://sqlfiddle.com/#!6/5a16e/1

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Recursive Many to many relationship with Yii

From Dev

SQL many to many relationship table

From Dev

Many to many relationship in the same table?

From Dev

Junction table/many to many relationship

From Dev

Related name for recursive many to many relationship not working

From Dev

Oracle Recursive Join - Many to Many Relationship

From Dev

One to many relationship table

From Dev

Creating one to many relationship with pivot table of many to many relationship

From Dev

Creating one to many relationship with pivot table of many to many relationship

From Dev

Hibernate: many-to-many relationship table as entity

From Dev

Relationship across a many-to-many table

From Dev

Split Table into many to many relationship: Data Migration

From Dev

Why is a new table created in a many to many relationship?

From Dev

inserting data into a table that has a many to many relationship

From Dev

Mapping the other table in a many to many relationship

From Dev

Hibernate: many-to-many relationship table as entity

From Dev

Why is a new table created in a many to many relationship?

From Dev

How to populate a many-to-many relationship as table

From Dev

Rails: many to many relationship join table design

From Dev

Providing timestamp in reference table (many to many relationship)

From Dev

One to many recursive relationship with Code First

From Dev

One to many relationship on the same table

From Dev

GraphDiff and EF6.1 – Recursive Many-to-Many Relationship

From Dev

GraphDiff and EF6.1 – Recursive Many-to-Many Relationship

From Dev

CTE recursive query for two tables that has a many to many relationship

From Dev

Hibernate - Many to many relationship: relationship table not getting populated

From Dev

Many to many relationship with ASP.NET Identity Table and Custom Table

From Dev

Many to many relationship with ASP.NET Identity Table and Custom Table

From Dev

Is this a many-to-many relationship?

Related Related

  1. 1

    Recursive Many to many relationship with Yii

  2. 2

    SQL many to many relationship table

  3. 3

    Many to many relationship in the same table?

  4. 4

    Junction table/many to many relationship

  5. 5

    Related name for recursive many to many relationship not working

  6. 6

    Oracle Recursive Join - Many to Many Relationship

  7. 7

    One to many relationship table

  8. 8

    Creating one to many relationship with pivot table of many to many relationship

  9. 9

    Creating one to many relationship with pivot table of many to many relationship

  10. 10

    Hibernate: many-to-many relationship table as entity

  11. 11

    Relationship across a many-to-many table

  12. 12

    Split Table into many to many relationship: Data Migration

  13. 13

    Why is a new table created in a many to many relationship?

  14. 14

    inserting data into a table that has a many to many relationship

  15. 15

    Mapping the other table in a many to many relationship

  16. 16

    Hibernate: many-to-many relationship table as entity

  17. 17

    Why is a new table created in a many to many relationship?

  18. 18

    How to populate a many-to-many relationship as table

  19. 19

    Rails: many to many relationship join table design

  20. 20

    Providing timestamp in reference table (many to many relationship)

  21. 21

    One to many recursive relationship with Code First

  22. 22

    One to many relationship on the same table

  23. 23

    GraphDiff and EF6.1 – Recursive Many-to-Many Relationship

  24. 24

    GraphDiff and EF6.1 – Recursive Many-to-Many Relationship

  25. 25

    CTE recursive query for two tables that has a many to many relationship

  26. 26

    Hibernate - Many to many relationship: relationship table not getting populated

  27. 27

    Many to many relationship with ASP.NET Identity Table and Custom Table

  28. 28

    Many to many relationship with ASP.NET Identity Table and Custom Table

  29. 29

    Is this a many-to-many relationship?

HotTag

Archive