查询耗时太长,同时将其分为两个查询,耗时0.2秒

时间:2017-09-28 13:29:59

标签: mysql performance join

我有当前的查询:

select m.id, ms.severity, ms.risk_score, count(distinct si.id), boarding_date_tbl.boarding_date
from merchant m
join merchant_has_scan ms on m.last_scan_completed_id = ms.id
join scan_item si on si.merchant_has_scan_id = ms.id and si.is_registered = true
join (select m.id merchant_id, min(s_for_boarding.scan_date) boarding_date
    from merchant m
    left join merchant_has_scan ms on m.id = ms.merchant_id
    left join scan s_for_boarding on s_for_boarding.id = ms.scan_id and s_for_boarding.scan_type = 1
    group by m.id) boarding_date_tbl on boarding_date_tbl.merchant_id = m.id
group by m.id
limit 100;

当我在大计划(约2mil"商家")上运行时,它需要超过20秒。 但如果我把它分成:

select m.legal_name, m.unique_id, m.merchant_status, s_for_boarding.scan_date
from merchant m
join merchant_has_scan ms on m.id = ms.merchant_id
join scan s_for_boarding on s_for_boarding.id = ms.scan_id and s_for_boarding.scan_type = 1
group by m.id
limit 100;

select m.id, ms.severity, ms.risk_score, count(distinct si.id)
from merchant m
join merchant_has_scan ms on m.last_scan_completed_id = ms.id
join scan_item si on si.merchant_has_scan_id = ms.id and si.is_registered = true

group by m.id
limit 100;

两者都需要大约0.1秒 原因很明显,下限意味着它不需要做很多事情来获得前100个。很明显内部选择导致第一个查询运行的次数与它一样多。 我的问题是,有一种方法可以仅在相关商家上进行内部选择,而不是在整个桌子上进行内部选择吗?

更新

在内部查询之前制作left join而不是join有助于将其缩短为6秒,但如果我进行2次查询,它仍会比我获得的要多得多

更新2

为商家创建表格:

CREATE TABLE `merchant` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `last_scan_completed_id` bigint(20) DEFAULT NULL,
  `last_updated` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  CONSTRAINT `FK_9lhkm7tb4bt87qy4j3fjayec5` FOREIGN KEY (`last_scan_completed_id`) REFERENCES `merchant_has_scan` (`id`)
)

merchant_has_scan:

CREATE TABLE `merchant_has_scan` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `merchant_id` bigint(20) NOT NULL,
  `risk_score` int(11) DEFAULT NULL,
  `scan_id` bigint(20) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `unique_merchant_id` (`scan_id`,`merchant_id`),
  CONSTRAINT `FK_3d8f81ts5wj2u99ddhinfc1jp` FOREIGN KEY (`scan_id`) REFERENCES `scan` (`id`),
  CONSTRAINT `FK_e7fhioqt9b9rp9uhvcjnk31qe` FOREIGN KEY (`merchant_id`) REFERENCES `merchant` (`id`)
)

scan_item:

CREATE TABLE `scan_item` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `is_registered` bit(1) NOT NULL,
  `merchant_has_scan_id` bigint(20) NOT NULL,
 PRIMARY KEY (`id`),
  CONSTRAINT `FK_avcc5q3hkehgreivwhoc5h7rb` FOREIGN KEY (`merchant_has_scan_id`) REFERENCES `merchant_has_scan` (`id`)
)

扫描:

CREATE TABLE `scan` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `scan_date` datetime DEFAULT NULL,
  `scan_type` int(11) NOT NULL,
  PRIMARY KEY (`id`)
)

和解释:

enter image description here

1 个答案:

答案 0 :(得分:0)

  • 您没有最新版本的MySQL,它可以为派生表创建索引。 (你正在运行什么版本?)
  • "派生表" (子查询)将是EXPLAIN中的第一个表,因为它必须是。
  • merchant_has_scan是一个很多:很多表,但没有优化提示here - 修复这可能是加速它的最大因素。警告:提示建议删除id,但您似乎可以使用id,所以请保留它。
  • COUNT(DISTINCT si.id)JOIN si...可以替换为( SELECT COUNT(*) FROM scan_item WHERE ...),从而消除其中一个JOINs并可能会减少Explode-Implode
  • LEFT JOIN - 您有时希望NULL获得boarding_date吗?如果没有,请使用JOIN,而不是LEFT JOIN。 (最好说出你的意图,而不是让查询保持多种解释。)
  • 如果您可以删除LEFTs,那么由于m.idmerchant_id被指定为相同,为什么要在SELECT中列出它们? (这是一个混乱的因素,而不是速度问题)。
  • 你说你把它分成了两个 - 但你没有。您将其拉出时将LIMIT 100添加到内部查询中。如果需要,也可以将它添加到派生表中。然后,可以从外部查询中删除GROUP BY m.id LIMIT 100