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