Optimization 如何优化这个涉及两个左连接的MySQL查询?

Optimization 如何优化这个涉及两个左连接的MySQL查询?,optimization,mysql,left-join,polymorphic-associations,Optimization,Mysql,Left Join,Polymorphic Associations,我不明白为什么我的查询速度变慢了。它归结为四个表:团队、玩家、设备和元数据。玩家和装备中的记录对团队具有FK,使团队成为玩家和装备的父级。所有这三个表的行在元数据中都有一条记录,其中存储了创建日期、创建者用户id等信息 我想一次检索的是属于特定球队的任何球员和装备记录,按照创建日期的顺序。我从元数据表开始,通过metadata_id FK左键连接播放器和设备表,但是当我尝试过滤SELECT以仅检索某个团队的记录时,当有很多行时,查询会大大减慢 以下是查询: SELECT metadata.cre

我不明白为什么我的查询速度变慢了。它归结为四个表:团队、玩家、设备和元数据。玩家和装备中的记录对团队具有FK,使团队成为玩家和装备的父级。所有这三个表的行在元数据中都有一条记录,其中存储了创建日期、创建者用户id等信息

我想一次检索的是属于特定球队的任何球员和装备记录,按照创建日期的顺序。我从元数据表开始,通过metadata_id FK左键连接播放器和设备表,但是当我尝试过滤SELECT以仅检索某个团队的记录时,当有很多行时,查询会大大减慢

以下是查询:

SELECT metadata.creation_date, player.id, equipment.id
FROM
  metadata
  JOIN datatype       ON datatype.id           = metadata.datatype_id
  LEFT JOIN player    ON player.metadata_id    = metadata.id
  LEFT JOIN equipment ON equipment.metadata_id = metadata.id
WHERE
  datatype.name IN ('player', 'equipment')
  AND (player.team_id = 1 OR equipment.team_id = 1)
ORDER BY metadata.creation_date;
您需要添加很多行才能真正看到速度的减慢,每个表大约10000行。我不明白的是,如果我只在一个表中过滤where子句,那么它为什么会很快,例如:“…和player.team_id=1”,但当我添加另一个表时,“…和(player.team_id=1或equipment.team_id=1)”它会花费很多、更长的时间

下面是表和数据类型。请注意,有一件事似乎很有帮助,但不是很有帮助,那就是玩家和设备上的元数据id和团队id的组合键

CREATE TABLE `metadata` (
  `id` INT(4) unsigned NOT NULL auto_increment,
  `creation_date` DATETIME NOT NULL,
  `datatype_id` INT(4) unsigned NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB;
CREATE TABLE `datatype` (
  `id` INT(4) unsigned NOT NULL auto_increment,
  `name` VARCHAR(255) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB;
CREATE TABLE `team` (
  `id` INT(4) unsigned NOT NULL auto_increment,
  `metadata_id` INT(4) unsigned NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB;
CREATE TABLE `player` (
  `id` INT(4) unsigned NOT NULL auto_increment,
  `metadata_id` INT(4) unsigned NOT NULL,
  `team_id` INT(4) unsigned NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB;
CREATE TABLE `equipment` (
  `id` INT(4) unsigned NOT NULL auto_increment,
  `metadata_id` INT(4) unsigned NOT NULL,
  `team_id` INT(4) unsigned NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=InnoDB;
ALTER TABLE  `metadata` ADD INDEX (  `datatype_id` ),
  ADD INDEX ( `creation_date` );
ALTER TABLE  `team`      ADD INDEX (  `metadata_id` );
ALTER TABLE  `player`    ADD INDEX `metadata_id` (  `metadata_id`,  `team_id` ),
  ADD INDEX ( `team_id` );
ALTER TABLE  `equipment` ADD INDEX `metadata_id` (  `metadata_id`,  `team_id` ),
  ADD INDEX ( `team_id` );
ALTER TABLE `metadata`  ADD CONSTRAINT `metadata_ibfk_1`  FOREIGN KEY (`datatype_id`) REFERENCES `datatype` (`id`);
ALTER TABLE `team`      ADD CONSTRAINT `team_ibfk_1`      FOREIGN KEY (`metadata_id`) REFERENCES `metadata` (`id`);
ALTER TABLE `player`    ADD CONSTRAINT `player_ibfk_1`    FOREIGN KEY (`metadata_id`) REFERENCES `metadata` (`id`);
ALTER TABLE `player`    ADD CONSTRAINT `player_ibfk_2`    FOREIGN KEY (`team_id`)     REFERENCES `team` (`id`);
ALTER TABLE `equipment` ADD CONSTRAINT `equipment_ibfk_1` FOREIGN KEY (`metadata_id`) REFERENCES `metadata` (`id`);
ALTER TABLE `equipment` ADD CONSTRAINT `equipment_ibfk_2` FOREIGN KEY (`team_id`)     REFERENCES `team` (`id`);
INSERT INTO `datatype` VALUES(1,'team'),(2,'player'),(3,'equipment');

请注意我意识到我可以通过对给定球队id的球员和装备进行两次选择来轻松加快速度,但我使用的ORM本身并不支持UNION,因此我更愿意尝试看看是否可以优化此查询。我也很好奇。

在MySQL中,很难优化“
”条件

一种常见的补救方法是将查询拆分为两个更简单的查询,并使用
UNION
组合它们

 (SELECT metadata.creation_date, datatype.name, player.id
  FROM metadata
    JOIN datatype ON datatype.id = metadata.datatype_id
    JOIN player ON player.metadata_id = metadata.id
  WHERE datatype.name = 'player' AND player.team_id = 1)
 UNION ALL
 (SELECT metadata.creation_date, datatype.name, equipment.id
  FROM metadata
    JOIN datatype ON datatype.id = metadata.datatype_id
    JOIN equipment ON equipment.metadata_id = metadata.id
  WHERE datatype.name = 'equipment' AND equipment.team_id = 1)
 ORDER BY creation_date;
必须使用括号,以便
排序依据
应用于
联合
的结果,而不是仅应用于第二个
选择
的结果


更新:您所做的就是所谓的多态关联,很难在SQL中使用。我甚至称之为SQL反模式,尽管有些ORM框架鼓励使用它

在这种情况下,你真正拥有的是团队和球员之间的关系,以及团队和设备之间的关系。玩家不是装备,装备也不是玩家;它们没有共同的超类型。从面向对象和关系的角度来看,你用这种方式对它们进行建模是一种误导

我会说转储
元数据
数据类型
表。这些是反关系结构。相反,使用
team_id
(我假设它是
teams
表的外键)。将球员和装备视为不同的类型。如果不能在ORM中使用
UNION
,请分别获取它们。然后在应用程序中组合结果集


您不必在一个SQL查询中获取所有内容。

哦,拜托,您能用真实的baz替换您的baz bar foo foo吗?对不起,我想我失去了您,但我猜您的意思是用真实的表名替换foo、bar、baz吗?没错,因为您可以看到,这样很难理解。。。就像使用变量名foo和baz的代码一样。。。但如果你想享受你的美食,我是酒吧!好的,现在球员和设备属于一个团队。谢谢你的回复,但也许我应该把我的笔记移到更高的位置。我已经发现了这一点,但出于好奇,我正在寻找一种不用工会来优化它的方法。啊,对不起,我错过了那张便条。如果你对优化感兴趣,你到底为什么要使用ORM-p感谢您对这种关系的解释和术语。我现在明白你在说什么了,这基本上是有道理的。