MySQL递归cte基本情况

时间:2021-05-08 09:17:16

标签: mysql common-table-expression recursive-query recursive-cte

我有一堆聚合的事件表(order 是其中之一),我可以像这样查询

SELECT * FROM events WHERE aggregate_id = :order_id AND aggregate_type = :order_type

这适用于简单的情况,但它会更多地涉及 duplicate created 事件。业务需求之一是订单可以重复(从概念上讲,请考虑move,而不是copy)。这将关闭原始订单,然后工作人员可以继续处理新订单。为了显示订单的完整历史记录,不仅要显示当前订单的事件,还要显示任何原始订单上的事件。

这是一个简化的例子:

订单 3

  • 5 月 10 日 - 发货
  • 5 月 9 日 - 打包
  • 5 月 9 日 - 复制自 Order 2

订单 2

  • 5 月 9 日 - 关闭
  • 5 月 8 日 - 联系了客户
  • 5 月 8 日 - 收到库存
  • 5 月 6 日 - 复制自 Order 1

订单 1

  • 5 月 6 日 - 关闭
  • 5 月 5 日 - 创建

我提出了一个相当简单的查询,适用于这种情况:

WITH RECURSIVE original_orders(order_id) AS (
  select aggregate_id
  from events
  where aggregate_type = 'order'
    and aggregate_id = :order_id
    and event_name = 'duplicate created'
  UNION ALL
  select body->'$.duplicatedFromOrderId'
  from original_orders
    inner join events on (aggregate_type = 'order' and aggregate_id = order_id and event_name = 'duplicate created')
)
SELECT events.*
FROM events
INNER JOIN original_orders ON aggregate_id = order_id;
-- extra context from the real (not simplified) use-case:
-- I actually use the CTE twice, because "notes" are stored separately
-- INNER JOIN users ON event.user_id - to get more info
-- UNION ALL SELECT * FROM notes JOIN original_orders ON order_id = notes.object_id

递归 CTE 最终选择从最新订单(基本情况部分)开始的订单 ID,然后通过从事件正文中获取原始订单 ID 进行递归。 该示例的结果将是具有 3 行的单列 order_id(3, 2, 1)

问题在于,如果我们查看原始订单(在本例中为订单 1),根本不会显示任何事件,因为它没有重复创建事件。因此,任何不重复的订单在基本情况下都不会选择任何内容,因此 CTE 将返回一个空结果集。

我觉得我的逻辑有些缺陷,而且我缺少[明显]简单的方法来获得我需要的东西。我认为在基本情况下,我可以改为搜索 ANY 事件名称和 GROUP BY id,从而在基本情况查询中获得 order_id,即使订单不是重复的(以类似的方式我可以使用 SELECT DISTINCT 并放弃 GROUP BY 但它实际上是同一件事)。像这样:

WITH RECURSIVE original_orders(order_id) AS (
  select aggregate_id
  from events
  where aggregate_type = 'order'
    and aggregate_id = :order_id
  group by aggregate_id
  UNION ALL
  -- ...
)

这感觉像是一种解决方法,所以我很想修正我的逻辑。我错过了什么?


可重现的例子:

create table events
(
    id             int auto_increment,
    aggregate_type varchar(100) not null,
    aggregate_id   varchar(255) not null,
    event_name     varchar(255) not null,
    occurred_on    datetime     not null,
    body           json         not null,
    constraint events_pk
        primary key (id)
);

insert into events (aggregate_type, aggregate_id, event_name, occurred_on, body)
values ('order', 1, 'created', '2020-05-05 09:00:00', json_object()),
       ('order', 1, 'closed', '2020-05-06 09:00:00', json_object()),
       ('order', 2, 'duplicate created', '2020-05-06 09:00:00', json_object('duplicatedFromOrderId', 1)),
       ('order', 2, 'received', '2020-05-07 09:00:00', json_object()),
       ('order', 2, 'closed', '2020-05-08 09:00:00', json_object()),
       ('order', 3, 'duplicate created', '2020-05-08 09:00:00', json_object('duplicatedFromOrderId', 2)),
       ('order', 3, 'shipped', '2020-05-09 09:00:00', json_object());

使用 :order_id = 3 运行查询应返回所有 7 个事件。

使用 :order_id = 2 运行查询应返回 5 个事件(应忽略​​订单 3 的事件)。

使用 :order_id = 1 运行查询应返回 2 个事件。

1 个答案:

答案 0 :(得分:2)

AND GPA < 3
相关问题