以下查询大约需要200秒才能完成。我想要实现的目标是获得6笔或更多付款的用户,但尚未订购任何订单(不同市场有2个订单表)。
u.id
,ju.id
都是主键。
我已将user_id
和order_status
合并为两个订单表中的一个索引。如果我删除了COUNT()
表上的连接和mp_orders
,则查询需要8秒才能完成,但是使用它会花费太长时间。我想我已经将我可以拥有的所有东西编入索引,但我不明白为什么需要这么长时间才能完成。有什么想法吗?
SELECT
u.id,
ju.name,
COUNT(p.id) as payment_count,
COUNT(o.id) as order_count,
COUNT(mi.id) as marketplace_order_count
FROM users as u
INNER JOIN users2 as ju
ON u.id = ju.id
INNER JOIN payments as p
ON u.id = p.user_id
LEFT OUTER JOIN orders as o
ON u.id = o.user_id
AND o.order_status = 1
LEFT OUTER JOIN mp_orders as mi
ON u.id = mi.producer
AND mi.order_status = 1
WHERE u.package != 1
AND u.enabled = 1
AND u.chart_ban = 0
GROUP BY u.id
HAVING COUNT(p.id) >= 6
AND COUNT(o.id) = 0
AND COUNT(mi.id) = 0
LIMIT 10
付款表
+-----------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+---------------+------+-----+---------+----------------+
| id | bigint(255) | NO | PRI | NULL | auto_increment |
| user_id | bigint(255) | NO | | NULL | |
+-----------------+---------------+------+-----+---------+----------------+
订单表(mp_orders表几乎相同)
+-----------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-----------------+---------------+------+-----+---------+----------------+
| id | int(255) | NO | PRI | NULL | auto_increment |
| order_number | varchar(1024) | NO | MUL | NULL | |
| user_id | int(255) | NO | MUL | NULL | |
+-----------------+---------------+------+-----+---------+----------------+
答案 0 :(得分:4)
您不需要对订单行进行COUNT操作,您需要检索没有订单的用户,这不是真的相同。
而不是计算,过滤没有订单的用户:
SELECT
u.id,
ju.name,
COUNT(p.id) as payment_count
FROM users as u
INNER JOIN users2 as ju
ON u.id = ju.id
INNER JOIN payments as p
ON u.id = p.user_id
LEFT OUTER JOIN orders as o
ON u.id = o.user_id
AND o.order_status = 1
LEFT OUTER JOIN mp_orders as mi
ON u.id = mi.producer
AND mi.order_status = 1
WHERE u.package != 1
AND u.enabled = 1
AND u.chart_ban = 0
AND o.id IS NULL -- filter happens here
AND mi.id IS NULL -- and here
GROUP BY u.id
HAVING COUNT(p.id) >= 6
LIMIT 10
这会阻止引擎计算每个用户的每个订单,您将获得大量时间。
可以认为引擎应该使用索引进行计数,因此计数必须足够快 I will quote from a different site: InnoDB COUNT(id) - Why so slow?
可能与缓冲有关,InnoDb不会缓存索引 将实际数据行缓存到内存中,因为这样做 似乎是一个简单的扫描它不加载主键索引但是 将所有数据放入RAM,然后在其上运行查询。这可能需要 一段时间工作 - 希望如果您在此之后运行查询 在同一张桌子上,它们会跑得更快。
MyIsam将索引加载到RAM中,然后运行其计算 这个空间然后返回一个结果,因为索引一般很多 比表中的所有数据小得多,你应该看到一个 那里的直接差异。
另一种选择可能是innodb将数据存储在磁盘上的方式 - innodb文件是一个虚拟表空间,因此如果你有一个,你不一定按表中的数据排序 碎片化的数据文件然后这可能会为您创建问题 磁盘IO因此运行速度较慢。 MyIsam一般都是 顺序文件,因此如果您使用索引来访问数据 系统确切知道该行位于磁盘上的哪个位置 - 你对innodb没有这种奢侈,但我不这么认为 只需一个简单的计数即可发挥特殊问题(*) ==================== http://dev.mysql.com/doc/refman/5.0/en/innodb-restrictions.html 解释了这个:
InnoDB不保留表中的内部行数。 (在 实践,由于多版本化,这会有点复杂。) 要处理SELECT COUNT(*)FROM t语句,InnoDB必须扫描一个 表的索引,如果索引不完全,则需要一些时间 在缓冲池中。要快速计数,您必须使用计数器 您自己创建的表,并让您的应用程序更新它 它插入和删除它。如果你的表没有改变 通常,使用MySQL查询缓存是一个很好的解决方案。显示表 如果近似行数足够,也可以使用STATUS。看到 第14.2.11节“InnoDB性能调优技巧”。 =================== todd_farmer:它确实解释了差异--MyISAM理解COUNT(ID)其中ID是PK列 与COUNT(*)相同,MyISAM在InnoDB时保持预先计算 没有。
答案 1 :(得分:3)
请尝试通过COUNT() = 0
检查删除IS NULL
:
SELECT
u.id,
ju.name,
COUNT(p.id) as payment_count,
0 as order_count,
0 as marketplace_order_count
FROM users as u
INNER JOIN users2 as ju
ON u.id = ju.id
INNER JOIN payments as p
ON u.id = p.user_id
LEFT OUTER JOIN orders as o
ON u.id = o.user_id
AND o.order_status = 1
LEFT OUTER JOIN mp_orders as mi
ON u.id = mi.producer
AND mi.order_status = 1
WHERE
u.package != 1
AND u.enabled = 1
AND u.chart_ban = 0
AND mi.id IS NULL
AND o.id IS NULL
GROUP BY u.id
HAVING COUNT(p.id) >= 6
LIMIT 10
但我认为8秒对普通查询来说仍然太多了。您应该发布主查询的解释计划而不使用OUTER JOINS来查看错误,例如包,启用和图表禁止过滤器可能完全破坏它。