【问题标题】:mysql query speed optimizationmysql查询速度优化
【发布时间】:2010-12-07 18:18:32
【问题描述】:

我有以下内容,当我自己运行时非常快,但是当我为许多 entity_id 执行此操作时,查询开始花费越来越长的时间(循环是 PHP foreach),例如此查询只需要0.078 但对循环内不同实体的相同查询最多需要 2.1 秒,我放入循环中的实体越多,查询似乎变得越来越慢。为什么是这样?以及如何改进/优化查询?

foreach($entity_ids as $entity_id) {
    SELECT COUNT(*) as prev, DATE_FORMAT(`created`, '%Y%m%d') AS date_group 
    FROM articles_entities 
    WHERE entity_id = '$entity_id' 
    AND `created` >= DATE_SUB(CURDATE(), INTERVAL 10 DAY) 
    GROUP BY date_group

    // store result
}

我的表结构如下:

CREATE TABLE `articles_entities` (
  `id` CHAR(36) NOT NULL,
  `article_id` CHAR(36) NOT NULL,
  `entity_id` CHAR(36) NOT NULL,
  `created` DATETIME DEFAULT NULL,
  `modified` DATETIME DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `article_id` (`article_id`),
  KEY `entity_id` (`entity_id`),
  KEY `created` (`created`)
) ENGINE=MYISAM DEFAULT CHARSET=utf8;

【问题讨论】:

    标签: php mysql optimization


    【解决方案1】:

    在我看来,您有一个 ID 数组,然后想要从您的表中提取记录(以您的语句控制的方式),其中 ID 字段与其中一个数组值匹配。

    与其在 PHP 中使用 LOOP 来运行多个 SQL 语句,最好的办法是构建一个主语句,然后使用 PHP 来处理结果。您可以使用 SQL IN 语句完成此操作:

        // where $entity_ids is an array eg 1,2,3,4,5
    
        $sql="SELECT entity_id AS 'alt_entity_id', COUNT(entity_id) as prev, DATE_FORMAT(`created`, '%Y%m%d') AS 'date_group'  
        FROM articles_entities 
        WHERE entity_id IN ".implode(",",$entity_ids)." 
        AND `created` >= DATE_SUB(CURDATE(), INTERVAL 10 DAY) 
        GROUP BY date_group, entity_id";
        // you may wish to revese the group fields, as you require, you may also wish to change the count field to date_group, depending on what you wish to be counted
    

    这将为您拥有的所有 id 值运行一次原始查询,按日期和传递的 id 值分组。然后,您可以使用 PHP 从返回的结果集中过滤掉特定 id 的结果。

    这比循环执行查询所产生的开销要高效得多。

    您返回的结果集将类似于:

     entity_id | count(entity_id) | date_group
     ----------|------------------|------------
         1     |        3         | 2010-04-01
         1     |        3         | 2010-03-01
         1     |        3         | 2010-02-01
         2     |        2         | 2010-01-01
         2     |        2         | 2010-02-01
         3     |        1         | 2010-06-01
         4     |        2         | 2010-06-01
         4     |        2         | 2010-02-01
    

    【讨论】:

    • 答案中唯一缺少的是选择,entity_id AS alt_entity_id,因为我可以计算出每个实体是哪个实体(注意我不能只选择 entity_id,因为它给出了不正确的结果。(我还必须交换分组结束。如果你更新你的答案,我会接受它。
    【解决方案2】:

    我不知道您从哪里获取循环的实体值,但在循环中执行此查询总是会产生很大的性能开销。如果您从之前的 SQL 查询中获取 entity_ids,那么重构 SQL 以将初始查询与循环查询连接起来可能更有意义,这样您就可以在单个 SQL 查询中返回所需的所有数据。

    【讨论】:

    • $entity_ids 来自哪里?
    • 我首先从同一个表中选择了符合特定条件(不同日期范围)的文章,并返回了它的实体
    • @Lizard - 我的意思是应该可以将第一个选择与循环选择结合起来:如果您发布两个查询(以及任何其他相关的表结构),我们也许可以制定一个单个查询,这意味着您不必在循环中执行一系列查询
    【解决方案3】:

    您大约处理多少个实体?

    您能否将所需的实体插入到单独的表中并进行连接,而不是进行多个查询?

    【讨论】:

      【解决方案4】:

      将所有 id 放入一个数组中,将其连接成一个字符串,并使用“where in”以优化的方式获取详细信息

      $enitityIDS = array();
          foreach($entity_ids as $entity_id) {
             $enitityIDS[]=$entity_id;
          }
          $entityIDString = join(",",$enitityIDS);
      

      然后做

      SELECT COUNT(*) as prev, DATE_FORMAT(`created`, '%Y%m%d') AS date_group 
          FROM articles_entities 
          WHERE entity_id in (".$entityIDString.")
          AND `created` >= DATE_SUB(CURDATE(), INTERVAL 10 DAY) 
          GROUP BY date_group, entity_id
      

      最佳方式

      【讨论】:

      • 别忘了还需要按id字段对查询进行分组,否则IN子句的结果是无法区分的
      猜你喜欢
      • 2019-08-30
      • 1970-01-01
      • 1970-01-01
      • 2022-11-11
      • 2012-06-30
      • 1970-01-01
      • 2019-04-22
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多