2009-08-12 6 views
2

私のクエリが遅くなる理由を理解できません。それは、チーム、プレーヤー、機器、メタデータの4つのテーブルです。プレーヤーと機器の記録にはFKチームがあり、チームをプレーヤーと機器の親とする。これら3つのテーブルの行にはそれぞれ、作成日、作成者のユーザーIDなどのメタデータが記録されています。2つの左結合を含むこのMySQLクエリを最適化するにはどうすればよいですか?

私が一度に取得したいのは、特定のチーム、作成日順。私はメタデータテーブルから開始し、player_とequipmentテーブルを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; 

あなたが各テーブル10,000の周りに、本当に遅いダウンを参照するには、多数の行を追加する必要があります。私が理解できないのは、「... AND player.team_id = 1」のように、あるテーブルのwhere句にフィルタをかけるだけで本当にすばらしい理由です。しかし、もう一方を追加すると「.. AND(player.team_id = 1 OR equipment.team_id = 1) "それははるかに長い時間がかかります。

ここに表とデータ型があります。多くのことを助けてくれると思われるものの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のプレイヤーと設備上の2つのSELECTSのUNIONを行うことによって、これをスピードアップする可能性が実現するが、それに注意してください、私はネイティブにUNIONのをサポートしていません使用していますORMとだから私はむしろ、代わりにこのクエリを最適化できるかどうか試してみたいと思います。また、私はただ好奇心が強いです。

+0

ああ、あなたのbaz-bar-foo fooを本当のbazに置き換えることはできますか? – markus

+0

申し訳ありません私はあなたを失ったと思いますが、実際のテーブル名でfoo、bar、bazを置き換えることを意味すると思いますか? – mcsnolte

+1

あなたが見ることができるように、変数名fooとbazを使用するコードのようにするのはかなり難しいので...しかし、あなたのbazをfooしたいなら、私はbarです! – markus

答えて

2

MySQLでは、 "OR"の条件を最適化するのは難しいです。

1つの共通の救済策は、クエリを2つのより単純なクエリに分割し、それらを結合するために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が第二SELECTの結果にのみUNIONの代わりの結果に適用されるように括弧を使用する必要があります。


更新:何がポリモーフィック関連付けと呼ばれやっている、それがSQLで使用するのは難しいです。その使用を推奨するいくつかのORMフレームワークにもかかわらず、私はそれをSQL反パターンと呼んでいます。

この場合、実際にはチームとプレーヤーの間、チームと装置の関係があります。プレーヤーは機器ではなく、機器はプレーヤーではありません。彼らは共通のスーパータイプを持っていません。 OOの意味とそれをモデル化した関係感の両方で誤解を招いています。

metadatadatatypeテーブルをダンプするといいでしょう。これらは反リレーショナル構造です。代わりに、team_id(私はteamsテーブルへの外部キーであると仮定します)を使用してください。選手や機器を別の種類のものとして扱う。あなたのORMにUNIONを使用できない場合は、それらを別々に取得してください。次に、アプリケーションで結果セットを結合します。

1つのSQLクエリですべてを取得する必要はありません。

+0

応答をありがとうが、おそらく私は少し上に私のメモを移動している必要があります。私はすでにこれを発見しましたが、好奇心のためにUNIONを使わずに最適化する方法を探しています。 – mcsnolte

+0

ああ、申し訳ありませんが、私はそのメモを忘れました。最適化に興味があれば、なぜORMを使用するのですか? :-P –

+0

説明とこのタイプの関係の言葉を与えてくれてありがとう。私は今あなたが言っていることを見て、それはほとんど理にかなっています。 – mcsnolte

関連する問題