I have the following query:
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
Where match_main
is a table of sports matches and match_team
is a table of the teams that played in a given sports match.
match_main
contains around 5m records and match_team
contains around 10m records.
The query above takes over 5 mins to return about 1700 records and I can’t figure out how to optimise it further.
This is the EXPLAIN
query output:
+----+-------------+------------+------------+--------+---------------------------------------------------------------------------------------------------------------------+------------------------------------------+---------+------------------------------------------+---------+----------+-------------+
| 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 |
+----+-------------+------------+------------+--------+---------------------------------------------------------------------------------------------------------------------+------------------------------------------+---------+------------------------------------------+---------+----------+-------------+
Here are the SHOW CREATE TABLE
outputs:
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
I need to retain the two aliased match_team
tables as when I expand this query I’ll need to link these tables to the team
tables. This is so I can return an output of a match on a single row with columns for details on all the individual teams that played in a match. I’ve excluded this to keep the MRE simple.
2
Answers
Redefine the column order in one of the unique keys:
Then try this query:
It should use the new
uq__match_team__team_num__version__match
index for bothmt1
andmt2
, then join tomatch_main
by its primary key. This eliminates the table-scan of 4 million rows onmatch_main
.Here’s the EXPLAIN report I got when I tested:
What is the purpose of the second JOIN? You could try with just one:
Also, the
LEFT JOIN
becomes a normalJOIN
when you move the condition to theWHERE
-clause.See a dbfiddle