Mysql

MySQL內部連接太慢

  • December 25, 2019

我的機器盤是SSD,記憶體是32GB。

我執行的 SQL:

SELECT `country_detail`.`country_name` AS `country_name`
FROM `statistic_detail`
 INNER JOIN `country_detail` ON (`statistic_detail`.`country` = `country_detail`.`country_code`)
GROUP BY 1;

SQL 需要花費 30 秒。這麼慢!我的解釋是: 在此處輸入圖像描述 我不明白為什麼需要這麼多時間?

我知道如何優化它,我想知道它為什麼這麼慢。

我的表資訊是:

CREATE TABLE `statistic_detail` (
 `date` varchar(10) NOT NULL,
 `os` varchar(10) NOT NULL,
 `ver` varchar(16) NOT NULL,
 `country` varchar(16) NOT NULL,
 `utype` varchar(8) NOT NULL,
 `stype` varchar(32) NOT NULL,
 `language` varchar(16) NOT NULL,
 `num0` bigint DEFAULT NULL,
 `num1` double DEFAULT NULL,
 `num2` double DEFAULT NULL,
 `num3` double DEFAULT NULL,
 `num4` double DEFAULT NULL,
 `num5` double DEFAULT NULL,
 `num6` double DEFAULT NULL,
 `num7` double DEFAULT NULL,
 `num8` double DEFAULT NULL,
 `num9` double DEFAULT NULL,
 `num10` double DEFAULT NULL,
 `num11` double DEFAULT NULL,
 `num12` double DEFAULT NULL,
 `num13` double NOT NULL,
 PRIMARY KEY (`country`, `ver`, `language`, `utype`, `stype`, `os`, `date`),  
 KEY `ver_idx` (`ver`),
 KEY `language_idx` (`language`),
 KEY `utype_idx` (`utype`),
 KEY `stype_idx` (`stype`),
 KEY `os_idx` (`os`),
 KEY `date_idx` (`date`),
 KEY `country_idx` (`country`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

13731253 行。

CREATE TABLE `country_detail` (
 `country_code` varchar(20) NOT NULL,
 `country_name` varchar(16),
 `region_code` varchar(16),
 `region_name` varchar(16),
 PRIMARY KEY (`country_code`),
 KEY `region_code_idx` (`region_code`),
 KEY `region_name_idx` (`region_name`),
 KEY `country_name_idx` (`country_name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

334 行。

要了解目前查詢緩慢的原因,您需要了解MySQL 使用的Nested Loop Join 算法。基本上,對於country_codecountry 表中的每個值,它都會在統計表中查找所有匹配的行。所以如果你看你的解釋,它大致是訪問 334*306 行(一個很大的數字)。不幸的是,在這種情況下,MySQL 優化器不夠聰明,無法在找到匹配項後立即停止搜尋行(因為您正在使用Group By,並且沒有使用聚合函式)。

現在,JOIN這裡沒有必要,因為您似乎只是想檢查“國家”在statistic_detail表格中是否至少有一個相應的行。可以使用Correlated Subquery以更高效的方式編寫此查詢。一旦在統計表中找到匹配的行,就會停止。EXISTS()Exists()

嘗試以下查詢:

SELECT cd.country_name
FROM country_detail AS cd 
WHERE EXISTS (SELECT 1 FROM statistic_detail AS sd 
             WHERE sd.country = cd.country_code)

如果表country_name中的值不是唯一的country_detail,您可以另外使用DISTINCT子句來消除重複項:

SELECT DISTINCT cd.country_name
FROM country_detail AS cd 
WHERE EXISTS (SELECT 1 FROM statistic_detail AS sd 
             WHERE sd.country = cd.country_code)

引用自:https://dba.stackexchange.com/questions/247164