使用 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,因为在这种情况下使用重复的元组(id
和 state
)没有意义。
总而言之,您的查询应如下所示
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.
我有以下流程图。希望它是不言自明的
在层次结构的顶部有一个请求,它是它下面所有请求的基本父级。以下请求具有 '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,因为在这种情况下使用重复的元组(id
和 state
)没有意义。
总而言之,您的查询应如下所示
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.