简体   繁体   中英

How do I exclude entries from a recursive CTE?

How can I exclude entries from a recursive CTW with Sqlite?

CREATE TABLE GroupMembers (
    group_id        VARCHAR,
    member_id       VARCHAR
);

INSERT INTO GroupMembers(group_id, member_id) VALUES
    ('1', '10'),
    ('1', '20'),
    ('1', '30'),
    ('1', '-50'),
    ('2', '30'),
    ('2', '40'),
    ('3', '1'),
    ('3', '50'),
    ('4', '-10'),
    ('10', '50'),
    ('10', '60');

I want a query that will give me the list of members (recursively) in the group. However, a member with the first character being '-' means that the id that comes after the minus is NOT in the group.

For example, the members of '1' are '10', '20', '30', and '-50'. '10', however, is a group so we need to add its children '50' and '60'. However, '-50' is already a member so we cannot include '50'. In conclusion the members of '1' are '10', '20', '30', '-50', and '60'.

It seems like this query should work:

WITH RECURSIVE members(id) AS (
    VALUES('1')
    UNION
    SELECT gm.member_id 
        FROM members m
        INNER JOIN GroupMembers gm ON mg.group_id=m.id
        LEFT OUTER JOIN members e ON '-' || gm.member_id=e.id
        WHERE e.id IS NULL
)
SELECT id FROM members;

But I get the error: multiple references to recursive table: members

How can I fix/rewrite this to do what I want?

Note: it doesnt matter whether the '-50' entry is returned in the result set.

I don't have a SQLite available for testing, but assuming the -50 also means that 50 should be excluded as well, I think you are looking for this:

WITH RECURSIVE members(id) AS (
    VALUES('1')
    UNION
    SELECT gm.member_id 
    FROM GroupMembers gm 
      JOIN members m ON gm.group_id=m.id
    WHERE member_id not like '-%'
      AND not exists (select 1 
                      from groupMembers g2
                      where g2.member_id = '-'||gm.member_id)
)
SELECT id 
FROM members;

(The above works in Postgres)

You usually select from the base table in the recursive part and the join back to the actual CTE. The filtering of unwanted rows is then done with a regular where clause not by joining the CTE again. A recursive CTE is defined to terminate when the JOIN finds no more rows.

SQLFiddle (Postgres): http://sqlfiddle.com/#!15/04405/1

Edit after the requirements have changed (have been detailed):

As you need to exclude the rows based on their position (a detail that you didn't provide in your original question). The filter can only be done outside of the CTE. Again I can't test this with SQLite, only with Postgres:

WITH RECURSIVE members(id, level) AS (
    VALUES('4', 1)
    UNION
    SELECT gm.member_id, m.level + 1
    FROM GroupMembers gm 
      JOIN members m ON gm.group_id=m.id
)
SELECT m.id, m.level
FROM members m
where id not like '-%'
  and not exists (select 1 
                  from members m2
                  where m2.level < m.level 
                    and m2.id = '-'||m.id);

Updated SQLFiddle: http://sqlfiddle.com/#!15/ec0f9/3

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM