Domanda

I have a hierarchical structure in a SQL Server database. I'm trying to write a query to get all elements in the structure under a given element.

So, given a DB table with the columns id and parent_id, this is what I do:

WITH recursive_cte (root_id, id) AS (
  SELECT parent_id, id
  FROM test_cte
  UNION ALL
  SELECT t.parent_id, r.id
  FROM test_cte t
  INNER JOIN recursive_cte r
  ON (r.root_id=t.id)
)
SELECT *
FROM recursive_cte
WHERE root_id=0

Now, if there is a circular reference in the structure under the element with id=0 I get an error from the DBMS (The maximum recursion 100 has been exhausted before statement completion). This is fine, the existance of circular references is already an error.

But if I have a circular reference in the structure under another element, the query will always give an error. Even if I specify a condition which restricts the record set to a non circular one (e.g WHERE root_id=0).

For example in:

id|parent_id|name           |
--+---------+---------------+
0 |NULL     |TEST A         |
1 |4        |TEST CIRCULAR  |
2 |0        |TEST B         |
3 |2        |TEST C         |
4 |1        |TEST CIRCULAR B|

I want my query to work without errors with the condition root_id=0. Is there a way to do that?

È stato utile?

Soluzione 2

You will need to put your WHERE-filter in the CTE part of your query, like so:

WITH recursive_cte (root_id, id) AS (
  SELECT parent_id, id
  FROM test_cte
  WHERE id=0       -- Restrict your recursion to start from the item with id = 0, instead of considdering all items.
  UNION ALL
  SELECT t.parent_id, r.id
  FROM test_cte t
  INNER JOIN recursive_cte r
  ON (r.root_id=t.id)
)
SELECT *
FROM recursive_cte

Altri suggerimenti

This may not be an elegant solution, but it seems to work. Store the list of visited ids in a list and exclude them from further searches. I think this is the right comparison for your query:

WITH recursive_cte(root_id, id, ids) AS (
  SELECT parent_id, id, ',' + cast(id as varchar(8000)) + ',' as ids
  FROM test_cte
  UNION ALL
  SELECT t.parent_id, r.id, ids + cast(id as varchar(8000)) + ','
  FROM test_cte t INNER JOIN 
       recursive_cte r
       ON r.root_id = t.id
  WHERE ',' + r.ids + ',' not like '%,' + cast(t.id as varchar(8000)) + ',%'
)
SELECT *
FROM recursive_cte
WHERE root_id = 0;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top