【问题标题】:Optimize Query - Select COUNT and SUM from the same subquery优化查询 - 从同一个子查询中选择 COUNT 和 SUM
【发布时间】:2021-11-14 23:28:23
【问题描述】:

我试图在 Stackoverflow 上找到解决方案,可能我的措辞有误。

我有一个查询需要很长时间才能执行。我相信有一些简单的方法可以改进它。例如,我两次使用相同的子查询来显示两个不同的列(sum 和 count),但在尝试自己解决时遇到了几个错误。

SELECT u.ID,
u.user_email AS mail, 
u.user_login AS userName, 
u.user_registered  AS signUpDate, 
(select meta_value from wp_usermeta where user_id = u.ID and meta_key = 'first_name' limit 1) as firstName, 
(select meta_value from wp_usermeta where user_id = u.ID and meta_key = 'last_name' limit 1) as lastName, 
(select meta_value from wp_usermeta where user_id = u.ID and meta_key = 'billing_phone' limit 1) as billingPhone, 
(select meta_value from wp_usermeta where user_id = u.ID and meta_key = 'shipping_phone' limit 1) as shippingPhone, 
(SELECT COUNT(meta_value) from wp_postmeta WHERE meta_key = '_order_total' and post_id IN (select post_id from wp_postmeta where meta_value = u.ID and meta_key = '_customer_user')) as orderCount,
(SELECT SUM(meta_value) from wp_postmeta WHERE meta_key = '_order_total' and post_id IN (select post_id from wp_postmeta where meta_value = u.ID and meta_key = '_customer_user')) as moneySpent 
FROM wp_users u;

【问题讨论】:

  • 我建议您简化查询以确定性能问题所在。也就是说,删除子查询,看看有什么影响。我会这是最后两个子查询,但我不知道您的数据或数据模型。
  • 你是对的,是两个子查询。由于 COUNT 和 SUM 使用完全相同的子查询,是否有办法一次性获取两列?
  • 。 .您应该编辑问题。如此重要的信息不应该只存在于 cmets 中。
  • WP 的元表索引效率低下。使用这个插件来获得更快的速度:wordpress.org/plugins/index-wp-mysql-for-speed

标签: mysql sql wordpress subquery query-optimization


【解决方案1】:

您的查询的问题在于您有嵌套的相关子查询。相关子查询对 SQL 性能造成巨大影响。在这种情况下,您想尝试将连接与聚合一起使用 (group by)。

我不了解您的数据库架构的复杂性,所以我不太确定您为什么将 firstName 等的子查询限制为 1 条记录。如果您提供更多详细信息,我可以编辑答案以适应它。

我建议你尝试这样的事情:

SELECT   u.ID,
         u.user_email AS mail, 
         u.user_login AS userName, 
         u.user_registered  AS signUpDate, 
         mfn.meta_value as firstName, 
         mln.meta_value as lastName, 
         mbp.meta_value as billingPhone, 
         msp.meta_value as shippingPhone, 
         COUNT(pval.meta_value) as orderCount,
         SUM(pval.meta_value) as moneySpent 
FROM     wp_users u
JOIN     wp_usermeta as mfn  ON u.ID = mfn.user_id  AND mfn.meta_key = 'first_name'
JOIN     wp_usermeta as mln  ON u.ID = mln.user_id  AND mln.meta_key = 'last_name'
JOIN     wp_usermeta as mbp  ON u.ID = mbp.user_id  AND mbp.meta_key = 'billing_phone'
JOIN     wp_usermeta as msp  ON u.ID = msp.user_id  AND msp.meta_key = 'shipping_phone'
JOIN     wp_postmeta as p    ON p.meta_value = u.ID AND p.meta_key = '_customer_user'
JOIN     wp_postmeta as pval ON pval.post_id = p.post_id AND pval.meta_key = '_order_total'
GROUP BY u.ID,
         u.user_email, 
         u.user_login, 
         u.user_registered, 
         mfn.meta_value, 
         mln.meta_value, 
         mbp.meta_value, 
         msp.meta_value 

根据 @O.Jones 的评论,考虑使用 LEFT JOINS 而不是 INNER JOINS,这样查询仍会返回缺少 meta_value 的结果。

