现在有个功能需要统计每个用户的首单。自己写的 sql 不太满意,希望大家能指点下。
这是我写的 sql
SELECT * FROM order WHERE create_time IN ( SELECT min( create_time ) create_time FROM order WHERE order_status IN ( 'PRE_DELIVER', 'PRE_ACCEPT', 'AFTER_DELIVER' ) AND user_id in (1,2,3,4) AND deleted = 0 GROUP BY user_id ) GROUP BY user_id
我是先查询出每个用户第一单的时间,在通过时间去查询对应的订单。
但想不出怎么去优化,只能请教下大家,希望能给一些思路
1 linauror 2020-04-23 10:18:39 +08:00 ` SELECT *,MIN(create_time) FROM order WHERE order_status IN ( 'PRE_DELIVER', 'PRE_ACCEPT', 'AFTER_DELIVER' ) AND user_id in (1,2,3,4) AND deleted = 0 GROUP BY user_id ` 试试这个呢 |
2 kamisama 2020-04-23 10:21:17 +08:00 这种最好把表索引也发出来 |
3 dongisking 2020-04-23 10:27:17 +08:00 via Android 敢用子查询的都是菜鸡吧 |
![]() | 5 zhuzhibin 2020-04-23 10:36:30 +08:00 via iPhone @dongisking 有必要吗?人家是请教问题 ?你在干嘛? |
![]() | 6 zhuzhibin 2020-04-23 10:36:30 +08:00 via iPhone @dongisking 有必要吗?人家是请教问题 ?你在干嘛? |
7 liubx OP @kamisama 索引是`user_id`,`create_time``sale_order_no``deleted`这四个字段 |
8 liubx OP @dongisking 嗯,数据库确实不熟,大佬指点下啊 |
9 newtype0092 2020-04-23 10:42:22 +08:00 @dongisking 多干两年接触点复杂业务再来说话。 |
![]() | 10 CrazyMoon 2020-04-23 10:45:50 +08:00 不太懂怎么优化,不过感觉这个 sql 的正确性可能有问题。。如果有多订单同时创建的话,按照 create time 取会取出多余的订单 |
![]() | 11 moonsola 2020-04-23 11:06:20 +08:00 要我就选择在用户表里加个字段:first_order_id |
![]() | 12 lifespy 2020-04-23 11:09:27 +08:00 |
![]() | 14 lifespy 2020-04-23 11:11:48 +08:00 如果数据量特别大的话,建议专门独立一个表出去存储 |
15 mwiker 2020-04-23 11:15:38 +08:00 SELECT t.* FROM order t INNER JOIN ( SELECT user_id, min(create_time) create_time FROM order WHERE order_status IN ( 'PRE_DELIVER', 'PRE_ACCEPT', 'AFTER_DELIVER' ) AND user_id in (1,2,3,4) AND deleted = 0 GROUP BY user_id ) t2 ON t.user_id = t2.user_id AND t.create_time = t2.create_time |
![]() | 16 int11 2020-04-23 11:15:51 +08:00 订单 id 是自增的话,通过 id 大小去判断要好一些 |
17 mwiker 2020-04-23 11:17:59 +08:00 搞个 mysql8.0,有开窗函数做这些就很简单了 |
18 Tomorrowxxy 2020-04-23 11:18:55 +08:00 如果是 mysql5.7 版本以下的 |
![]() | 19 tinybaby365 2020-04-23 11:19:05 +08:00 order 表有多大,每个用户平均有几单?如果你是在线上 db 操作,我有点担心你这一次性把所有用户首单查出来的操作把会把你的 db 搞挂。如果是操作 OLAP 的 DB,那无所谓啦。 |
20 Tomorrowxxy 2020-04-23 11:21:03 +08:00 SELECT * FROM order WHERE order_status IN ( 'PRE_DELIVER', 'PRE_ACCEPT', 'AFTER_DELIVER' ) AND user_id in (1,2,3,4) AND deleted = 0 GROUP BY user_id ORDER BY order_id ) |
22 wuzhizuiguo 2020-04-23 11:24:16 +08:00 |
![]() | 24 DoUSeeMe 2020-04-23 11:26:52 +08:00 这个问题类似于获取分类前 N 条记录,你这里的 user 就是类,可以去查一查。 如果你要具体的优化语句,可以把表的代码复制出来,我比较懒,懒得去新建一个 |
![]() | 25 levelworm 2020-04-23 11:27:29 +08:00 不能用窗口函数吗?不能的话这个不知道行不行 select * from ( select * from order order by user_id, time asc ) as x group by cid |
![]() | 26 Nostalgiaaaa 2020-04-23 11:27:52 +08:00 试一下 partition by,解决这类首单,首次,最后一次的问题 简单搜了一下应该是一个类似的问题 https://stackoverflow.com/questions/18987727/sql-query-using-partition-by SELECT * FROM (SELECT * , ROW_NUMBER() over( partition by user_id ORDER BY create_time DESC ) AS rank FROM order WHERE order_status IN ( 'PRE_DELIVER', 'PRE_ACCEPT', 'AFTER_DELIVER' ) AND user_id IN (1,2,3,4) AND deleted = 0 ) WHERE rank = 1 另外,分析型 sql 建议做成定时任务定时刷新,比如半小时一次,实时查意义不大并且性能问题很严重 |
![]() | 27 levelworm 2020-04-23 11:30:08 +08:00 @Nostalgiaaaa 他这个估计版本比较老用不了窗口函数,我猜测 |
28 iffi 2020-04-23 11:45:15 +08:00 可用 join |
30 liubx OP ![]() @Nostalgiaaaa 嗯,谢谢回复。现在决定做成定时任务了 |
![]() | 31 icchux 2020-04-23 11:47:02 +08:00 @levelworm 这个可以 我上午刚用这个子查询的方法 统计一些东西 但这个是慢查询 数据量大的时候特别慢。。。。。。mysql 低版本对于这种统计要求真的烦心 |
![]() | 36 ooyy 2020-04-23 12:01:07 +08:00 sql 查询改动小,性能要看表数据量 with t as ( select *, ROW_NUMBER() over (partition by user_id ORDER BY create_time DESC ) AS rn from order inner join c_order on order.id = c_order.id ... ) select * from t where rn = 1 and a.order_status IN ( 'PRE_DELIVER', 'PRE_ACCEPT', 'AFTER_DELIVER' ) and ... |
![]() | 37 seanseek 2020-04-23 13:09:54 +08:00 能不能按照 id 和时间排序,然后再去个重? |
38 jeff0819 2020-04-23 15:11:18 +08:00 建议中间表,写个脚本跑出来再去查 |
![]() | 39 seanseek 2020-04-23 16:37:15 +08:00 group by user_id 可以 |