كيف يمكنني تحسين هذا الاستعلام MySQL الذي ينطوي على اثنين اليسار ينضم؟
-
13-09-2019 - |
سؤال
لا أستطيع معرفة سبب إبطال استفسالي. ما يغززه إلى أربعة طاولات: فريق، لاعب، معدات، وبيانات البيانات الوصفية. السجلات في اللاعب والمعدات لديها 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 واحد.