Oracle 中的递归查询
Recursive query in Oracle
我对 PLSQL 的更高级主题有点陌生,所以希望有人能帮助我。
问题:
我有一个 table,其中包含管理员和用户之间发送的消息。 table 有一个 message_parent 与 FK 到相同的 table message_id 字段:如果该字段被填充,则意味着该消息是作为对先前消息的回复发送的信息。我需要 select 属于同一对话的所有消息并显示它们。这可以通过单个查询来完成,还是我需要一个过程来处理这种逻辑?据我了解,它需要递归,因为我正在搜索的 message_id 总是在变化
例子
消息 table:
|message_id|parent_id|message_content|
|----------|---------|---------------|
|101 |100 | foo |
|100 |97 | bar |
|99 |(null) | Left out |
|97 |(null) | baz |
因此 selecting message_content 的正确查询应该 return "baz"、"bar" 和 "foo" 而不是 "Left out" (因为 baz 是原始消息)。
如果有例如,这将很简单只有两条消息可以捆绑在一起,或者例如thread_id 列,这将 link 所有消息都在同一个 'thread' 中,但是随着 parent_id 的不断变化,我很难弄明白。
在 Oracle 中,使用 CONNECT BY
可以轻松完成此操作
select message_id, parent_id, message_content
from messages
start with message_id = 97 -- this is the root of your conversation
connect by prior message_id = parent_id;
这会从上到下遍历树。
如果您想从一条消息到根遍历树,请更改 start with
和 connect by
部分:
select message_id, parent_id, message_content
from messages
start with message_id = 100 -- this is the root of your conversation
connect by prior parent_id = message_id; -- this now goes "up" in the tree
要根据单个 message_id
获取整个消息上下文,您可以使用两个分层查询。一个将消息树从当前消息 down
遍历到根,而第二个从根向后 up
遍历到叶子。假设当前 message_id 是 100(尽管 101 或 97 的值将具有相同的最终结果),以下查询 returns 所有相关消息(除 'Left out' 之外的所有消息):
with msgs(message_id, parent_id, message_content) as (
select 101, 100, 'foo' from dual union all
select 100, 97, 'bar' from dual union all
select 99, null, 'Left out' from dual union all
select 97, null, 'baz' from dual
), down as (
select message_id start_id
, CONNECT_BY_ROOT message_id curr_id
from msgs
where connect_by_isleaf = 1
start with message_ID = 100
connect by message_ID = prior parent_ID
) /* up */
select level lvl
, case message_id when curr_id then '*' end curr
, msgs.*
from msgs, down
start with message_ID = start_id
connect by prior message_ID = parent_ID
order siblings by message_id;
在 down
查询中,我将其限制为 return 只有根消息节点 (connect_by_isleaf
) 这样 start_id
将成为根消息,并且将当前 message_id 作为 curr_id
包含在 up
查询中以供参考,其中我包含一个列以用星号标记当前消息。
我对 PLSQL 的更高级主题有点陌生,所以希望有人能帮助我。
问题: 我有一个 table,其中包含管理员和用户之间发送的消息。 table 有一个 message_parent 与 FK 到相同的 table message_id 字段:如果该字段被填充,则意味着该消息是作为对先前消息的回复发送的信息。我需要 select 属于同一对话的所有消息并显示它们。这可以通过单个查询来完成,还是我需要一个过程来处理这种逻辑?据我了解,它需要递归,因为我正在搜索的 message_id 总是在变化
例子 消息 table:
|message_id|parent_id|message_content|
|----------|---------|---------------|
|101 |100 | foo |
|100 |97 | bar |
|99 |(null) | Left out |
|97 |(null) | baz |
因此 selecting message_content 的正确查询应该 return "baz"、"bar" 和 "foo" 而不是 "Left out" (因为 baz 是原始消息)。 如果有例如,这将很简单只有两条消息可以捆绑在一起,或者例如thread_id 列,这将 link 所有消息都在同一个 'thread' 中,但是随着 parent_id 的不断变化,我很难弄明白。
在 Oracle 中,使用 CONNECT BY
select message_id, parent_id, message_content
from messages
start with message_id = 97 -- this is the root of your conversation
connect by prior message_id = parent_id;
这会从上到下遍历树。
如果您想从一条消息到根遍历树,请更改 start with
和 connect by
部分:
select message_id, parent_id, message_content
from messages
start with message_id = 100 -- this is the root of your conversation
connect by prior parent_id = message_id; -- this now goes "up" in the tree
要根据单个 message_id
获取整个消息上下文,您可以使用两个分层查询。一个将消息树从当前消息 down
遍历到根,而第二个从根向后 up
遍历到叶子。假设当前 message_id 是 100(尽管 101 或 97 的值将具有相同的最终结果),以下查询 returns 所有相关消息(除 'Left out' 之外的所有消息):
with msgs(message_id, parent_id, message_content) as (
select 101, 100, 'foo' from dual union all
select 100, 97, 'bar' from dual union all
select 99, null, 'Left out' from dual union all
select 97, null, 'baz' from dual
), down as (
select message_id start_id
, CONNECT_BY_ROOT message_id curr_id
from msgs
where connect_by_isleaf = 1
start with message_ID = 100
connect by message_ID = prior parent_ID
) /* up */
select level lvl
, case message_id when curr_id then '*' end curr
, msgs.*
from msgs, down
start with message_ID = start_id
connect by prior message_ID = parent_ID
order siblings by message_id;
在 down
查询中,我将其限制为 return 只有根消息节点 (connect_by_isleaf
) 这样 start_id
将成为根消息,并且将当前 message_id 作为 curr_id
包含在 up
查询中以供参考,其中我包含一个列以用星号标记当前消息。