Let's suppose that any given client can have multiple boxes. Each box can contain multiple items as well as multiple boxes (sub-boxes).
BoxA -> Item1, Item2, BoxB, BoxC
Unfortunately, due to the business rules, it's possible to create a circular cycle.
BoxA -> Item1, BoxB, BoxC
BoxB -> BoxA, BoxD
As you can see, BoxA contains BoxB, and BoxB contains BoxA.
The problem I'm attempting to solve is to get all the sub-boxes for a given list of boxes in a client.
So if I was looking for the sub-boxes for BoxA (from the previous example), I would get the following: BoxB, BoxC, BoxA, BoxD.
This is what I have so far:
WITH box_info AS (
-- This is typically a bit more complicated, that's why I have it in a seperate WITH clause
SELECT sub_box_id
FROM client_box
WHERE box_id = 1
),
all_sub_boxes(sub_box_id) AS (
SELECT sub_box_id
FROM box_info
WHERE sub_box_id IS NOT NULL
UNION ALL
SELECT cb.sub_box_id
FROM client_box cb, all_sub_boxes asb
WHERE cb.box_id = asb.sub_box_id AND cb.sub_box_id IS NOT NULL
-- AND cb.sub_box_id NOT IN (SELECT sub_box_id FROM all_sub_boxes)
)
SELECT sub_box_id FROM all_sub_boxes;
However, since it's possible to get stuck in a recursive loop, the "all_sub_boxes" WITH clause will fail. The commented out line is what I would intuitively put in since it prevents already visited sub-boxes from getting added to the recursive list, but it seems that we can't reference "all_sub_boxes" from within.
So essentially, I need a way to not include already included sub-boxes in the recursive query.
Perhaps I could create a temp table? But I don't even know if it's possible to insert into a table during a recursive query. And additionally, what is the cost each time this query is run if we do create a temp table every time?
I'm trying to write this query so that it could be used across different commercial DBs, so if I can avoid non-standard sql, that would be great. But I understand that if it's not possible, then it is what it is.
Edit
For the sake of clarity, here's how the client_box
table might look:
+--------+---------+------------+
| BOX_ID | ITEM_ID | SUB_BOX_ID |
+--------+---------+------------+
| BoxA | Item1 | (null) |
| BoxA | (null) | BoxB |
| BoxA | (null) | BoxC |
| BoxB | (null) | BoxA |
| BoxB | (null) | BoxD |
+--------+---------+------------+