Mysql 如何优化我关于在同一个表中联接3个表的查询?
我想获得从三个月前开始每月购买我产品的id客户。今天是2020-02-15。因此,我希望获得在2019年11月、2019年12月、2020年1月购买的客户 我只有一个表顺序MySQL,如下所示:Mysql 如何优化我关于在同一个表中联接3个表的查询?,mysql,sql,database,Mysql,Sql,Database,我想获得从三个月前开始每月购买我产品的id客户。今天是2020-02-15。因此,我希望获得在2019年11月、2019年12月、2020年1月购买的客户 我只有一个表顺序MySQL,如下所示: SELECT g1.`id_cust` FROM `orders` g1 JOIN `orders` g2 ON g2.`id_cust` = g1.`id_cust` AND g2.`buy_date` >= STR_TO_DATE(CONCAT('01-',
SELECT g1.`id_cust`
FROM `orders` g1
JOIN `orders` g2
ON g2.`id_cust` = g1.`id_cust`
AND g2.`buy_date` >= STR_TO_DATE(CONCAT('01-', LPAD(MONTH(DATE_SUB(NOW(), INTERVAL 2 MONTH)), 2, '0'), '-', YEAR(DATE_SUB(NOW(), INTERVAL 2 MONTH))), '%d-%m-%Y')
AND g2.`buy_date` < STR_TO_DATE(CONCAT('01-', LPAD(MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH)), 2, '0'), '-', YEAR(DATE_SUB(NOW(), INTERVAL 1 MONTH))), '%d-%m-%Y')
JOIN `orders` g3
ON g3.`id_cust` = g1.`id_cust`
AND g3.`id_cust` = g2.`id_cust`
AND g3.`buy_date` >= STR_TO_DATE(CONCAT('01-', LPAD(MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH)), 2, '0'), '-', YEAR(DATE_SUB(NOW(), INTERVAL 1 MONTH))), '%d-%m-%Y')
AND g3.`buy_date` < STR_TO_DATE(CONCAT('01-', LPAD(MONTH(NOW()), 2, '0'), '-', YEAR(NOW())), '%d-%m-%Y')
WHERE g1.`buy_date` >= STR_TO_DATE(CONCAT('01-', LPAD(MONTH(DATE_SUB(NOW(), INTERVAL 3 MONTH)), 2, '0'), '-', YEAR(DATE_SUB(NOW(), INTERVAL 3 MONTH))), '%d-%m-%Y')
AND g1.`buy_date` < STR_TO_DATE(CONCAT('01-', LPAD(MONTH(DATE_SUB(NOW(), INTERVAL 2 MONTH)), 2, '0'), '-', YEAR(DATE_SUB(NOW(), INTERVAL 2 MONTH))), '%d-%m-%Y')
GROUP BY g1.`id_cust`
订单表主键=ID自动增量:
-----------------------------------------------
| ID | id_cust | buy_date |
-----------------------------------------------
| 1 | 10 | 2019-11-01 |
| 2 | 11 | 2019-11-10 |
| 3 | 10 | 2019-12-11 |
| 4 | 12 | 2019-12-12 |
| 5 | 10 | 2020-01-13 |
| 6 | 11 | 2020-01-14 |
| 7 | 12 | 2020-01-15 |
-----------------------------------------------
根据我的要求,答案是id_cust 10
我试过了,结果如下:
SELECT g1.`id_cust`
FROM `orders` g1
JOIN `orders` g2
ON g2.`id_cust` = g1.`id_cust`
AND g2.`buy_date` >= STR_TO_DATE(CONCAT('01-', LPAD(MONTH(DATE_SUB(NOW(), INTERVAL 2 MONTH)), 2, '0'), '-', YEAR(DATE_SUB(NOW(), INTERVAL 2 MONTH))), '%d-%m-%Y')
AND g2.`buy_date` < STR_TO_DATE(CONCAT('01-', LPAD(MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH)), 2, '0'), '-', YEAR(DATE_SUB(NOW(), INTERVAL 1 MONTH))), '%d-%m-%Y')
JOIN `orders` g3
ON g3.`id_cust` = g1.`id_cust`
AND g3.`id_cust` = g2.`id_cust`
AND g3.`buy_date` >= STR_TO_DATE(CONCAT('01-', LPAD(MONTH(DATE_SUB(NOW(), INTERVAL 1 MONTH)), 2, '0'), '-', YEAR(DATE_SUB(NOW(), INTERVAL 1 MONTH))), '%d-%m-%Y')
AND g3.`buy_date` < STR_TO_DATE(CONCAT('01-', LPAD(MONTH(NOW()), 2, '0'), '-', YEAR(NOW())), '%d-%m-%Y')
WHERE g1.`buy_date` >= STR_TO_DATE(CONCAT('01-', LPAD(MONTH(DATE_SUB(NOW(), INTERVAL 3 MONTH)), 2, '0'), '-', YEAR(DATE_SUB(NOW(), INTERVAL 3 MONTH))), '%d-%m-%Y')
AND g1.`buy_date` < STR_TO_DATE(CONCAT('01-', LPAD(MONTH(DATE_SUB(NOW(), INTERVAL 2 MONTH)), 2, '0'), '-', YEAR(DATE_SUB(NOW(), INTERVAL 2 MONTH))), '%d-%m-%Y')
GROUP BY g1.`id_cust`
请帮助我简化我的语法,因为当它在大量数据上运行时,速度非常慢,或者如果我的语法错误,请更正我的语法。这怎么样
select c.id_cust
from (select o.id_cust, year(buy_date) as yyyy, month(buy_date) as mm,
row_number() over (partition by o.id_cust) as month_counter
from orders o
where buy_date >= date_format(current_date - interval 3 month, '%Y-%m-%d') and
buy_date < date_format(current_date, '%Y-%m-%d')
group by id_cust, yyyy, mm
) c
where month_counter = 3;
这只会过滤到你关心的三个月。然后它按年份和月份进行聚合,只返回第三行
事实上,这更容易表达为:
select o.id_cust
from orders o
where buy_date >= date_format(current_date - interval 3 month, '%Y-%m-%d') and
buy_date < date_format(current_date, '%Y-%m-%d')
group by o.id_cust
having count(distinct year(buy_date), month(buy_date)) = 3;
我将使用戈登的第二个问题。但是,如果您的代码作为练习使用,您可以通过在buy_date、id_cust和id_cust、buy_date上创建索引来优化执行时间。第一个用于WHERE子句,第二个用于ON子句 使用此模式
CREATE TABLE orders (
`ID` INTEGER primary key,
`id_cust` INTEGER,
`buy_date` VARCHAR(10)
);
查询的解释结果是
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
| --- | ----------- | ----- | ---------- | ---- | ------------- | --- | ------- | --- | ---- | -------- | -------------------------------------------------- |
| 1 | SIMPLE | g1 | | ALL | | | | | 7 | 14.29 | Using where; Using temporary; Using filesort |
| 1 | SIMPLE | g2 | | ALL | | | | | 7 | 14.29 | Using where; Using join buffer (Block Nested Loop) |
| 1 | SIMPLE | g3 | | ALL | | | | | 7 | 14.29 | Using where; Using join buffer (Block Nested Loop) |
没有使用任何键,块嵌套循环听起来非常糟糕
在添加索引之后
ALTER TABLE orders ADD INDEX (buy_date, id_cust);
ALTER TABLE orders ADD INDEX (id_cust, buy_date);
它现在看起来好多了,尽管它决定不使用我的第一个索引,可能是因为GROUPBY
然后,我会将查询简化为:
SELECT DISTINCT g1.id_cust
FROM orders g1
JOIN orders g2 ON g2.id_cust = g1.id_cust
JOIN orders g3 ON g3.id_cust = g1.id_cust
-- AND g3.id_cust = g2.id_cust -- redundant condition
WHERE g1.buy_date >= DATE_FORMAT(NOW() - INTERVAL 3 MONTH, '%Y-%m-01')
AND g1.buy_date < DATE_FORMAT(NOW() - INTERVAL 2 MONTH, '%Y-%m-01')
AND g2.buy_date >= DATE_FORMAT(NOW() - INTERVAL 2 MONTH, '%Y-%m-01')
AND g2.buy_date < DATE_FORMAT(NOW() - INTERVAL 1 MONTH, '%Y-%m-01')
AND g3.buy_date >= DATE_FORMAT(NOW() - INTERVAL 1 MONTH, '%Y-%m-01')
AND g3.buy_date < DATE_FORMAT(NOW() - INTERVAL 0 MONTH, '%Y-%m-01')
-- GROUP BY g1.id_cust -- You can use DISTINCT instead
您在HAVING子句中缺少“=3”,在第一个查询中将select o.id\u cust更改为select c.id\u cust-是的,我在HAVING子句中缺少“=3”,请您回答,但为了得到我想要的。。您必须将日期格式更改为“%Y-%m-01”@RikcatKristianLumbanGaol。我注意到你没有接受这个答案。这不是编写代码最有效的方法吗?