【问题标题】:How to optimize this query with some subqueries in it如何使用其中的一些子查询优化此查询
【发布时间】:2013-03-18 10:05:46
【问题描述】:

我的查询是这样的:

SELECT date_format( created_at, '%Y-%m-%d' ) AS the_date, 
COUNT(s.id) AS total, 
(SELECT COUNT(ks.id) FROM kc_shares ks WHERE site = 'facebook' AND date_format( created_at, '%Y-%m-%d' ) = the_date ) AS total_facebook, 
(SELECT COUNT(ks.id) FROM kc_shares ks WHERE site = 'twitter' AND date_format( created_at, '%Y-%m-%d' ) = the_date ) AS total_twitter 
FROM `kc_shares` s
GROUP BY `the_date`

我想要得到的是每日分享的数量,其中包含总计、facebook 的总份额(因此 site = 'facebook')和 twitter 的总份额。这就是为什么我需要GROUP BY

当它有几千行时,没有问题。但是该表目前有将近 200,000 行,并且查询非常慢,大约需要 20-30 秒,我猜还要更多。

我尝试向 site 和 created_at 字段添加索引,但无济于事。

谢谢

【问题讨论】:

    标签: mysql query-optimization subquery


    【解决方案1】:

    我认为子查询正在消耗性能。所以也许你可以这样做:

    SELECT 
        date_format( created_at, '%Y-%m-%d' ) AS the_date, 
        COUNT(s.id) AS total, 
        SUM(CASE WHEN s.site='facebook' THEN 1 ELSE 0 END) AS total_facebook, 
        SUM(CASE WHEN s.site='twitter' THEN 1 ELSE 0 END) AS total_twitter
    FROM 
        `kc_shares` s
    GROUP BY 
        `the_date
    

    `

    【讨论】:

      【解决方案2】:

      移动子选择以便加入它们,而不是对每个返回的行进行子选择。

      类似这样的东西(未经测试):-

      SELECT date_format( created_at, '%Y-%m-%d' ) AS the_date, 
      COUNT(s.id) AS total, 
      Sub1.total_facebook, Sub2.total_twitter
      FROM `kc_shares` s
      LEFT OUTER JOIN (SELECT date_format( created_at, '%Y-%m-%d' ) AS sub_date, COUNT(ks.id) AS total_facebook FROM kc_shares ks WHERE site = 'facebook' GROUP BY sub_date ) Sub1 ON date_format( created_at, '%Y-%m-%d' ) = Sub1.sub_date
      LEFT OUTER JOIN (SELECT date_format( created_at, '%Y-%m-%d' ) AS sub_date, COUNT(ks.id) AS total_twitter  FROM kc_shares ks WHERE site = 'twitter' GROUP BY sub_date ) Sub2 ON date_format( created_at, '%Y-%m-%d' ) = Sub2.sub_date
      GROUP BY `the_date`
      

      虽然找到一种方法来对非派生列(即日期/时间的日期部分)进行连接也会有所帮助。在这里可能是一个很好的例子来进行一点或非规范化,在当前存储的日期/时间之外添加一个仅用于日期的字段。

      【讨论】:

      • 我已经尝试将它移动到连接中,它好多了,查询现在需要 6-8 秒,但@Arion 给出了完美的解决方案。不过谢谢!
      【解决方案3】:

      另一种方法是更改​​查询的工作方式。以下将提供每一天/网站的行,而不是将两个网站放在同一行。

      SELECT 
         date_format( created_at, '%Y-%m-%d' ) AS the_date,site, 
         count(id) 
      FROM 
         kc_shares s 
      where 
         (site="facebook" or site="twitter") ) 
      group by  
         created_at, site
      

      我假设 created_at 是一个日期字段。

      这应该提供相同的数据(我想,我没有尝试过),但格式不同。

      尝试在 (created_at,site) 上建立索引。

      【讨论】:

      • 我使用子查询的原因是因为在选择中会有另一个聚合。所以它就像 count(id) as total,count(something_id) as total_something 等等。因此,结果将被很好地格式化,这样我就可以每天循环获取共享的总数,无论它们是 facebook、twitter等加上其他聚合值。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-03-01
      相关资源
      最近更新 更多