我有以下疑问:
SELECT
match_main.checked,
match_main.match_main_id,
match_main.updated,
match_main.created
FROM
match_main
LEFT JOIN
match_team AS mt1 ON mt1.match_main_id = match_main.match_main_id
AND mt1.team_number = 1
AND mt1.version_number = 0
LEFT JOIN
match_team AS mt2 ON mt2.match_main_id = match_main.match_main_id
AND mt2.team_number = 2
AND mt2.version_number = 0
WHERE
mt1.team_id = 557949
OR mt2.team_id = 557949
其中
match_main
是体育比赛表,match_team
是参加特定体育比赛的球队表。
match_main
包含大约 5m 条记录,match_team
包含大约 10m 条记录。
上面的查询需要 5 分钟以上才能返回大约 1700 条记录,我不知道如何进一步优化它。
这是
EXPLAIN
查询输出:
+----+-------------+------------+------------+--------+---------------------------------------------------------------------------------------------------------------------+------------------------------------------+---------+------------------------------------------+---------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+--------+---------------------------------------------------------------------------------------------------------------------+------------------------------------------+---------+------------------------------------------+---------+----------+-------------+
| 1 | SIMPLE | match_main | NULL | ALL | NULL | NULL | NULL | NULL | 4756279 | 100 | NULL |
| 1 | SIMPLE | mt1 | NULL | eq_ref | uq__match_team__match__team_id__version,uq__match_team__match__team_num__version,ix__match_team__match_main_id,comp | uq__match_team__match__team_num__version | 6 | itf.match_main.match_main_id,const,const | 1 | 100 | NULL |
| 1 | SIMPLE | mt2 | NULL | eq_ref | uq__match_team__match__team_id__version,uq__match_team__match__team_num__version,ix__match_team__match_main_id,comp | uq__match_team__match__team_num__version | 6 | itf.match_main.match_main_id,const,const | 1 | 100 | Using where |
+----+-------------+------------+------------+--------+---------------------------------------------------------------------------------------------------------------------+------------------------------------------+---------+------------------------------------------+---------+----------+-------------+
以下是
SHOW CREATE TABLE
输出:
CREATE TABLE `match_main` (
`match_main_id` int NOT NULL AUTO_INCREMENT,
`checked` datetime NOT NULL,
`updated` timestamp NOT NULL,
`created` timestamp NOT NULL,
PRIMARY KEY (`match_main_id`)
) ENGINE=InnoDB AUTO_INCREMENT=2121471809 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
CREATE TABLE `match_team` (
`match_team_id` int NOT NULL AUTO_INCREMENT,
`match_main_id` int NOT NULL,
`team_id` int NOT NULL,
`team_number` tinyint NOT NULL,
`version_number` tinyint NOT NULL,
`updated` timestamp NOT NULL,
`created` timestamp NOT NULL,
PRIMARY KEY (`match_team_id`),
UNIQUE KEY `uq__match_team__match__team_id__version` (`match_main_id`,`team_id`,`version_number`),
UNIQUE KEY `uq__match_team__match__team_num__version` (`match_main_id`,`team_number`,`version_number`),
KEY `ix__match_team__team_id` (`team_id`),
KEY `ix__match_team__match_main_id` (`match_main_id`),
CONSTRAINT `fk__match_team__match_main_id` FOREIGN KEY (`match_main_id`) REFERENCES `match_main` (`match_main_id`),
CONSTRAINT `fk__match_team__team_id` FOREIGN KEY (`team_id`) REFERENCES `team` (`team_id`)
) ENGINE=InnoDB AUTO_INCREMENT=9297542 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
我需要保留两个别名的
match_team
表,因为当我扩展此查询时,我需要将这些表链接到 team
表。这样我就可以在单行上返回一场比赛的输出,其中包含有关参加比赛的所有单独球队的详细信息的列。我排除了这一点以保持 MRE 简单。
在唯一键之一中重新定义列顺序:
ALTER TABLE match_team
DROP KEY `uq__match_team__match__team_num__version`,
ADD UNIQUE KEY `uq__match_team__team_num__version__match` (`team_number`,`version_number`,`match_main_id`);
然后尝试这个查询:
SELECT m.checked, m.match_main_id, m.updated, m.created
FROM match_team AS mt1
INNER JOIN match_team AS mt2
ON mt1.match_main_id = mt2.match_main_id
INNER JOIN match_main AS m
ON m.match_main_id = mt1.match_main_id
WHERE 557949 IN (mt1.team_id, mt2.team_id)
AND mt1.team_number = 1
AND mt1.version_number = 0
AND mt2.team_number = 2
AND mt2.version_number = 0;
它应该对
uq__match_team__team_num__version__match
和 mt1
使用新的 mt2
索引,然后通过其主键连接到 match_main
。这消除了 match_main
上 400 万行的表扫描。
这是我测试时得到的解释报告:
*************************** 1. row ***************************
id: 1
select_type: SIMPLE
table: mt1
partitions: NULL
type: ref
possible_keys: uq__match_team__match__team_id__version,uq__match_team__team_num__version__match,ix__match_team__match_main_id
key: uq__match_team__team_num__version__match
key_len: 2
ref: const,const
rows: 1
filtered: 100.00
Extra: NULL
*************************** 2. row ***************************
id: 1
select_type: SIMPLE
table: mt2
partitions: NULL
type: eq_ref
possible_keys: uq__match_team__match__team_id__version,uq__match_team__team_num__version__match,ix__match_team__match_main_id
key: uq__match_team__team_num__version__match
key_len: 6
ref: const,const,test.mt1.match_main_id
rows: 1
filtered: 100.00
Extra: Using where
*************************** 3. row ***************************
id: 1
select_type: SIMPLE
table: m
partitions: NULL
type: eq_ref
possible_keys: PRIMARY
key: PRIMARY
key_len: 4
ref: test.mt1.match_main_id
rows: 1
filtered: 100.00
Extra: NULL
第二个JOIN的目的是什么?你可以尝试只用一个:
SELECT
mm.checked,
mm.match_main_id,
mm.updated,
mm.created
FROM match_team AS mt
JOIN match_main AS mm ON mm.match_main_id = mt.match_main_id
WHERE mt.team_id = 557949 AND mt.version_number = 0
AND mt.team_number in (1,2);
此外,当您将条件移至
LEFT JOIN
子句时,JOIN
会变为正常 WHERE
。