我有一堆聚合的事件表(订单就是其中之一),我可以像这样查询
SELECT * FROM events WHERE aggregate_id = :order_id AND aggregate_type = :order_type
这适用于简单的情况,但它更多地涉及重复创建的事件。业务需求之一是可以复制订单(从概念上讲,认为是move
,不是copy
)。这将关闭原始订单,然后员工可以继续处理新订单。为了显示订单的完整历史记录,不仅要显示当前订单的事件,还要显示任何原始订单的事件。
这是一个简化的示例:
订单 3
- 5 月 10 日 - 发货
- 5 月 9 日 - 包装
- 5 月 9 日 - 与订单 2重复
订单 2
- 5 月 9 日 - 关闭
- 5 月 8 日 - 联系客户
- 5 月 8 日 - 收到货
- 5 月 6 日 - 从订单 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 进行递归。order_id
该示例的结果将是具有 3 行的单列: (3, 2, 1)
.
问题是,如果我们查看原始订单(在本例中为Order 1 ),根本不会显示任何事件,因为它没有重复的 created事件。因此,任何不重复的订单在基本情况下都不会选择任何内容,因此 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 个事件。