我怎么能优化这MySQL的查询包括两个左加入?
-
13-09-2019 - |
题
我无法弄清楚为什么我的查询减慢。什么这可以归结为四个表格:团队,球员、设备和元数据。记录在播放器和设备有FK队,作队的一个家长的播放器和设备。和所有的三个这些表的行各有一个记录的元数据存储的东西喜欢制作日期,创造者user id,等等。
我想检索所有在一旦任何播放器和设备记录属于一个特别的团队,以便创造的日期。我开始从一元数据表和左加入机器和设备表通过metadata_id FK,但是,当我试着过滤器的选择,只能检索记录某个团队,查询减慢大的时候,有很多行。
这里是查询:
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;
你需要添加一个很大的行真正看到缓慢下来,约有10,000每个表。我不明白的是为什么这是真的快速如果我只用过滤器在那里的条款之一表,例如:"...和球员。team_id=1"但是,当我加入的其他要让它"...和(播放机。team_id=1或设备。team_id=1)"这需要多,更长的时间。
这里有表格和数据类型。注意一件事,似乎有很大的帮助,但并不是所有那么多,是组合键在播放机和设备为metadata_id和team_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,但是对象我使用本身不支持联盟并以我宁愿尝试一下,看看如果我能够优化这种查询。我也只是普通的好奇。
解决方案
在MySQL这是难以优化"OR
"条件。
一个常见的补救方法是分裂的查询变成两个较简单的查询和使用 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;
你必须使用括号中,以便的 ORDER BY
适用于结果的 UNION
而不是只对结果的第二 SELECT
.
更新: 你在做什么是所谓的多态协会,这是难以使用在SQL。我甚至把它SQL反模式,尽管一些欧姆框架,鼓励其使用。
什么你真的有在这种情况之间的关系团队和球员和球队之间和设备。球员都没有的设备和设备是不是玩家;他们没有一个共同的超类型.这是误导人在两个OO识和关系的感觉是你有仿照他们的方式。
我会说你倾倒 metadata
和 datatype
表。这些都是防的关系结构。相反,使用 team_id
(我认为是一个外国的关键 teams
表)。治疗员和设备作为不同的类型。获取他们分开如果你不能使用 UNION
在你的奥姆.然后结合的结果集中的应用程序。
你不必取一切都在一个单一的SQL query.