超级/父SQL表上的索引

时间:2013-11-19 18:18:37

标签: mysql sql indexing foreign-keys

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

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>

1 个答案:

答案 0 :(得分:1)

索引永远不会跨越表格。外键引用另一个表,但FK利用表中定义的索引和FK,引用引用表中的唯一索引。

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

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_id值可能少于子级中的父级(但是根据定义,这是不可能的,因为没有值可以出现在父母不在的孩子身上。)

最后,我猜一般原则是:你可以根据包含相同逻辑值域的任何列进行连接 - 它们必须是显式外来的一部分关键关系。