我有一个使用左连接和条件的复杂查询,我会尽量避免复杂化。
我有两个表posts
和metadata
,两个表都有索引,我的查询如下:
SELECT `posts`.* FROM posts
LEFT JOIN `metadata` ON `metadata`.`extended_type`='App\Post' AND `metadata`.`extended_id` = `posts`.`id` AND `metadata`.`key`='display_date'
WHERE metadata.value <= DATE(NOW()) AND posts.type='type' AND posts.published=1
ORDER BY metadata.value DESC limit 8;
字段值为date
类型且已编入索引。
查询取0.84 sec
。
当我删除ORDER BY
或使用posts.created_at
列进行排序时,查询将0.00 sec
。
有没有办法从联接表中使用FORCE INDEX FOR ORDER BY
?
或者有没有办法让查询更快?
为表格帖子显示创建
Create Table: CREATE TABLE `posts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`published` tinyint(1) NOT NULL DEFAULT '0',
`type` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'post',
`user_id` int(10) unsigned NOT NULL,
`sort` int(11) DEFAULT NULL,
`deleted_at` timestamp NULL DEFAULT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `posts_user_id_foreign` (`user_id`),
KEY `posts_type_index` (`type`),
KEY `posts_published_index` (`published`),
KEY `posts_sort_index` (`sort`),
KEY `posts_deleted_at_index` (`deleted_at`),
KEY `posts_created_at_index` (`created_at`),
CONSTRAINT `posts_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=39302 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
为post_contents显示创建
CREATE TABLE `post_contents` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`post_id` int(10) unsigned NOT NULL,
`lang` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT 'ar',
`title` text COLLATE utf8mb4_unicode_ci NOT NULL,
`content` mediumtext COLLATE utf8mb4_unicode_ci,
`excerpt` text COLLATE utf8mb4_unicode_ci,
`published` tinyint(1) NOT NULL DEFAULT '0',
`user_id` int(10) unsigned NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `post_contents_post_id_foreign` (`post_id`),
KEY `post_contents_user_id_foreign` (`user_id`),
KEY `post_contents_lang_index` (`lang`),
KEY `post_contents_published_index` (`published`),
KEY `post_contents_post_id_lang_index` (`lang`(2)),
CONSTRAINT `post_contents_post_id_foreign` FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`) ON DELETE CASCADE,
CONSTRAINT `post_contents_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON UPDATE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=84100 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
为meta_data表显示create
CREATE TABLE `meta_data` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`extended_id` int(10) unsigned NOT NULL,
`extended_type` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`key` varchar(191) COLLATE utf8mb4_unicode_ci NOT NULL,
`value` text COLLATE utf8mb4_unicode_ci,
`type` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
`created_at` timestamp NULL DEFAULT NULL,
`updated_at` timestamp NULL DEFAULT NULL,
`value_int` int(11) DEFAULT NULL,
`value_date` date DEFAULT NULL,
`value_float` decimal(20,9) DEFAULT NULL,
`value_string` varchar(191) COLLATE utf8mb4_unicode_ci DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `meta_data_unique_key` (`extended_id`,`extended_type`,`key`),
KEY `meta_data_extended_type_index` (`extended_type`),
KEY `meta_data_key_index` (`key`),
KEY `meta_data_value_int_index` (`value_int`),
KEY `meta_data_value_date_index` (`value_date`),
KEY `meta_data_value_float_index` (`value_float`),
KEY `meta_data_value_string_index` (`value_string`)
) ENGINE=InnoDB AUTO_INCREMENT=330592 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
真正的查询是:
SELECT `posts`.* FROM `posts`
LEFT JOIN `post_contents` ON `post_contents`.`post_id`=`posts`.`id` AND `post_contents`.`lang`='en'
LEFT JOIN `meta_data` ON `meta_data`.`extended_id`=`post_contents`.`id` AND `meta_data`.`extended_type` = 'App\\PostContent' AND `meta_data`.`key`='display_date'
WHERE
`posts`.`type` = 'type' AND
`posts`.`published` = 1 AND
exists(
SELECT *
FROM `post_contents`
WHERE
`posts`.`id` = `post_contents`.`post_id` AND
`lang` = 'en' AND
`published` = 1
AND
exists(
SELECT *
FROM `meta_data`
WHERE
`post_contents`.`id` = `meta_data`.`extended_id` AND
`meta_data`.`extended_type` = 'App\\PostContent' AND
`key` = 'display_date' AND
`value_date` <= DATE(now()))
) AND
NOT exists(
SELECT *
FROM `meta_data`
WHERE
`posts`.`id` = `meta_data`.`extended_id` AND
`meta_data`.`extended_type` = 'App\\Post' AND
`key` = 'not_in_home' AND
`value_int` = 1
) AND
`posts`.`deleted_at` IS NULL
ORDER BY `meta_data`.`value_date` DESC
LIMIT 8;
查询解释结果:
*************************** 1. row ***************************
id: 1
select_type: PRIMARY
table: posts
type: ref
possible_keys: posts_type_index,posts_published_index,posts_deleted_at_index
key: posts_type_index
key_len: 766
ref: const
rows: 22112
Extra: Using index condition; Using where; Using temporary; Using filesort
*************************** 2. row ***************************
id: 1
select_type: PRIMARY
table: post_contents
type: ref
possible_keys: post_contents_post_id_foreign,post_contents_lang_index,post_contents_post_id_lang_index
key: post_contents_post_id_foreign
key_len: 4
ref: tislamqa_db.posts.id
rows: 1
Extra: Using where
*************************** 3. row ***************************
id: 1
select_type: PRIMARY
table: meta_data
type: eq_ref
possible_keys: meta_data_unique_key,meta_data_extended_type_index,meta_data_key_index
key: meta_data_unique_key
key_len: 1536
ref: tislamqa_db.post_contents.id,const,const
rows: 1
Extra: Using where
*************************** 4. row ***************************
id: 4
select_type: DEPENDENT SUBQUERY
table: meta_data
type: eq_ref
possible_keys: meta_data_unique_key,meta_data_extended_type_index,meta_data_key_index,meta_data_value_int_index
key: meta_data_unique_key
key_len: 1536
ref: tislamqa_db.posts.id,const,const
rows: 1
Extra: Using index condition; Using where
*************************** 5. row ***************************
id: 2
select_type: DEPENDENT SUBQUERY
table: post_contents
type: ref
possible_keys: post_contents_post_id_foreign,post_contents_lang_index,post_contents_published_index,post_contents_post_id_lang_index
key: post_contents_post_id_foreign
key_len: 4
ref: tislamqa_db.posts.id
rows: 1
Extra: Using where
*************************** 6. row ***************************
id: 3
select_type: DEPENDENT SUBQUERY
table: meta_data
type: eq_ref
possible_keys: meta_data_unique_key,meta_data_extended_type_index,meta_data_key_index,meta_data_value_date_index
key: meta_data_unique_key
key_len: 1536
ref: tislamqa_db.post_contents.id,const,const
rows: 1
Extra: Using index condition; Using where
答案 0 :(得分:1)
您正在使用LEFT JOIN
而不是INNER JOIN
,但实际上并未从联接表中选择任何列。这意味着,如果metadata
行没有可用的链接posts
,则此posts
行仍将包含在结果集中。它使您的查询几乎完全过滤无意义。
您在extended_id
行的metadata
表中没有单独的索引,您将用于加入。当然它是meta_data_unique_key
索引的一部分,但我会查看EXPLAIN
子句以查询您的查询是否实际使用。由于您正在使用InnoDB引擎,因此从metadata
到posts
创建显式外键也是一个好主意。
由于ORDER BY
适用于结果集而非源表,但您没有将列排序到结果集中 - 它(很可能)强制MySQL以{{1}的整个内容运行}表,并记住其中的自动增量值 - 它是相当大的数据集。
我建议尝试使用metadata
并在您的INNER JOIN
条款中包含metadata.value
字段。
答案 1 :(得分:1)
由于某些原因,当ORDER BY
条件与子查询一起使用时exists
没有使用索引,我将查询更改为使用JOIN
条件where
语句。< / p>
我的上次质疑:
SELECT `posts`.* FROM `posts`
JOIN `post_contents` ON `post_contents`.`post_id` = `posts`.`id` AND `post_contents`.`lang` = 'en' AND `post_contents`.`published` = 1
JOIN `meta_data` as m ON m.`extended_id` = `post_contents`.`id` AND m.`extended_type` = 'App\\PostContent' AND m.`key` = 'display_date'
LEFT JOIN `meta_data` as n ON n.`extended_id` = `posts`.`id` AND n.`extended_type` = 'App\\POST' AND n.`key` = 'not_in_home'
WHERE
`posts`.`type` = 'fatawa' AND
`posts`.`published` = 1 AND
m.`value_date` <= DATE(now()) AND
n.`value_int` != 1 AND
`posts`.`deleted_at` IS NULL
ORDER BY m.`value_date` DESC
LIMIT 8;
花了0.00 sec