Mysql
MySQL內部連接太慢
我的機器盤是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_code
country 表中的每個值,它都會在統計表中查找所有匹配的行。所以如果你看你的解釋,它大致是訪問 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)