【问题标题】:Indexes on super/parent SQL tables超级/父 SQL 表上的索引
【发布时间】:2013-12-03 10:44:11
【问题描述】:

我有一个如下所示的数据库架构。

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';

CREATE SCHEMA IF NOT EXISTS `mydb` DEFAULT CHARACTER SET latin1 COLLATE latin1_swedish_ci ;
USE `mydb` ;

-- -----------------------------------------------------
-- Table `mydb`.`parent`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`parent` (
  `id` INT NOT NULL ,
  `data` VARCHAR(45) NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;

-- -----------------------------------------------------
-- Table `mydb`.`OtherTable`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`OtherTable` (
  `id` INT NOT NULL ,
  `data` VARCHAR(45) NULL ,
  PRIMARY KEY (`id`) )
ENGINE = InnoDB;

-- -----------------------------------------------------
-- Table `mydb`.`parent_has_OtherTable`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`parent_has_OtherTable` (
  `parent_id` INT NOT NULL ,
  `OtherTable_id` INT NOT NULL ,
  PRIMARY KEY (`parent_id`, `OtherTable_id`) ,
  INDEX `fk_parent_has_OtherTable_OtherTable1_idx` (`OtherTable_id` ASC) ,
  INDEX `fk_parent_has_OtherTable_parent1_idx` (`parent_id` ASC) ,
  CONSTRAINT `fk_parent_has_OtherTable_parent1`
    FOREIGN KEY (`parent_id` )
    REFERENCES `mydb`.`parent` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_parent_has_OtherTable_OtherTable1`
    FOREIGN KEY (`OtherTable_id` )
    REFERENCES `mydb`.`OtherTable` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

-- -----------------------------------------------------
-- Table `mydb`.`child`
-- -----------------------------------------------------
CREATE  TABLE IF NOT EXISTS `mydb`.`child` (
  `parent_id` INT NOT NULL ,
  `andAnotherTable_id` INT NOT NULL ,
  `data` VARCHAR(45) NULL ,
  PRIMARY KEY (`parent_id`) ,
  CONSTRAINT `fk_child_parent1`
    FOREIGN KEY (`parent_id` )
    REFERENCES `mydb`.`parent` (`id` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;  

SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

我希望执行以下两个查询:

SELECT *
FROM child
INNER JOIN parent ON parent.id=child.parent_id
INNER JOIN parent_has_OtherTable ON parent_has_OtherTable.parent_id=parent.id
INNER JOIN OtherTable ON OtherTable.id=parent_has_OtherTable.OtherTable_id
WHERE child.parent_id=123

SELECT *
FROM child
INNER JOIN parent ON parent.id=child.parent_id
INNER JOIN parent_has_OtherTable ON parent_has_OtherTable.parent_id=parent.id
INNER JOIN OtherTable ON OtherTable.id=parent_has_OtherTable.OtherTable_id
WHERE OtherTable.id=123

我是否有任何理由需要在查询中包含parent 表(假设我不需要其中的任何数据),而是执行如下查询?我担心的是索引可能不再起作用,因为索引在parentparent_has_OtherTable 之间而不是childparent_has_OtherTable 之间。 PS。我还有其他类似于child 的表与parent 具有一对一的关系,因此我的架构就是这样的原因。

SELECT *
FROM child
INNER JOIN parent_has_OtherTable ON parent_has_OtherTable.parent_id=child.parent_id
INNER JOIN OtherTable ON OtherTable.id=parent_has_OtherTable.OtherTable_id
WHERE child.parent_id=123

SELECT *
FROM child
INNER JOIN parent_has_OtherTable ON parent_has_OtherTable.parent_id=child.parent_id
INNER JOIN OtherTable ON OtherTable.id=parent_has_OtherTable.OtherTable_id
WHERE OtherTable.id=123



mysql> EXPLAIN SELECT * FROM child INNER JOIN parent_has_OtherTable ON parent_has_OtherTable.parent_id=child.parent_id INNER JOIN OtherTable ON OtherTable.id=parent_has_OtherTable.OtherTable_id WHERE child.parent_id=1;
+----+-------------+-----------------------+-------+---------------------------------------------------------------------------------------+--------------------------------------+---------+-------+------+--------------------------------+
| id | select_type | table                 | type  | possible_keys                                                                         | key                                  | key_len | ref   | rows | Extra                          |
+----+-------------+-----------------------+-------+---------------------------------------------------------------------------------------+--------------------------------------+---------+-------+------+--------------------------------+
|  1 | SIMPLE      | child                 | const | PRIMARY                                                                               | PRIMARY                              | 4       | const |    1 |                                |
|  1 | SIMPLE      | parent_has_OtherTable | ref   | PRIMARY,fk_parent_has_OtherTable_OtherTable1_idx,fk_parent_has_OtherTable_parent1_idx | fk_parent_has_OtherTable_parent1_idx | 4       | const |    2 | Using index                    |
|  1 | SIMPLE      | OtherTable            | ALL   | PRIMARY                                                                               | NULL                                 | NULL    | NULL  |    3 | Using where; Using join buffer |
+----+-------------+-----------------------+-------+---------------------------------------------------------------------------------------+--------------------------------------+---------+-------+------+--------------------------------+
3 rows in set (0.00 sec)

mysql> EXPLAIN SELECT * FROM child INNER JOIN parent_has_OtherTable ON parent_has_OtherTable.parent_id=child.parent_id INNER JOIN OtherTable ON OtherTable.id=parent_has_OtherTable.OtherTable_id WHERE OtherTable.id=1;
+----+-------------+-----------------------+-------+---------------------------------------------------------------------------------------+------------------------------------------+---------+-------+------+--------------------------------+
| id | select_type | table                 | type  | possible_keys                                                                         | key                                      | key_len | ref   | rows | Extra                          |
+----+-------------+-----------------------+-------+---------------------------------------------------------------------------------------+------------------------------------------+---------+-------+------+--------------------------------+
|  1 | SIMPLE      | OtherTable            | const | PRIMARY                                                                               | PRIMARY                                  | 4       | const |    1 |                                |
|  1 | SIMPLE      | parent_has_OtherTable | ref   | PRIMARY,fk_parent_has_OtherTable_OtherTable1_idx,fk_parent_has_OtherTable_parent1_idx | fk_parent_has_OtherTable_OtherTable1_idx | 4       | const |    2 | Using index                    |
|  1 | SIMPLE      | child                 | ALL   | PRIMARY                                                                               | NULL                                     | NULL    | NULL  |    3 | Using where; Using join buffer |
+----+-------------+-----------------------+-------+---------------------------------------------------------------------------------------+------------------------------------------+---------+-------+------+--------------------------------+
3 rows in set (0.00 sec)

mysql>

【问题讨论】:

    标签: mysql sql indexing foreign-keys


    【解决方案1】:

    索引从不跨越表。外键引用另一个表,但 FK 使用该表中定义的索引与 FK,引用被引用表中的唯一索引。

    您应该能够通过使用EXPLAIN 分析查询来测试是否正在使用索引。例如,您可以看到每个表引用显示了在 key 字段中使用的一些索引:

    mysql> explain SELECT * FROM child 
    INNER JOIN parent ON parent.id=child.parent_id 
    INNER JOIN parent_has_OtherTable ON parent_has_OtherTable.parent_id=parent.id 
    INNER JOIN OtherTable ON OtherTable.id=parent_has_OtherTable.OtherTable_id 
    WHERE child.parent_id=123\G
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: child
             type: const
    possible_keys: PRIMARY
              key: PRIMARY
          key_len: 4
              ref: const
             rows: 1
            Extra: NULL
    *************************** 2. row ***************************
               id: 1
      select_type: SIMPLE
            table: parent
             type: const
    possible_keys: PRIMARY
              key: PRIMARY
          key_len: 4
              ref: const
             rows: 1
            Extra: NULL
    *************************** 3. row ***************************
               id: 1
      select_type: SIMPLE
            table: parent_has_OtherTable
             type: ref
    possible_keys: PRIMARY,fk_parent_has_OtherTable_OtherTable1_idx,fk_parent_has_OtherTable_parent1_idx
              key: PRIMARY
          key_len: 4
              ref: const
             rows: 1
            Extra: Using index
    *************************** 4. row ***************************
               id: 1
      select_type: SIMPLE
            table: OtherTable
             type: eq_ref
    possible_keys: PRIMARY
              key: PRIMARY
          key_len: 4
              ref: mydb.parent_has_OtherTable.OtherTable_id
             rows: 1
            Extra: NULL
    

    对于第二个查询,它也使用索引:

    mysql> explain SELECT * FROM child 
    INNER JOIN parent ON parent.id=child.parent_id 
    INNER JOIN parent_has_OtherTable ON parent_has_OtherTable.parent_id=parent.id 
    INNER JOIN OtherTable ON OtherTable.id=parent_has_OtherTable.OtherTable_id 
    WHERE OtherTable.id=123\G
    *************************** 1. row ***************************
               id: 1
      select_type: SIMPLE
            table: OtherTable
             type: const
    possible_keys: PRIMARY
              key: PRIMARY
          key_len: 4
              ref: const
             rows: 1
            Extra: NULL
    *************************** 2. row ***************************
               id: 1
      select_type: SIMPLE
            table: parent_has_OtherTable
             type: ref
    possible_keys: PRIMARY,fk_parent_has_OtherTable_OtherTable1_idx,fk_parent_has_OtherTable_parent1_idx
              key: fk_parent_has_OtherTable_OtherTable1_idx
          key_len: 4
              ref: const
             rows: 1
            Extra: Using index
    *************************** 3. row ***************************
               id: 1
      select_type: SIMPLE
            table: child
             type: eq_ref
    possible_keys: PRIMARY
              key: PRIMARY
          key_len: 4
              ref: mydb.parent_has_OtherTable.parent_id
             rows: 1
            Extra: NULL
    *************************** 4. row ***************************
               id: 1
      select_type: SIMPLE
            table: parent
             type: eq_ref
    possible_keys: PRIMARY
              key: PRIMARY
          key_len: 4
              ref: mydb.parent_has_OtherTable.parent_id
             rows: 1
            Extra: NULL
    

    关于您的评论和已编辑的问题:

    是的,由于childparent 具有1:1 的关系,因此不需要在联接中包含父级。包含 parent 的原因是如果您需要来自 parent 的一些列,或者行限制 - 也就是说,parent 中的 parent_id 值可能比 child 中的少(但根据定义,这是不可能的,因为没有值可以出现在不在父级中的子级中)。

    最后,我想一般原则是:您可以基于包含相同逻辑值域的任何列进行连接——它们不必必须是显式外部的一部分关键关系。

    【讨论】:

    • 谢谢比尔,您使用EXPLAIN 的两个查询都加入了parent。因此,我自己针对未加入父母的查询运行了它们,并将结果发布在我原来的帖子下。我是否正确理解索引不跨越表,因此,我没有理由需要加入父母?
    猜你喜欢
    • 2015-09-09
    • 1970-01-01
    • 2021-11-18
    • 1970-01-01
    • 2018-09-25
    • 2018-03-30
    • 1970-01-01
    • 1970-01-01
    • 2013-12-01
    相关资源
    最近更新 更多