我有以下查询
SELECT SUBSTRING(a0_.created_date FROM 1 FOR 10) AS sclr_0,
COUNT(1) AS sclr_1
FROM applications a0_ INNER JOIN
package_codes p1_ ON a0_.id = p1_.application_id
WHERE a0_.created_date BETWEEN '2019-01-01' AND '2020-01-01' AND
p1_.type = 'Package 1'
GROUP BY sclr_0
---编辑---
你们中的大多数都集中在GROUP BY和串,但这不是问题的根源。
下面的查询具有相同的执行时间:
SELECT COUNT(1) AS sclr_1
FROM applications a0_ INNER JOIN
package_codes p1_ ON a0_.id = p1_.application_id
WHERE a0_.created_date BETWEEN '2019-01-01' AND '2020-01-01' AND
p1_.type = 'Package 1'
---编辑2 ---
在applications.created_date添加索引,并迫使查询中使用指定的指标作为@DDS建议的执行时间之后下降到〜750ms之间
目前查询是这样的:
SELECT SUBSTRING(a0_.created_date FROM 1 FOR 10) AS sclr_0,
COUNT(1) AS sclr_1
FROM applications a0_ USE INDEX (applications_created_date_idx) INNER JOIN
package_codes p1_ USE INDEX (PRIMARY, UNIQ_70A9C6AA3E030ACD, package_codes_type_idx) ON a0_.id = p1_.application_id
WHERE a0_.created_date BETWEEN '2019-01-01' AND '2020-01-01' AND
p1_.type = 'Package 1'
GROUP BY sclr_0
---编辑3 ---
我发现,在查询中使用到多指标可能会导致在某些情况下,MySQL将使用非最优的索引,所以最终的查询应该看起来如下:
SELECT SUBSTRING(a0_.created_date FROM 1 FOR 10) AS sclr_0,
COUNT(1) AS sclr_1
FROM applications a0_ USE INDEX (applications_created_date_idx) INNER JOIN
package_codes p1_ USE INDEX (package_codes_application_idx) ON a0_.id = p1_.application_id
WHERE a0_.created_date BETWEEN '2019-01-01' AND '2020-01-01' AND
p1_.type = 'Package 1'
GROUP BY sclr_0
---编辑完---
package_codes包含超过100.000.000记录。
应用程序包含了超过250.000记录。
查询需要2分钟就搞定了结果。有什么办法优化它?我卡上的MySQL 5.5。
表:
CREATE TABLE `applications` (
`id` int(11) NOT NULL,
`created_date` datetime NOT NULL,
`name` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL,
`surname` varchar(64) COLLATE utf8mb4_unicode_ci NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `applications`
ADD PRIMARY KEY (`id`),
ADD KEY `applications_created_date_idx` (`created_date`);
ALTER TABLE `applications`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
CREATE TABLE `package_codes` (
`id` int(11) NOT NULL,
`application_id` int(11) DEFAULT NULL,
`created_date` datetime NOT NULL,
`type` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`code` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL,
`disabled` tinyint(1) NOT NULL DEFAULT '0',
`meta_data` longtext COLLATE utf8mb4_unicode_ci
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
ALTER TABLE `package_codes`
ADD PRIMARY KEY (`id`),
ADD UNIQUE KEY `UNIQ_70A9C6AA3E030ACD` (`application_id`),
ADD KEY `package_codes_code_idx` (`code`),
ADD KEY `package_codes_type_idx` (`type`),
ADD KEY `package_codes_application_idx` (`application_id`),
ADD KEY `package_codes_code_application_idx` (`code`,`application_id`);
ALTER TABLE `package_codes`
MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;
ALTER TABLE `package_codes`
ADD CONSTRAINT `FK_70A9C6AA3E030ACD` FOREIGN KEY (`application_id`) REFERENCES `applications` (`id`);
我的建议是为了避免这样的:
SELECT SUBSTRING(a0_.created_date FROM 1 FOR 10) AS sclr_0,
[...]
GROUP BY sclr_0
因为每一次的DBMS“重新计算”领域,它不能使用索引,如果你把这个数据在它自己的专栏,并在其上的性能指标应提高
或者,至少,使用date_part数函数,因此MySQL可以设法利用其索引(显然你应该添加在application.created_date索引)
SELECT SUBSTRING(a0_.created_date FROM 1 FOR 10) AS sclr_0, COUNT(1) AS sclr_1
FROM applications a0_ INNER JOIN
package_codes p1_ ON (a0_.id = p1_.application_id and a0_.created_date
BETWEEN '2019-01-01' AND '2020-01-01' and p1_.type = 'Package 1')
FORCE INDEX (date_index, type_index)
Group by date(a0_.created_date)
另一优化是“推”的条件在“接通”子句因此MySQL“过滤器”接合前的数据 - >加入跨少得多的行执行
编辑:这是上的日期创建索引
CREATE INDEX date_index ON application(created_date);
如果您有更多的类型比日期,你应该考虑把指数型。
CREATE INDEX type_index ON package_codes(type);
[EDIT 2]请后的结果
select count(distinct date(a0_.created_date)) as N_DATES, count(distinct type)as N_TYPES
FROM applications a0_ INNER JOIN
package_codes p1_ ON a0_.id = p1_.application_id
只是有一个想法上女巫指数将更具选择性
有用link使用MySQL索引的优化
在applications.created_date添加索引,并迫使查询中使用指定的指标作为@DDS建议的执行时间之后下降到〜750ms之间
最后的查询看起来应该如下:
SELECT SUBSTRING(a0_.created_date FROM 1 FOR 10) AS sclr_0,
COUNT(1) AS sclr_1
FROM applications a0_ USE INDEX (applications_created_date_idx) INNER JOIN
package_codes p1_ USE INDEX (package_codes_application_idx) ON a0_.id = p1_.application_id
WHERE a0_.created_date BETWEEN '2019-01-01' AND '2020-01-01' AND
p1_.type = 'Package 1'
GROUP BY sclr_0
您需要创建一个综合指数。看来,你已经在表上创建单独的索引。在这种情况下,你想在package_codes上CREATED_DATE一个单独的索引,也为CREATED_DATE和类型的复合指数。
也许通过后投之前的日期和组。
最佳指标是
p1_: (type, application_id)
a0_: (created_date, id)
这适用于所有(?)介绍,除了那些“逼”的索引查询的版本。
优化器将尝试以决定是否开始p1_
或a0_
。而且,这些指标,就应该在挑选更好的表有一个好球。
SUBSTRING(a0_.created_date FROM 1 FOR 10)
可以简化为DATE(a0_.created_date)
,但我怀疑它是否能够改变性能。
请注意,索引将被“覆盖”,从而给人一种额外的动力。 EXPLAIN
表示这样说Using index
(不Using index condition
)。
进一步的改进:摆脱package_codes.id
,推动application_id
是PRIMARY KEY
。这可能导致查询的简化!
我的建议适用于(也许)的MySQL的所有版本。