如何快速计算大表?

时间:2010-10-05 20:26:22

标签: sql mysql query-optimization aggregate-functions

我有大量MySQL表,有数十万行。

我需要在客户表上写一个查询,该表可以了解客户何时可以再次联系。

例如

SELECT 'This week', COUNT(*) FROM customers 
WHERE sales_person_id = 1 AND DATEDIFF(NOW(), available_date) < 7

UNION

SELECT 'Next week', COUNT(*) FROM customers 
WHERE sales_person_id = 1 AND DATEDIFF(NOW(), available_date) >= 7 
    AND DATEDIFF(NOW(), available_date) < 14

UNION

... (a few more like this)

在不同的大表上编写了类似的查询后,我注意到将引擎从InnoDB更改为MyISAM大大加快了查询速度(这些表不需要InnoDB,因为它们没有外键检查)。还有什么我可以做的事情来加速这样的计数(除了索引相应的字段)?

3 个答案:

答案 0 :(得分:5)

WHERE sales_person_id = 1 AND available_date BETWEEN CURDATE() - INTERVAL 1 WEEK AND CURDATE()

这样做应该让MySQL使用在(sales_person_id, available_date)列上创建的复合索引(使用EXPLAIN来检查)

答案 1 :(得分:3)

  1. 永远不要在多个查询中,你可以在一个查询中做什么。

    如果您创建一个具有必要开始和放大的派生表/内联视图结束日期,这可用于使用GROUP BY在单个查询中生成所需结果。 MySQL没有递归函数,因此你必须使用NUMBERS表技巧来生成日期......

    1. 创建一个只保存递增数字的表 - 使用auto_increment很容易做到:

      DROP TABLE IF EXISTS `example`.`numbers`;
      CREATE TABLE  `example`.`numbers` (
       `id` int(10) unsigned NOT NULL auto_increment,
        PRIMARY KEY  (`id`)
      ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
      
    2. 使用以下方法填充表格:

      INSERT INTO NUMBERS (id)
      VALUES (NULL)
      

      ...根据需要提供尽可能多的价值。

    3. 使用DATE_ADD构建日期列表,根据NUMBERS.id值增加日期。

      SELECT x.start_dt,
             x.end_dt
        FROM (SELECT DATE_ADD(NOW(), INTERVAL n.id - 1 DAY) AS start_dt,
                     DATE_ADD(NOW(), INTERVAL n.id + 6 DAY) AS end_dt
                FROM `numbers` n
               WHERE DATE_ADD(NOW(), INTERVAL (n.id - 1) DAY) <= '2011-01-01') x
      
    4. 根据日期时间部分加入您的数据表:

        SELECT x.start_dt,
               x.end_dt,
               COUNT(*) AS num
          FROM (SELECT DATE_ADD(NOW(), INTERVAL n.id - 1 DAY) AS start_dt,
                       DATE_ADD(NOW(), INTERVAL n.id + 6 DAY) AS end_dt
                  FROM `numbers` n
                 WHERE DATE_ADD(NOW(), INTERVAL (n.id - 1) DAY) <= '2011-01-01') x
          JOIN CUSTOMERS c ON c.available_date BETWEEN x.start_dt
                                                   AND x.end_dt
      GROUP BY x.start_dt, x.end_dt
      
  2. 不要使用在实际列数据上执行的函数 - IE:DATEDIFF(NOW(), *available_date*) - 因为数据库不能使用available_date列上的索引(如果存在),因为数据已经改变了指数值。

答案 2 :(得分:1)

专注于WHERE子句。

  • WHERE子句中的字段是否有索引?
  • 你可以用一个常量替换datediff()函数,它正在为每一行进行评估。