MySQL子查询计数与日历表慢

时间:2017-04-26 10:07:23

标签: mysql performance count innodb

我在MySQL(InnoDB)中有一个销售表。它的记录超过100万。我想展示一些不错的图表。获取正确的数据不是问题。快速获取它是......

所以我喜欢计算表A中每日分组的销售额(后来也是月份和年份),直到Z. Concrete;在过去的30天里,我每天都想知道我们在数据库中有多少销售记录。

所以MySQL必须返回这样的东西:

我喜欢实现MySQL返回这样的数据:

date, count
2017-04-01, 2482
2017-04-02, 1934
2017-04-03, 2701
...

销售的结构基本上是这样的:

CREATE TABLE `sales` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00',
  `deleted_at` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `contacts_created_at_index` (`created_at`),
  KEY `contacts_deleted_at_index` (`deleted_at`),
  KEY `ind_created_at_deleted_at` (`created_at`,`deleted_at`),
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

有些日子(数据点)可能没有任何结果,但我不想在数据方面存在差距。所以我也有一些日历'表

CREATE TABLE `time_dimension` (
  `id` int(11) NOT NULL,
  `db_date` date NOT NULL,
  `year` int(11) NOT NULL,
  `month` int(11) NOT NULL,
  `day` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `td_ymd_idx` (`year`,`month`,`day`),
  UNIQUE KEY `td_dbdate_idx` (`db_date`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;

每天计数30行(30天)需要30秒......

这是我尝试的第一个查询:

SELECT 
    `db_date` AS `date`,
    (SELECT 
            COUNT(1)
        FROM
            sales
        WHERE
            DATE(created_at) = db_date) AS count
FROM
    `time_dimension`
WHERE
    `db_date` >= '2017-04-11'
        AND `db_date` <= '2017-04-25'
ORDER BY `db_date` ASC

但就像我说它真的很慢(11.9秒)。我尝试了其他各种方法,但没有运气。例如:

SELECT time_dimension.db_date AS DATE,
       COUNT(1) AS count
FROM sales RIGHT JOIN time_dimension ON (DATE(sales.created_at) =         
    time_dimension.db_date)
WHERE
    (time_dimension.db_date BETWEEN '2017-03-11' AND '2017-04-11')
GROUP BY
    DATE

仅1个数据点的查询只需5.4ms:

SELECT COUNT(1) FROM sales WHERE created_at BETWEEN '2017-04-11 00:00:00' AND '2017-04-25 23:59:59'

我还没有在我的本地机器上检查过innodb_buffer_poolsize。我也会检查一下。有关如何快速进行此类查询的任何想法?将来我甚至需要where子句和连接来过滤销售记录集。

感谢。

尼克

3 个答案:

答案 0 :(得分:0)

您可以先尝试计算销售数据,然后将计数结果与日历表结合起来。

rrc3h = h2o.H2OFrame(rrc3)

答案 1 :(得分:0)

查询中有问题的部分是数据类型转换DATE(created_at),这有效地阻止了Mysql在created_at使用索引。

您的1 datapoint查询可以避免这种情况,这就是它快速运作的原因。

要解决此问题,您应该检查created_at是否在特定日期的范围内,例如:

created_at BETWEEN db_date AND DATE_ADD(db_date,INTERVAL 1 DAY)

这样Mysql就可以根据需要使用索引(进行范围查找)。

答案 2 :(得分:0)

 WHERE DATE(created_at) = db_date)

- &GT;

 WHERE created_at >= db_date
   AND created_at  < db_date + INTERVAL 1 DAY
  • 这可以避免包括第二天的午夜(BETWEEN确实如此)
  • 适用于所有口味:DATEDATETIMEDATETIME(6)
  • 不会将created_at隐藏在索引无法看到的函数内。

对于time_dimension,请删除PRIMARY KEY (id)并将UNIQUE(db_date)更改为PK。

进行这些更改后,您的原始子查询可能与LEFT JOIN ( SELECT ... )具有竞争力。 (这取决于MySQL的版本。)