背景:
电子商务网络应用程序,但具有相互订购的“成员”。当成员执行各种事件(例如搜索)时,记录将插入到“事件”表中。如果/当成员(member)提交订单时,一条记录将被插入到“订单”表中。目标是找到成员(member)执行事件但在事件时间后的某个时间窗口(比如一个小时)内没有下订单的情况。
注意:创建订单记录的代码不能更改。如果可能的话,我可以简单地“记住”这些事件,并将此信息包含在订单记录中。然后,查找成员执行事件但未订购的情况很简单:只需在订单表的这一列中查找 NULL 值(或其他一些默认值)。再次,唉,这在我的情况下是不可能的......
表格:
Order
(id, ts/* timestamp */, sending_member_id, receiving_member_id, …)成员
(id,姓名,...)Activity_Type
(ID、名称……)Activity_Log
(id、ts、member_id、type_id、extra_info)
索引:
All appropriate indexes are in place. Specifically, an index on order.ts does exist.
我试过这三个查询:
方法一
SELECT …
FROM activity_log,
Member
WHERE activity_log.member_id = member.id
AND activity_log.type_id = 1 /* Search */
AND activity_log.ts > [start time]
AND activity_log.ts < [end time]
AND NOT EXISTS (SELECT ‘x’
FROM order
WHERE order.ts >= activity_log.ts
AND order.ts <= activity_log.ts + 3600
AND order.sending_member_id = activity_log.member_id)
ORDER BY activity_log.member_id, activity_log.ts desc
方法二
SELECT …
FROM activity_log, member
WHERE activity_log.member_id = member.id
AND activity_log.type_id = 1 /* Search */
AND activity_log.ts > [start time]
AND activity_log.ts < [end time]
AND activity_log.member_id NOT IN (SELECT order.sending_member_id
FROM order
WHERE order.ts >= activity_log.ts
AND order.ts <= activity_log.ts + 3600)
ORDER BY activity_log.member_id, activity_log.ts desc
方法 3
SELECT …
FROM activity_log
JOIN member ON activity_log.member_id = member.id
LEFT JOIN order ON order.ts >= activity_log.ts
AND order.ts <= activity_log.ts + 3600
AND activity_log.member_id = order.sending_member_id
WHERE activity_log.type_id = 1 /* Search */
AND activity_log.ts > [start time]
AND activity_log.ts < [end time]
AND order.sending_member_id IS NULL
ORDER BY activity_log.member_id, activity_log.ts desc
即使使用方法 3,查询也会运行 20-30 秒并且不使用 order.ts 上的索引。
最佳答案
对于 MySQL,选择取决于被比较的列:
- 如果可为空,NOT EXISTS and NOT IN (with additional checks for NULLS) are the most efficient methods to implement an anti-join in MySQL .
- 如果不可空,use either a LEFT JOIN / IS NULL or NOT IN rather than NOT EXISTS .
如果您更改数据以进行比较,索引将毫无用处:
AND order.ts <= activity_log.ts + 3600
在此示例中,ACTIVITY_LOG.ts
上的索引无关紧要。考虑复合索引(单个索引,多列)。
关于mysql 查询缓慢尝试根据第二个表中的行在一个表中查找 "missing"记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6039166/