كيف يمكنني تحسين هذا الاستعلام MySQL الذي ينطوي على اثنين اليسار ينضم؟

StackOverflow https://stackoverflow.com/questions/1268924

سؤال

لا أستطيع معرفة سبب إبطال استفسالي. ما يغززه إلى أربعة طاولات: فريق، لاعب، معدات، وبيانات البيانات الوصفية. السجلات في اللاعب والمعدات لديها FK To Team، مما يجعل الفريق أحد الوالدين من اللاعب والمعدات. وكل ثلاثة من صفوف الصفوف الثلاثة لها سجل في البيانات الوصفية التي تخزن أشياء مثل تاريخ الإنشاء ومعرف مستخدم الخالق، إلخ.

ما أود استرداده كل مرة في وقت واحد، أي سجلات لاعب ومعدات تنتمي إلى فريق معين، من أجل تاريخ الإبداع. أبدأ من طاولة البيانات الوصفية ويترك الجداول اللاعب والمعدات عبر FK Metadata_ID، ولكن عندما أحاول تصفية التحديد إلى سجل السجلات فقط لفريق معين، فإن الاستعلام يبطئ وقتا كبيرا عندما يكون هناك الكثير من الصفوف.

هنا هو الاستعلام:

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 ONE على جدول واحد، على سبيل المثال: "... و Player.team_id = 1" ولكن عندما أضيف الآخر لجعله ".. . و (Player.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');

يرجى الملاحظة أن أدرك أنه يمكنني بسهولة تسريع هذا الأمر من خلال القيام بعمل اتحاد محددين على اللاعب والمعدات لمعرف فريق معين، ولكن orm أنا أستخدمه لا يدعم اتحاد أصحابها ولأنني أحاول كثيرا ومعرفة ما إذا كنت يمكن تحسين هذا الاستعلام بدلا من ذلك. أيضا أنا مجرد فضول.

هل كانت مفيدة؟

المحلول

في 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. أنا حتى أن أسميها مزيدا من مكنسوباتيرن، على الرغم من بعض أطر الجميل التي تشجع استخدامها.

ما لديك حقا في هذه الحالة هو علاقة بين الفرق واللاعبين، وبين الفرق والمعدات. اللاعبون ليسوا المعدات والمعدات ليسوا لاعبين؛ ليس لديهم supertype مشترك. من المضلل في كل من المعنى OO والمعنى العلائقية التي قمت بصنعها بهذه الطريقة.

أود أن أقول تفريغ الخاص بك metadata و datatype الجداول. هذه هي هياكل معادية العلائقية. بدلا من ذلك، استخدم team_id (الذي افترض هو مفتاح أجنبي ل teams جدول). علاج اللاعبين والمعدات أنواع متميزة. جلب لهم بشكل منفصل إذا كنت لا تستطيع استخدام UNION في orm الخاص بك. ثم الجمع بين مجموعات النتائج في التطبيق الخاص بك.

ليس لديك لإحضار كل شيء في استعلام SQL واحد.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top