我有张表
需求 查询销售排行前10的商品
SELECT o.product_id,COUNT(*) AS num
FROM t_order o
GROUP BY o.product_id
ORDER BY COUNT(*) DESC
问题 通过explain 发现 有一个致命的问题 mysql 使用了临时表和 filesort 这种情况表数据一旦上了百万,效率会断崖式下降
请问如何解决这个order by 问题
最后附上建表sql
CREATE TABLE t_order
(order_id
int(11) NOT NULL AUTO_INCREMENT,product_id
int(11) DEFAULT NULL,deal_price
int(11) DEFAULT NULL,
PRIMARY KEY (order_id
),
KEY idx_product_id
(product_id
)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
insert into t_order
(order_id
,product_id
,deal_price
) values (1,2,18);
insert into t_order
(order_id
,product_id
,deal_price
) values (2,3,30);
insert into t_order
(order_id
,product_id
,deal_price
) values (3,2,9);
CREATE TABLE t_product
(product_id
int(11) NOT NULL AUTO_INCREMENT,product_name
varchar(32) DEFAULT NULL,
PRIMARY KEY (product_id
),
KEY idx_product_id
(product_id
)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4;
insert into t_product
(product_id
,product_name
) values (2,'葡萄');
insert into t_product
(product_id
,product_name
) values (3,'苹果');
insert into t_product
(product_id
,product_name
) values (4,'香蕉');
这种sql避免不了 "Using temporary;" 和 "Using filesort",这是order by 非基表数据时会产生的,
基于group by生成temporary的数据执行order by ,就是filesort了。
一般情况下,这个查询应该要带条件,如果是订单表的话,一般会按某个时间区间来进行统计,这个时候统计的数据量会减少很多。
还有,如非必要,一般不建议在sql中使用order by,有时甚至可以取出数据来再进行排序,
你可以尝试把order by 去掉,会发现 "Using temporary;" 和 "Using filesort"都没有了,
另外,你可以测试一下下面这个sql,我这里数据量不够比较不出效果
select * from
(SELECT o.product_id,COUNT(1) AS num
FROM t_order o
GROUP BY o.product_id) x
ORDER BY 2 DESC
在订单表的product_id字段添加索引,同时将sql中的count(*)换成count(1)或count(product_id)试试,然后执行explain
干掉sql里的ORDER BY COUNT(*) DESC
,干掉后就没有Using temporary; Using filesort
了。
把排序放到代码里,代码操作速度可比执行sql快多了。
销售前10这种业务需求不应该每次去查数据库,每天晚上跑一次历史数据,实际查询的时候当天数据count以后再加上历史汇总的数据进行排序