我无法弄清楚为什么我的查询会变慢。它归结为四个表:团队,玩家,设备和元数据。玩家和装备中的记录拥有FK团队,使团队成为玩家和设备的父母。并且所有这三个表的行都有一个元数据记录,存储创建日期,创建者用户ID等内容。
我想一次性检索所有属于特定团队的玩家和设备记录,按创建日期排序。我从元数据表开始,并通过metadata_id FK离开加入播放器和设备表,但是当我尝试过滤SELECT以仅检索某个团队的记录时,查询会在有很多行时减慢大量时间。 / p>
以下是查询:
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。我不明白为什么如果我只在一个表上的where子句中过滤它真的很快,例如:“... AND player.team_id = 1”但是当我添加另一个使它“... .AND(player.team_id = 1 OR equipment.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做两个SELECTS的UNION来轻松加快速度,但是我使用的ORM本身并不支持UNION的所以我宁愿尝试看看我是否可以优化这个查询。我也很好奇。
答案 0 :(得分:2)
在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反模式,尽管有一些ORM框架鼓励使用它。
在这种情况下,你真正拥有的是球队和球员之间,球队和装备之间的关系。玩家不是设备和装备不是玩家;他们没有共同的超类型。你在OO意义和关系意义上都误导了你以这种方式模仿他们。
我要说转储您的metadata
和datatype
表格。这些都是反关系结构。相反,使用team_id
(我假设它是teams
表的外键)。将球员和装备视为不同类型。如果您无法在ORM中使用UNION
,请单独获取它们。然后在应用程序中组合结果集。
您不必在单个SQL查询中获取所有内容。