جعل MySQL يستخدم الفهرس الأساسي دون التلميح أو الإجبار
سؤال
لدي بنية قاعدة البيانات التالية:
CREATE TABLE `question` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`level_id` int(11) NOT NULL,
`video_id` int(11) NOT NULL,
`is_active` tinyint(1) NOT NULL,
PRIMARY KEY (`id`),
KEY `question_b8f3f94a` (`level_id`),
KEY `question_c11471f1` (`video_id`),
CONSTRAINT `level_id_refs_id_27dd88d9` FOREIGN KEY (`level_id`) REFERENCES `level` (`id`),
CONSTRAINT `video_id_refs_id_1c4fbe15` FOREIGN KEY (`video_id`) REFERENCES `video` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3054 DEFAULT CHARSET=utf8
CREATE TABLE `level` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`success_rate` tinyint(3) unsigned NOT NULL,
`name` varchar(100) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=25 DEFAULT CHARSET=utf8
CREATE TABLE `video` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`file` varchar(200) DEFAULT NULL,
`name` longtext NOT NULL,
`subtitle` longtext NOT NULL,
`producer` longtext,
`director` longtext,
`details` longtext,
`related_content_url` longtext,
`counter` int(10) unsigned NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3055 DEFAULT CHARSET=utf8
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`password` varchar(128) NOT NULL,
`last_login` datetime NOT NULL,
`is_superuser` tinyint(1) NOT NULL,
`username` varchar(82) NOT NULL,
`first_name` varchar(30) NOT NULL,
`last_name` varchar(30) NOT NULL,
`email` varchar(82) NOT NULL,
`is_staff` tinyint(1) NOT NULL,
`is_active` tinyint(1) NOT NULL,
`date_joined` datetime NOT NULL,
`is_verified` tinyint(1) NOT NULL,
`verification_code` varchar(40) DEFAULT NULL,
`birthday` date DEFAULT NULL,
`gender` varchar(1) DEFAULT NULL,
`language_id` int(11) DEFAULT NULL,
`auth_token` varchar(40) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`),
UNIQUE KEY `verification_code` (`verification_code`),
KEY `user_784efa61` (`language_id`),
KEY `email_idx` (`email`),
CONSTRAINT `language_id_refs_id_016597a8` FOREIGN KEY (`language_id`) REFERENCES `language` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=469322 DEFAULT CHARSET=utf8
CREATE TABLE `star` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`question_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`counter` tinyint(3) unsigned NOT NULL,
PRIMARY KEY (`id`),
KEY `star_25110688` (`question_id`),
KEY `star_6340c63c` (`user_id`),
CONSTRAINT `question_id_refs_id_3c6023b7` FOREIGN KEY (`question_id`) REFERENCES `question` (`id`),
CONSTRAINT `user_id_refs_id_4b270cea` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3737324 DEFAULT CHARSET=utf8
يقوم orm الذي أستخدمه بإنشاء الاستعلام التالي لصفحة معينة:
SELECT *
FROM `star`
INNER JOIN `question` ON (`star`.`question_id` = `question`.`id`)
INNER JOIN `level` ON (`question`.`level_id` = `level`.`id`)
INNER JOIN `video` ON (`question`.`video_id` = `video`.`id`)
INNER JOIN `user` ON (`star`.`user_id` = `user`.`id`)
ORDER BY `star`.`id` DESC
LIMIT 10;
يتم تشغيل هذا الاستعلام للأعمار.
+------+-------------+----------+--------+---------------------------------------------+-------------------+---------+----------------------------+------+---------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+-------------+----------+--------+---------------------------------------------+-------------------+---------+----------------------------+------+---------------------------------+
| 1 | SIMPLE | level | ALL | PRIMARY | NULL | NULL | NULL | 24 | Using temporary; Using filesort |
| 1 | SIMPLE | question | ref | PRIMARY,question_b8f3f94a,question_c11471f1 | question_b8f3f94a | 4 | level.id | 63 | |
| 1 | SIMPLE | video | eq_ref | PRIMARY | PRIMARY | 4 | question.video_id | 1 | |
| 1 | SIMPLE | star | ref | PRIMARY,star_25110688,star_6340c63c | star_25110688 | 4 | question.id | 631 | Using index condition |
| 1 | SIMPLE | user | eq_ref | PRIMARY | PRIMARY | 4 | star.user_id | 1 | |
+------+-------------+----------+--------+---------------------------------------------+-------------------+---------+----------------------------+------+---------------------------------+
"الطلب حسب star.id" لا يستخدم المفتاح الأساسي.إضافة فهرس الاستخدام (الأساسي) يحل المشكلة بالنسبة لي.لكنني لا أريد كتابة الاستعلامات يدويًا.
هل هناك أي طريقة أخرى لإجبار الخلية على استخدام الفهارس الأساسية (باستخدام ordr by وما إلى ذلك)، دون التلميح أو فرض الفهارس يدويًا؟
المحلول
وهنا اقتراح لإعادة الكتابة.ربما يمكن إقناع ORM الخاص بك بإنتاج هذا الاستعلام.إنه يختلف قليلاً عن الأصل.التغيير الوحيد هو جدول مشتق بدلاً من الجدول الأساسي star
.حيث أن الجداول تحتوي على مفاتيح خارجية محددة ويبدأ الاستعلام منها star
, ، يتبع المفاتيح الخارجية، وستكون النتيجة هي نفسها:
SELECT *
FROM
( SELECT * FROM star ORDER BY id DESC LIMIT 10 ) -- the only change
AS star
INNER JOIN `question` ON (`star`.`question_id` = `question`.`id`)
INNER JOIN `level` ON (`question`.`level_id` = `level`.`id`)
INNER JOIN `video` ON (`question`.`video_id` = `video`.`id`)
INNER JOIN `user` ON (`star`.`user_id` = `user`.`id`)
ORDER BY `star`.`id` DESC
LIMIT 10 ;
هناك فكرة أخرى تتمثل في الاحتفاظ باستعلامك ولكن تغيير كل شيء INNER
ينضم إلى LEFT
ينضم.قد ينتج عن هذا خطة مختلفة تستخدم المفتاح الأساسي لـ star
.وبسبب المفاتيح الخارجية، نحن على يقين مرة أخرى من أن الاستعلام مكافئ وسيؤدي إلى نفس النتائج.
لا علاقة لها تماما بالكفاءة:
SELECT *
لا ينبغي أن تستخدم.قم بإضافة قائمة الأعمدة التي تحتاجها فقط، وليس كل أعمدة جميع الجداول المعنية.كيف ستحدد على أي حال في النتائج ما إذا كانid
العمود يأتي منstar.id
او منquestion.id
او من ...؟