mysql查询慢慢尝试根据第二个表中的行在一个表中查找“丢失”记录

时间:2011-05-18 02:14:14

标签: mysql sql

背景

电子商务网络应用程序,但“成员”彼此订购。当成员执行各种活动(例如搜索)时,记录将插入到“活动”表中。如果/当成员提交订单时,会将记录插入“订单”表中。目标是找到一个成员执行活动但在活动开始后的某个时间窗口(比如一小时)内没有下订单的情况。

注意:无法更改创建订单记录的代码。如果可能的话,我可以简单地“记住”这些活动,并将这些信息包含在订单记录中。然后,要查找成员执行活动但未执行订单的情况很简单:只需在订单表的此列中查找NULL值(或其他一些默认值)。唉,唉,这在我的情况下是不可能的......

表:

  • Order(id,ts / * timestamp * /,sending_member_id,receiving_member_id,...)
  • Member(id,name,...)
  • Activity_Type(id,name,...)
  • Activity_Log(id,ts,member_id,type_id,extra_info)

索引:

All appropriate indexes are in place. Specifically, an index on order.ts does exist.

我尝试过这三个问题:

APPROACH 1

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

APPROACH 2

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

APPROACH 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上使用索引。

3 个答案:

答案 0 :(得分:0)

  

然后,要查找成员执行活动但没有订购的情况很简单:只需在订单表的此列中查找NULL值(或其他一些默认值)。唉,唉,这在我的情况下是不可能的......

如果你真的不能,那么我害怕暗示没有灵丹妙药。

如果没有重新访问您的架构(正如您已经正确诊断的那样,这是正确的继续方式),那么您最好的选择将是反连接(即左连接...其中为null)。

没有任何限制(例如,在最近订单/活动日期的子集上),您基本上将两个表连接在一起(在您的情况下,三个,但我怀疑您可以将其放在成员上)。

您可以做的最好的事情是将自己限制在订单和活动日志的时间戳子集上。这应该允许您减少连接大小。要实现这一点,您可能需要引入多列索引,其左侧为约束,右侧为join子句,例如: activity_log(ts,member_id)和orders(ts,member_id),或者相反的列,具体取决于您的数据。

答案 1 :(得分:0)

对于MySQL,选择取决于要比较的列:

如果您更改数据以进行比较,索引将毫无用处:

AND order.ts <= activity_log.ts + 3600 

在此示例中,ACTIVITY_LOG.ts上的索引无关紧要。考虑复合索引(单个索引,多个列)。

答案 2 :(得分:0)

仅供参考,我改变了上面的第三个查询,如下所示,当[开始时间] - [结束时间]窗口相对于activity_log和order中的行所代表的总时间跨度较小时,它大大加快了我的查询速度表。基本上,MySQL根据它们的有用程度决定是否在每个表上使用索引(on ts)...如果我要求“所有时间”,MySQL完全没有使用索引。但是,如果我要求一个小的持续时间,MySQL 最终使用索引。

SELECT …      
FROM activity_log JOIN member 
  ON activity_log.member_id = member.id 
LEFT JOIN (select * from order where order.ts > [start time] and order.ts < [end time + 3600]) orders
  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 

我不需要新的索引endts列。

非常感谢回复的人们,尤其是丹尼斯,他的评论使我得到了这个解决方案。 -M