【问题标题】:how to stop or limit select after selecting top 100 row?选择前100行后如何停止或限制选择?
【发布时间】:2013-12-22 02:56:39
【问题描述】:

我有一个这样的查询 -

SELECT c.msisdn,SUM(c.dataVolumeDownLink+c.dataVolumeUpLink) AS datasum 
FROM cdr c 
WHERE c.eveDate>='2013-10-29'
GROUP BY c.msisdn 
ORDER BY datasum DESC;

这个需要 4 分钟。我有一个关于 evedate 的索引。

CDR 表包含从“2013-10-01”到“2013-10-30”每天的 2400000 条记录。但我只想选择前 100 条记录。我想如何优化这个查询。

我用过限制子句,但没有任何好处。

所以请告诉我如何优化这个查询。

谢谢。

【问题讨论】:

  • 添加LIMIT 0, 100 应该可以。据我所知limit会在前100行满足要求后停止查询。
  • 因为您的 datesum 字段是动态计算的,所以它必须明确排序,这需要时间 - 无法解决这个问题。 limit 在这里帮不了你。如果您要创建查询视图,然后使用限制进行选择,您可能会看到性能提升。
  • 啊,是的,这是最好的选择。创建一个视图将完成这项工作。好想法@BoristheSpider
  • @BoristheSpider 这是正确的,但我无法为 7 个不同的查询创建 7 个视图。那么除了limit和view还有什么方法可以让查询更快吗?

标签: mysql performance optimization query-optimization limit


【解决方案1】:

你放了

LIMIT 100

在 .... ORDER BY datasum DESC 这里;

like .... ORDER BY datasum DESC LIMIT 100;

【讨论】:

    【解决方案2】:

    如果记录均匀分布,一天将有 ​​80k 行。 GROUP BY 超过 80k 的操作可能不需要 4 分钟(我猜)

    我不确定您是否有以下索引:

    INDEX(eveDate, msisdn)
    

    使用上述索引,记录按 eveDate 和 msisdn 排序,因此优化了 GROUP BY 操作。即,相同的 msisdns 位于相同的块。我猜下面的查询比你的查询要快。

    第一季度

    SELECT x.msisdn, SUM(datasum)
    FROM
    (
        SELECT c.msisdn AS msisdn,
            SUM(c.dataVolumeDownLink+c.dataVolumeUpLink) AS datasum 
        FROM cdr c 
        WHERE c.eveDate>='2013-10-29'
        GROUP BY eveDate, c.msisdn 
    ) x
    GROUP BY x.msisdn
    ORDER BY SUM(datasum)
    LIMIT 100;
    

    或类似的东西。

    第二季度

    SELECT c.msisdn SUM(c.dataVolumeDownLink+c.dataVolumeUpLink) AS datasum 
    FROM cdr c 
    WHERE c.eveDate>='2013-10-29'
    GROUP BY c.msisdn 
    ORDER BY 100;
    

    上面的查询比较简单,但是相同的msisdn 可以位于另一个eveDate。所以从INDEX(eveDate, msisdn) 中获益一点。如果您的磁盘有很大的可用空间,则执行 INDEX 只会执行 INDEX 扫描。不需要数据。所有必需的都在索引中

    INDEX(eveDate, msisdn, dataVolumeDownLink, dataVolumeUpLink)
    

    更新

    hmm,如果数据只是追加,并且追加的数据永远不会改变。我想知道是否每天都做一个汇总表。

    CREATE TABLE summary(eveDate, msisdn, datasum, INDEX(eveDate, msisdn);
    

    每晚通过 cronjob 运行以下查询

    INSERT INTO summary
    SELECT NOW() c.msisdn,SUM(c.dataVolumeDownLink+c.dataVolumeUpLink) AS datasum 
    FROM cdr c 
    WHERE c.eveDate = NOW()
    GROUP BY c.msisdn 
    

    那么您的查询将非常简单。

    SELECT msisdn, SUM(datasum) as datasum
    FROM summary
    WHERE eveDate BETWEEN ? AND ?
    

    【讨论】:

    • 我不能在这个表上添加更多的索引,因为插入率太多了。而且我也有其他查询,所以我不能为每个查询添加索引。但是如果有什么方法可以帮助我处理比较条件,因为当我要使用一个月前的日期时,这个查询(我也有其他查询)会花费很多时间。谢谢。
    • 什么是 - 在第一个查询中 -> ORDER BY SUM(datasum)?因为 datasum 已经是一个汇总列。在第二个查询中 -> ORDER BY 100??
    • @Aamir0731 Q1 的内部查询是“GROUP BY eveDate, msisdn”或需要 SUM() 外部 SELECT。这假设有 INDEX(eveDate, msisdn)
    • 表示不添加索引就无法让这个查询更快:(好的,让我根据查询添加复合索引并检查输出。
    • @Aamir0731 只是我的个人意见。我希望有人可以帮助你。
    【解决方案3】:
    SELECT c.msisdn,SUM(c.dataVolumeDownLink+c.dataVolumeUpLink) AS datasum 
    FROM cdr c 
    WHERE c.eveDate>='2013-10-29'
    GROUP BY c.msisdn 
    ORDER BY datasum DESC
    LIMIT 0, 100;
    

    【讨论】:

      【解决方案4】:
      SELECT c.msisdn,SUM(c.dataVolumeDownLink+c.dataVolumeUpLink) AS datasum 
      FROM 
      (select * from cdr where eveDate>='2013-10-29' limit 100) as c 
      
      GROUP BY c.msisdn 
      ORDER BY datasum DESC;
      

      与拉里的回答略有不同
      不完全确定我是否正确理解了这个问题
      这将首先获取前 100 条记录并对其进行计算。 所以最终结果可能少于 100 行,基于 group by 子句

      编辑:
      根据您的说明,您需要在 c.msisdn 上添加一个索引并在末尾添加一个限制子句
      删除 order by 子句并放置一个外部查询只是为了让记录按顺序排序

      SELECT a.* FROM (
      SELECT c.msisdn,SUM(c.dataVolumeDownLink+c.dataVolumeUpLink) AS datasum 
      FROM cdr c 
      WHERE c.eveDate>='2013-10-29'
      GROUP BY c.msisdn limit 100) a 
      ORDER BY a.datasum DESC;
      

      在 c.msisdn 上添加索引

      【讨论】:

      • 不,我不想对前100个进行操作,否则输出会出错。如果有什么方法可以从查询中(在我的问题中)首先选择前 100 条(从所有记录中)记录,请告诉我。
      猜你喜欢
      • 2015-01-18
      • 2016-07-09
      • 1970-01-01
      • 1970-01-01
      • 2015-09-09
      • 2017-01-17
      • 2020-11-23
      • 2015-02-19
      • 2014-12-11
      相关资源
      最近更新 更多