MYSQL 订单查询求助

2020-12-30 15:15:32 +08:00
 marine2c

假设有一张订单表 A,有下单时间和 IP,要求查出任意 30 分钟内同一 IP 交易超过 20 次的记录,该怎么写?自己想的是 group by ip,但是怎么控制任意 30 分钟以内呢

4325 次点击
所在节点    MySQL
37 条回复
drrrtt
2020-12-30 19:19:38 +08:00
自关联,group by ip,off set = 19
LEFT
2020-12-30 19:20:31 +08:00
仅供参考
mmdsun
2020-12-30 19:45:04 +08:00
先 mark 一下。我写过类似的 SQL 。现在忘记了。。

按时间分组 ,having 过滤 再关联?

这是按 1 天分组的 group by,查出来是 1 天下单超过 20 单的,所有 ip:

GROUP BY DATE_FORMAT(create_time, '%Y-%m-%d')
having count(*)> 20

这是按 5 秒分组的

SELECT id,time,count(1),second(time),floor(second(time)/5) FROM `personcount` GROUP BY DATE_FORMAT( concat(date(time),' ',hour(time),':',minute(time),':',floor(second(time)/5)) ,'%Y-%m-%d %H:%i:%s');
leeg810312
2020-12-30 21:39:38 +08:00
很难吗? where 下单时间 group by IP having count(1)>20 不就可以了吗?下单时间和 IP 要加索引。
PopRain
2020-12-30 23:55:42 +08:00
你这个描述不够清晰,你是需要那种:
1.用户指定一个 30 分钟的时间段,查出超过 20 条交易记录的 IP
2.对历史数据进行统计,找出每 30 分钟发生过 20 比以上交易的 IP

1:比较简单,时间过滤,group by+having
2:可以以记录顺序倒序,做个子查询汇总当前记录前面 30 分钟的相同 ip 的数据,效率可能低一点,但是肯定可行。
ttys001
2020-12-31 00:09:00 +08:00
select ip, sum(t4.cnt) from (select 1 cnt, * from ((select * from table t1) inner join (select * from table t2) on (t1.time-t2.time <= 30*60)) t3)t4 from t4 group by ip having sum(t4.cnt) >= 20;
好久没写 mysql 了,语法估计有问题。但是真的不难,自关联+groupby 。
dzdh
2020-12-31 00:16:15 +08:00
这个任意三十分钟是不是指的,当前已有数据和未来数据中,以每一单的时间(或分)往前(或后)推 30 分钟,有超过 20 的?

流计算?
dzdh
2020-12-31 00:16:51 +08:00
@dzdh 像令牌桶一样?
dusu
2020-12-31 02:51:48 +08:00
这种需求单靠 sql 压根就是自讨苦吃

窗口按时间移动的需求

显然用 redis 写个 key+ttl 就好解决的问题

例如每 5 分钟内 每个用户使用固定 key

用户交易成功 incr 记录次数

每次都往前推 4 个 key 去计算和是否满足大于 20 次总量 ,如果超过,记录到异常 list 当中备查即可

如果精度要求高那就每 1 分钟一个固定 key

这实现不管是 30 分钟 1 个小时 6 个小时 12 个小时都可以做

sql 去做这个事…想想都蛋疼
ashong
2020-12-31 09:09:30 +08:00
@leeg810312 他这个是非固定时间,要查的是任意时间段内频繁下单,估计是避免恶意下单吧
marine2c
2020-12-31 09:18:58 +08:00
@dzdh 是的,理解没错
marine2c
2020-12-31 09:19:26 +08:00
@LEFT 谢谢,我看看
marine2c
2020-12-31 09:21:08 +08:00
@dusu 只用了 MySQL,蛋疼
Habyss
2020-12-31 10:08:01 +08:00
查符合的 ip
```
select distinct a.ip
from (
select a.ip, a.时间, count(b.时间) num
from table a,
table b
where a.ip = b.ip and b.时间 between date_sub(a.时间, interval 30 minute) and a.时间
group by a.ip, a.时间
having num > 20) a;
```
查符合的单号
```
select a.ip, a.时间, count(b.时间) num, group_concat(b.单号) ids
from myt_sales_order a,
myt_sales_order b
where a.ip = b.ip and b.时间 between date_sub(a.时间, interval 30 minute) and a.时间
group by a.ip, a.时间
having num > 4;
```
AntoniotheFuture
2020-12-31 10:10:46 +08:00
假设你的 ordertime 是秒时间戳:

SELECT iprdertime, c
FROM (
SELECT CONCAT(o1.ip, o1.ordertime) AS iprdertime, count(*) AS c
FROM orders o1
JOIN (
SELECT ip, ordertime
FROM orders
) o2
ON o1.ip = o2.ip
AND o1.ordertime >= o2.ordertime
AND o1.ordertime < o2.ordertime + 1800
GROUP BY CONCAT(o1.ip, o1.ordertime)
ORDER BY o1.ordertime
) oc
WHERE c >= 20

只测试了一小部分数据,性能可能比较低,但满足你的需求,可以通过前置筛选的方法减少查询量,如果是用来监控的,可以每天运行一次(查询前 24.5 小时内新增的数据)
AntoniotheFuture
2020-12-31 10:11:47 +08:00
@AntoniotheFuture 鉴于 ORDER BY 对性能影响较大,这里可以去掉
marine2c
2020-12-31 10:25:22 +08:00
@ttys001
@Habyss
@AntoniotheFuture
感谢各位大佬提供的思路,自关联查询可以满足要求了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://yangjunhui.monster/t/740336

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX