通过不选择正确的索引来排序

时间:2012-12-15 21:12:20

标签: mysql

我有项目表,每个项目都分配了多个类别。类别映射存储在project_category表中。我想列出所有最近未过期的项目。这是架构,索引和查询。

模式

Create table projects (
   project_id Bigint UNSIGNED NOT NULL AUTO_INCREMENT,
   project_title Varchar(300) NOT NULL,
   date_added Datetime NOT NULL,
   is_expired Bit(1) NOT NULL DEFAULT false,
Primary Key (project_id)) ENGINE = InnoDB;

Create table project_category (
   project_category_id Int UNSIGNED NOT NULL AUTO_INCREMENT,
   cat_id Int UNSIGNED NOT NULL,
   project_id Bigint UNSIGNED NOT NULL,
Primary Key (project_category_id)) ENGINE = InnoDB;

索引

CREATE INDEX project_listing (is_expired, date_added) ON projects;
Create INDEX category_mapping_IDX ON project_category (project_id,cat_id);

查询

mysql> EXPLAIN
  SELECT P.project_id
  FROM projects P
  INNER JOIN project_category C USING (project_id)
  WHERE P.is_expired=false
    AND C.cat_id=17
  ORDER BY P.date_added DESC LIMIT 27840,10;
+----+-------------+-------+--------+--------------------------------------------+---------+---------+-------------------------+--------+---------------------------------+
| id | select_type | table | type   | possible_keys                              | key     | key_len | ref                     | rows   | Extra                           |
+----+-------------+-------+--------+--------------------------------------------+---------+---------+-------------------------+--------+---------------------------------+
|  1 | SIMPLE      | C     | ref    | project_id,cat_id,category_mapping_IDX     | cat_id  | 4       | const                   | 185088 | Using temporary; Using filesort |
|  1 | SIMPLE      | P     | eq_ref | PRIMARY,is_expired_INX,project_listing_IDX | PRIMARY | 8       | freelancer.C.project_id |      1 | Using where                     |
+----+-------------+-------+--------+--------------------------------------------+---------+---------+-------------------------+--------+---------------------------------+

我想知道为什么MySQL没有使用project_category上的索引,以及它为什么要进行完整排序?

我还尝试了以下查询,以避免文件排序,但它也无法正常工作。

mysql> EXPLAIN
  SELECT P.project_id
  FROM projects P,
  (
    SELECT P.project_id 
    FROM projects P
    INNER JOIN project_category C USING (project_id)
    WHERE C.cat_id=17
  ) F
  WHERE F.project_id=P.project_id
    AND P.is_expired=FALSE
  LIMIT 10;

+----+-------------+------------+--------+--------------------------------------------+---------+---------+-------------------------+--------+-------------+
| id | select_type | table      | type   | possible_keys                              | key     | key_len | ref                     | rows   | Extra       |
+----+-------------+------------+--------+--------------------------------------------+---------+---------+-------------------------+--------+-------------+
|  1 | PRIMARY     | <derived2> | ALL    | NULL                                       | NULL    | NULL    | NULL                    | 110920 |             |
|  1 | PRIMARY     | P          | eq_ref | PRIMARY,is_expired_INX,project_listing_IDX | PRIMARY | 8       | F.project_id            |      1 | Using where |
|  2 | DERIVED     | C          | ref    | project_id,cat_id,category_mapping_IDX     | cat_id  | 4       |                         | 185088 |             |
|  2 | DERIVED     | P          | eq_ref | PRIMARY                                    | PRIMARY | 8       | freelancer.C.project_id |      1 | Using index |
+----+-------------+------------+--------+--------------------------------------------+---------+---------+-------------------------+--------+-------------+

1 个答案:

答案 0 :(得分:0)

你的问题在这里:

Create INDEX category_mapping_IDX ON project_category (project_id,cat_id);

当您尝试子选择单个cat_id时,此索引无用,因为cat_id不是索引的第一部分。将索引视为连接字符串,您可以看到它无法使用的原因。交换订单:

Create INDEX category_mapping_IDX ON project_category (cat_id, project_id);