2

クエリが遅くなる理由がわかりません。つまり、チーム、プレーヤー、機器、メタデータの4つのテーブルになります。プレーヤーと機器のレコードにはチームへのFKがあり、チームはプレーヤーと機器の親になります。そして、これら3つのテーブルの行すべてに、作成日、作成者のユーザー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;

速度の低下を実際に確認するには、テーブルごとに約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');

Please note that I realize I could easily speed this up by doing a UNION of two SELECTS on player and equipment for a given team id, but the ORM I'm using does not natively support UNION's and so I'd much rather try and see if I can optimize this query instead. Also I'm just plain curious.

4

1 に答える 1

2

ORMySQL では、" " 条件を最適化するのは困難です。

一般的な解決策の 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;

が2 番目の の結果だけではなくORDER BY、 の結果に適用されるように、括弧を使用する必要があります。UNIONSELECT


更新:あなたがしていることはポリモーフィック アソシエーションと呼ばれ、SQL で使用するのは困難です。一部の ORM フレームワークでは使用が推奨されていますが、私はそれを SQL アンチパターンとさえ呼んでいます。

この場合、実際にあるのは、チームとプレーヤーの間、およびチームと機器の間の関係です。プレイヤーは装備ではなく、装備はプレイヤーではありません。共通のスーパータイプはありません。オブジェクト指向の意味とリレーショナルの意味の両方で、それらをそのようにモデル化したのは誤解を招きます。

あなたmetadatadatatypeテーブルをダンプすると思います。これらは反関係構造です。代わりに、team_id(テーブルへの外部キーであると想定していteamsます)を使用してください。プレーヤーと装備を別個のタイプとして扱います。UNIONORM で使用できない場合は、個別に取得してください。次に、アプリケーションで結果セットを結合します。

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

于 2009-08-12T22:10:36.073 に答える