【讨论】:

  • 好答案。一个建议:使用 LEFT JOIN 而不是普通的内部 JOIN 将允许结果集包含缺少某些元数据(例如 billing_phone)的行。 LIMIT 1 问题试图处理重复元数据行的远程可能性。 WordPress schema 允许这些重复,但大多数应用程序不允许
  • 嗨@O.Jones,感谢您的建议。同意,左连接可能更好。我看到了 usermeta 表的问题,并且没有修改/创建/过期日期。
  • 谢谢你们!它运作良好,我学到了一些东西。使用 LIMIT 1 会改变性能吗?
  • 降低性能的不是 LIMIT `,而是嵌套的子查询。
  • @Timur 您可以使用 wp_usermeta 表一次而不是 4 次。
【解决方案2】:

@Timur,根据您在第一个答案的 cmets 中的问题;你必须做这样的事情。您仍然需要连接,但 wp_usermeta 表不需要那么多连接。您仍然需要加入wp_postmeta 两次,因为您无法在一次加入中同时在“_customer_user”meta_key 上进行联接并同时检索“_order_total”meta_key

只是关于MAX(CASE WHEN um.meta_key = '...' THEN um.meta_value END) 逻辑的注释;相当于MAX(CASE WHEN um.meta_key = '...' THEN um.meta_value ELSE NULL END)

SELECT    u.ID, 
          u.user_email AS mail,
          u.user_login AS userName,
          u.user_registered AS signUpDate,
          MAX(CASE WHEN um.meta_key = 'first_name'     THEN um.meta_value END) AS firstName, 
          MAX(CASE WHEN um.meta_key = 'last_name'      THEN um.meta_value END) AS lastName, 
          MAX(CASE WHEN um.meta_key = 'billing_phone'  THEN um.meta_value END) AS billingPhone, 
          MAX(CASE WHEN um.meta_key = 'shipping_phone' THEN um.meta_value END) AS shippingPhone,
          COUNT(pval.meta_value) AS orderCount,
          SUM(pval.meta_value) AS moneySpent
FROM      wp_users AS u
LEFT JOIN wp_usermeta AS um   ON u.ID = um.user_id
LEFT JOIN wp_postmeta AS pm   ON u.ID = pm.meta_value      AND pm.meta_key = '_customer_user'
LEFT JOIN wp_postmeta AS pval ON pm.post_id = pval.post_id AND pval.meta_key = '_order_total'
WHERE     (um.meta_key IN ('first_name', 'last_name', 'billing_phone', 'shipping_phone') 
           OR um.meta_key IS NULL)
GROUP BY  u.ID, 
          u.user_email,
          u.user_login,
          u.user_registered;

我不是 100% 确定逻辑。让我知道是否需要调整

【讨论】:

    【解决方案3】:

    这里引入了子查询,以最大限度地减少使用单个表的次数,并从 GROUP BY 子句中删除几列以进行优化。如果需要特定用户/少数用户或任何搜索条件,请在每个子查询中使用 WHERE 子句。如果子查询不返回数据但需要用户信息,则使用 LEFT JOIN 而不是 INNER JOIN

    -- MySQL
    SELECT u.ID
         , u.user_email AS mail
         , u.user_login AS userName
         , u.user_registered  AS signUpDate
         , t.firstName
         , t.lastName
         , t.billingPhone
         , t.shippingPhone
         , r.orderCount
         , r.moneySpent
    FROM wp_users u      
    INNER JOIN (SELECT user_id
                     , MAX(CASE WHEN meta_key = 'first_name' THEN meta_value END) firstName 
                     , MAX(CASE WHEN meta_key = 'last_name' THEN meta_value END) lastName 
                     , MAX(CASE WHEN meta_key = 'billing_phone' THEN meta_value END) billingPhone 
                     , MAX(CASE WHEN meta_key = 'shipping_phone' THEN meta_value END) shippingPhone 
                FROM wp_usermeta 
                WHERE meta_key IN ('first_name', 'last_name', 'billing_phone', 'shipping_phone') 
                GROUP BY user_id) t
           ON u.ID = t.user_id
    INNER JOIN (SELECT p.meta_value
                     , COUNT(pval.meta_value) as orderCount
                     , SUM(pval.meta_value) as moneySpent 
                FROM wp_postmeta as p
                INNER JOIN wp_postmeta as pval
                        ON p.post_id = pval.post_id
                       AND p.meta_key = '_customer_user'
                       AND pval.meta_key = '_order_total'
                GROUP BY p.meta_value) r
            ON u.ID = r.meta_value;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-20
      • 1970-01-01
      相关资源
      最近更新 更多