⛓️

sequelize の migrationで外部キー制約を貼り付けた時の undo 時の設定

2018/07/11に公開

TL;DR

removeConstraint だけではなく removeIndex も指定しよう。

どうして?

constraint を導入した時に同時に index が張られるので、 removeConstraint だけすると index が残ってしまうため。

気になる人は具体例を後ろの方に書いてあるのでそちらを参照。

正解の記述例

'use strict';
module.exports = {
  up: (queryInterface, Sequelize) => {
    return [
      queryInterface.addConstraint(
        'members',
        ['group_id'],
        {
          type: 'foreign key',
          name: 'members_group_id_groups_fk',
          references: {
            table: 'groups',
            field: 'id',
          },
          onDelete: 'cascade',
        },
      ),
    ];
  },
  down: async (queryInterface, Sequelize) => {
    return [
      await queryInterface.removeConstraint('members', 'members_group_id_groups_fk'),
      await queryInterface.removeIndex('members', 'members_group_id_groups_fk'),
    ];
  }
};

実際にやってみる

スキーマ

例えば以下のような関係のテーブルがあったとする。
group_member.png

up する

それぞれのテーブルは既に create されているものとして、 members.group_id の外部キー制約として groups.id を設定したい場合、 up 時の記述としては以下のようになる。

addConstraint(
  'members',
  ['group_id'],
  {
    type: 'foreign key',
    name: 'members_group_id_groups_fk',
    references: {
      table: 'groups',
      field: 'id',
    },
    onDelete: 'cascade',
  },
)

sequelize db:migrate 実行後に SHOW CREATE TABLE members; を発行すると以下のように、 contraint とさらに index が追加されていることがわかる。

CREATE TABLE `members ` (
  `id` char(26) COLLATE utf8mb4_unicode_ci NOT NULL,
  `group_id` char(26) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0',
  `name` char(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  KEY `members_group_id_groups_fk` (`group_id`),
  CONSTRAINT `members_group_id_groups_fk`
  FOREIGN KEY (`group_id`) REFERENCES `groups` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

down に removeConstraint だけ記述

removeConstraint('members', 'members_group_id_groups_fk')

だけ記述して sequelize db:migrate:undo を実行し、SHOW CREATE TABLE members; を発行して確認する。

CREATE TABLE `members ` (
  `id` char(26) COLLATE utf8mb4_unicode_ci NOT NULL,
  `group_id` char(26) COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '0',
  `name` char(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  KEY `members_group_id_groups_fk` (`group_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

バッチリ index が残っておりますな。

備考

addConstraint の name は省略可

addConstraint で name を書かない場合、自動的に key 名が生成される。
しかしそれだと removeConstraint ができないので、適当なものを記述しておくと良い。
(書かずにやる方法はあるにはあるが、オススメするメリットはない)

await と順番

removeConstraint removeIndex に await を指定している。
これは制約が解除されてからじゃないと index を剥がせないため。
await にするので、実行元の関数に async を指定するのを忘れずに。

実際にやってしまった人の対応策

残ってしまった index を手動で削除する MySQL 文は以下の通り。
ALTER TABLE [table_name] DROP INDEX [index_name];

Discussion