使用 SQL 递归查询作为 AND 语句

Using SQL recursive query as AND statement

我有以下流程图。希望它是不言自明的

在层次结构的顶部有一个请求,它是它下面所有请求的基本父级。以下请求具有 'id'、'parent_id'、'state' 字段

我的最终目标是找到满足所有 AND 语句的所有父 ID,包括最后一个(分层查询)。但是,我不知道如何将它用作 AND 语句。

分层查询如下所示:

with cte 
    as (select id, state
        from tbl_request as rH 
        WHERE id = /* each id from the very first select */
        UNION ALL
        select rH.id, rH.state
        from tbl_request as rH 
        join cte
          on rH.parent_id = cte.id
             and (cte.state is null or cte.state NOT IN('not-legit'))
       )
    select case when exists(select 1 from cte where  cte.state IN('not-legit'))
        then 1 else 0 end

如期而至,它做了它应该做的事

问题中提出了解决方案

Return true/false in recursive SQL query based on condition

为方便起见,这里有一个SQL Fiddle

替换你的

WHERE id = /* each id from the very first select */

来自

WHERE id in (
    SELECT r.id FROM tbl_request AS r
    /* there's also an INNER JOIN (hopefully, it won't be an obstacle) */
    WHERE r.parent_id is null
    /* a lot of AND statements */
  )

此外,您应该使用 UNION 而不是 UNION ALL,因为在这种情况下使用重复的元组(idstate)没有意义。

总而言之,您的查询应如下所示

with cte 
    as (select id, state
        from tbl_request as rH 
        WHERE id in (

           SELECT r.id
             FROM tbl_request AS r
                  /* there's also an INNER JOIN (hopefully, it won't be an obstacle) */
            WHERE r.parent_id is null
                  /* a lot of AND statements */

        ) UNION
        select rH.id, rH.state
        from tbl_request as rH 
        join cte
          on rH.parent_id = cte.id
             and (cte.state is null or cte.state NOT IN('not-legit'))
       )

您的子查询可以包含您需要的任何内部联接或任意数量的 AND 运算符,只要它 returns 一列 (select r.id) 就可以正常工作。

我想我已经解决了你想要的问题。

您需要遍历所有节点及其子节点,返回其 state 及其最终根 parent_id

然后按该 ID 聚合并排除任何包含 state = 'not-legit' 行的组。换句话说,将逻辑翻转为双重否定。

WITH cte AS (
    SELECT rH.id, rH.state, rH.id AS top_parent
        FROM tbl_request as rH 
        WHERE (rH.state is null or rH.state <> 'not-legit')
          AND rH.parent_id IS NULL
    UNION ALL
    SELECT rH.id, rH.state, cte.top_parent
        FROM tbl_request as rH
        JOIN cte
          ON rH.parent_id = cte.id
)
SELECT top_parent
FROM cte
GROUP BY
    cte.top_parent
    HAVING COUNT(CASE WHEN cte.state = 'not-legit' THEN 1 END) = 0;

You could also change the logic back to a positive, but it would need to look like this:
HAVING COUNT(CASE WHEN cte.state is null or cte.state <> 'not-legit' THEN 1 END) = COUNT(*)
In other words, there are the same number of these filtered rows as there are all rows.
This feels more complex than what I have put above.

SQL Fiddle