使用左连接MySql中的列优化ORDER BY

时间:2017-11-19 11:28:01

标签: mysql query-optimization

我有一个使用左连接和条件的复杂查询,我会尽量避免复杂化。

我有两个表postsmetadata,两个表都有索引,我的查询如下:

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? 或者有没有办法让查询更快?

更新#1

为表格帖子显示创建

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

2 个答案:

答案 0 :(得分:1)

您正在使用LEFT JOIN而不是INNER JOIN,但实际上并未从联接表中选择任何列。这意味着,如果metadata行没有可用的链接posts,则此posts行仍将包含在结果集中。它使您的查询几乎完全过滤无意义。

您在extended_id行的metadata表中没有单独的索引,您将用于加入。当然它是meta_data_unique_key索引的一部分,但我会查看EXPLAIN子句以查询您的查询是否实际使用。由于您正在使用InnoDB引擎,因此从metadataposts创建显式外键也是一个好主意。

由于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