MySQL GROUP BY 使用 JOIN 时速度较慢

标签 mysql sql query-optimization

我们要实现的目标:

基本上,我们正在收集一些关于用户(姓名、地址)的一对一元数据,然后我们正在对他们的订单进行一些汇总报告。

查询 1

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 中,以便我们符合严格模式。对性能零影响。

查询 2

很困惑,然后我们把它变回一个大查询。该方法是将非聚合选择移动到外部选择。

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 的更快执行路径?

最佳答案

我要说的是,请记住,在查询 1 中,您在用户表上加入了其余未聚合查询记录的次数。

然而,在查询 2 中,您只是加入聚合数据的次数。

这就是它与众不同的原因。

如果你只需要名字和姓氏,我想你可以做一个子查询来获取名字和姓氏而不是 um_fn.meta_value,但是如果选项 2 很快,你最好离开它单独(或添加没有人会阅读的评论)。尽管在查询 1 中尝试了一些可能更具可读性的东西?

(SELECT um_fn.meta_value FROM wp_usermeta um_fn ON wp_users.id = um_fn.user_id AND um_fn.meta_key = 'shipping_first_name') as 'first_name',
(SELECT um_ln.meta_value FROM wp_usermeta um_ln ON wp_users.id = um_ln.user_id AND um_ln.meta_key = 'shipping_last_name') as 'last_name',

关于MySQL GROUP BY 使用 JOIN 时速度较慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48122118/

相关文章:

sql - 将不规则的时间序列拆分为规则的月平均值 - R

php - 如何在MYSQL列中过滤写入的消息?

sqlite - SQLite 外键是否自动具有索引?

entity-framework - Entity Framework 5.0 First 或 Group By Issue - 从 2.2 升级到 5.0 之后

algorithm - 如何在 sqrt{n} 时间内执行范围更新?

mysql - 对mysql表中的连接记录进行排序

php - 不插入或者报错

mysql - 与查询的输出混淆

mysql - 仅选择不唯一的行 [MySQL]

python - 模板的 Django/SQL 复式表