使用同一个表中的 2x LEFT JOIN 优化查询

问题描述 投票:0回答:2

我有以下疑问:

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 简单。

mysql query-optimization mysql-8.0
2个回答
1
投票

在唯一键之一中重新定义列顺序:

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

0
投票

第二个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

© www.soinside.com 2019 - 2024. All rights reserved.