寝ても覚めてもこんぴうた

プログラム書いたり、ネットワーク設計したり、サーバ構築したり、車いじったり、ゲームしたり。そんなひとにわたしはなりたい。 投げ銭は kyash_id : chidakiyo マデ

Spanner で NOT Nullのカラムを追加したい

f:id:chidakiyo:20200908094813j:plain

Spanner は RDB のようにかっちりとスキーマを定義する DB なので、
Datastore などのように、プロパティをふわっと追加してデータ投入などできません。

RDB と同じように DDL を利用してカラム追加して利用しますが、 NOT NULL のカラムの追加ができないという特性があります。

今回は以下のようなスキーマで考えます。

CREATE TABLE Member (
    ID STRING(256) NOT NULL,
    Name STRING(256) NOT NULL,
) PRIMARY KEY (ID);

NOT NULL カラムが追加できないことを確認する

以下のようなクエリを実行し、 NOT NULL の制約のあるカラムを追加しようとしてみます

ALTER TABLE Member ADD COLUMN Nickname STRING(256) NOT NULL;

→ 実行すると、 NOT NULL なカラムは追加できないよ。 という感じに怒られてしまいます。

NULL 許可したカラムを追加してみる

NOT NULL なカラムは追加できないが、 Nullable なカラムは追加できるはずなので追加してみます。

ALTER TABLE Member ADD COLUMN Nickname STRING(256);

→ 問題なく追加できますね。

Nickname カラムをなんとか NOT NULL にしてみる

例えば、 NOT NULL にカラムを変更しようとしても、 Nullable なカラムを追加した状態では NULL が入っているので、変更は失敗するはず。
なので、 NULL な値をなくして、 NOT NULL なカラムに変更するというのを試してみる。

ためしに、 Nullable なカラムに NULL が入った状態で NOT NULL に 変更してみる。

ALTER TABLE Member ALTER COLUMN Nickname STRING(256) NOT NULL;

→ 予想通り Adding a NOT NULL constraint on a column Member.Nickname is not allowed because it has a NULL value at key: [xxx] といった形で NULL な値があるので NOT NULL の制約はつけられないと怒られた

NULL 値を Empty String に置き換えてみる(なにか特定のデフォルト値でもいいと思います)

UPDATE Member SET Nickname = '' WHERE Nickname IS NULL;

NULL を置き換えたら NOT NULL 制約をつけてみる

ALTER TABLE Member ALTER COLUMN Nickname STRING(256) NOT NULL;

→ 結果うまく行きます。

最終的にこのようなテーブル構成になります

CREATE TABLE Member (
    ID STRING(256) NOT NULL,
    Name STRING(256) NOT NULL,
    Nickname STRING(256) NOT NULL,
) PRIMARY KEY (ID)

NOT NULL なカラムを追加するための流れの要約

  1. NULL 許可する形でカラムを追加する
  2. NOT NULL にしたいカラムの場合には NULL ではない値にカラムを書き換える (注意点あり *1)
  3. NOT NULL 制約を満たせる状態になったら ALTER 文で改めてスキーマ変更する。

の3つの手順を経る必要がある。(default的なものがない)

注意(この点注意しましょう!!)

*1) カラム書き換えはUPDATEステートメントなどを利用するが、Spannerの制約で 20k 以上の変更が1クエリで行えないので注意。(また、20k はレコード数でもない点も注意すること)

まとめ

機能的には NOT NULL のカラムを追加することはできるようなのですが、最後の 注意 の点だけ気をつけましょう
運用が始まって大量のレコードが追加されているテーブルの場合、Spanner の一度に更新できる制限(20k)のために非常に大変なことになります
場合によってはパーティション DML を利用したりするのかな?(参考のリンク先参照)

参考

cloud.google.com

cloud.google.com