基本上我们正在收集一些关于用户的一对一元数据(名称,地址),然后我们会对他们的订单进行一些摘要报告。
SELECT
-- STUDENT DATA
wp_users.user_email AS 'email',
-- STUDENT METADATA
um_fn.meta_value AS 'first_name',
um_ln.meta_value AS 'last_name',
### MANY MORE ###
-- ORDER DATA
MAX(pmt_orders.order_date) last_order,
MIN(pmt_orders.order_date) first_order,
COUNT(pmt_order_course.fk_order_id) life_courses,
### MANY MORE AGGREGATE FUNCTIONS ###
FROM wp_users
### LEFT OUTER JOINS, INNER JOINS, LEFT JOINS (FOR THE AGGREGATE FUNCTIONS) ###
-- STUDENT METADATA
LEFT JOIN wp_usermeta um_fn ON wp_users.id = um_fn.user_id AND um_fn.meta_key = 'shipping_first_name'
LEFT JOIN wp_usermeta um_ln ON wp_users.id = um_ln.user_id AND um_ln.meta_key = 'shipping_last_name'
### MANY MORE ###
WHERE pmt_order_course.unenroll_date IS NULL OR pmt_order_course.unenroll_date = '0000-00-00'
GROUP BY wp_users.user_email
时间:13秒
我们开始调查并将其分解为一个元数据查询(0.5秒)和其他内容(2秒)。基本上只是将列拆分为两个单独的查询。
注意:我确实尝试将每个非聚合选择添加到GROUP BY
中,以便我们符合严格模式。对绩效的零影响。
困惑,然后我们将其转回一个大型查询。方法是将非聚合选择移动到外部选择中。
SELECT users.*,
-- STUDENT METADATA
um_fn.meta_value AS 'first_name',
um_ln.meta_value AS 'last_name',
### MANY MORE ###
FROM (
SELECT
-- STUDENT DATA
wp_users.ID,
wp_users.user_email AS 'email',
-- ORDER DATA
MAX(pmt_orders.order_date) last_order,
MIN(pmt_orders.order_date) first_order,
COUNT(pmt_order_course.fk_order_id) life_courses,
### MANY MORE AGGREGATE FUNCTIONS ###
FROM wp_users
### LEFT OUTER JOINS, INNER JOINS, LEFT JOINS (FOR THE AGGREGATE FUNCTIONS) ###
WHERE pmt_order_course.unenroll_date IS NULL OR pmt_order_course.unenroll_date = '0000-00-00'
GROUP BY wp_users.user_email
) AS users
-- STUDENT METADATA
LEFT JOIN wp_usermeta um_fn ON users.id = um_fn.user_id AND um_fn.meta_key = 'shipping_first_name'
LEFT JOIN wp_usermeta um_ln ON users.id = um_ln.user_id AND um_ln.meta_key = 'shipping_last_name'
### MANY MORE ###
时间:2秒
查询2产生相同的结果,在数学上等同于查询1.它在2秒内运行。
我可以理解为什么,MySQL会为每个订单查找一次元数据然后按用户进行聚合,而不是每个用户查找一次元数据。
一些分析数据:wp_users
表很大,聚合行大约是每个用户两行。
为什么MySQL优化器本身不能解决这个问题?有没有其他方法可以编写看起来更具表现力的查询(如查询1),同时使MySQL使用查询2的更快的执行路径?
答案 0 :(得分:1)
我要说的是,在查询1中请记住,您在users表中加入了其余未聚合查询记录的次数。
但是,在查询2中,您只加入聚合数据的次数。
这就是它与众不同的原因。
如果您只需要名字和姓氏,我猜您可以使用子查询来获取名字和姓氏,而不是um_fn.meta_value,但如果选项2很快,您可能最好离开它一个人(或添加没有人会读的评论)。在查询1中可以尝试一些可能更具可读性的东西吗?
rxjs
答案 1 :(得分:0)
第一个查询是否有适当的索引?
尝试添加:
ALTER TABLE `wp_usermeta` ADD INDEX `wp_usermeta_index_1` (`meta_key`, `user_id`, `meta_value`);
ALTER TABLE `wp_users` ADD INDEX `wp_users_index_1` (`id`, `user_email`);
此外,只要有GROUP BY子句,就可以添加显式ORDER BY子句。如果排序没有意义,请添加ORDER BY NULL以避免冗余订单(这会降低性能)。
答案 2 :(得分:0)
问题1:优化器没有(或不能)执行您想象的所有优化。为什么不?你可以免费获得很多好东西。在MySQL中发布更多免费内容需要数年时间。
问题2:"爆炸 - 内爆" - JOINs
增加要查看的行数,然后GROUP BY
缩减回到您开始时的行数。 Ctznkane525部分解决了这个问题。
问题3:键值(EAV)架构真的很糟糕;除了放弃这个概念之外,没有什么能解决它。
问题4:WP" meta"数据是EAV。见问题2.
问题5:WP在wp_postmeta上有糟糕的索引。部分解决方案:http://mysql.rjweb.org/doc.php/index_cookbook_mysql#speeding_up_wp_postmeta(也许" post_id"需要替换为" user_id"在该引用中。)请注意,这超出了Tomer的建议。
问题6:MySQL没有答案"摘要表"。我不确定这对你有用,但这是一个讨论:http://mysql.rjweb.org/doc.php/summarytables