テーブルを継承して新しいテーブルを作成する(CREATE TABLE ... INHERITS)

PostgreSQL ではテーブルを作成する時に INHERITS を指定することで、作成済みのテーブルを継承した新しいテーブルを作成することができます。親となるテーブルを継承した子テーブルは親テーブルで定義されているカラムをすべて継承します。親テーブルに対する変更は子テーブルにも反映され、親テーブルに対してデータを取得すると子テーブルのデータもあわせて取得されます。ここでは PostgreSQL でテーブルの継承について解説します。

(Last modified: )

テーブルの継承の使い方

テーブルの継承の使い方の例としてあるサービスを利用している無料ユーザーと有料ユーザーに関するテーブルを作成する場合で考えてみます。

無料でも優良でもユーザーとしての基本データは同じですが、有料ユーザーの場合は課金開始日という項目が一つ余分に必要になります。一つのテーブルですべてのユーザーを管理し、有料ユーザーだけが使用する課金開始日というカラムを用意してもいいのですが、有料ユーザーだけを高速に処理するために別のテーブルに分けたいとします。ただし無料か有料かに関係なく全ユーザーを対象に検索を行いたい場合もあります。

このような時にテーブルの継承を使用します。最初に親テーブルとなる無料ユーザー向けのテーブルを作成し、子テーブルとなる有料ユーザー向けのテーブルでは無料ユーザー向けのテーブルを継承しつつ課金開始日というカラムを一つ追加します。こうすることで親テーブルに対して検索を行った場合は親テーブルだけでなく子テーブルのデータも含めて検索を行うことができ、また子テーブルに対してだけ検索を行うこともできます。

テーブルの継承を行う場合に、継承する子テーブルでは次の書式を使用します。

CREATE TABLE table_name (
  column_name data_type
) INHERITS ( parent_table [, ... ] )

継承する親テーブル( parent_table )を指定してテーブルを作成します。親テーブルで定義されているすべてのカラムと、子テーブルで定義したカラムを持つテーブルが作成されます。

継承するテーブルは複数指定することができますが、それぞれのテーブルで定義されているカラムに関していくつか条件があります。こちらはあとで解説します。

テーブル構造をコピーする LIKE 句を指定した場合と似ていますが、テーブル構造をコピーした場合はコピー元とコピー先は全く別のテーブルとして扱われますが、テーブルを継承する INHERITS 句を指定した場合は親テーブルと子テーブルのカラムが連動しており、また親テーブルからデータを取得した時に子テーブルのデータも含めて取得されます。( LIKE 句について詳しくは「テーブル構造をコピーして新しいテーブルを作成する(CREATE TABLE ... LIKE)」を参照されてください)。

親テーブルを作成する

それでは実際に試してみます。 mydb データベースの myschema スキーマに親となる次のようなテーブルを作成しました。

create table myschema.customer (
  name varchar(10), 
  address varchar(10)
);

テーブルの継承の使い方(1)

psql メタコマンドの \d コマンドを使って作成したテーブルのカラムに関する情報を表示してみます。

\d myschema.customer

テーブルの継承の使い方(2)

作成したテーブルには次のようなデータを追加しておきました。

insert into myschema.customer values
  ('Yamada', 'Tokyo'), 
  ('Honda', 'Kyoto'), 
  ('Watanabe', 'Nara'),
  ('Asada', 'Osaka'),
  ('Nomura', 'Chiba'),
  ('Kida', 'Tokyo');

テーブルの継承の使い方(3)

親テーブルを継承した子テーブルを作成する

次に先ほど作成した customer テーブルを親テーブルとして継承するテーブルを mydb データベースの myschema スキーマに次のように作成しました。

create table myschema.feecustomer (
  billingdate date 
) inherits(myschema.customer);

テーブルの継承の使い方(4)

psql メタコマンドの \d コマンドを使って作成したテーブルのカラムに関する情報を表示してみます。

\dt myschema.feecustomer

テーブルの継承の使い方(5)

feecustomer テーブルでは定義したカラムが billingdate カラムだけですが、 customer テーブルを継承しているので customer テーブルで定義されているすべてのカラムを feecustomer テーブルは持っています。

作成したテーブルには次のようなデータを 4 つ追加しておきました。

insert into myschema.feecustomer values
  ('Makimura', 'Tokyo', '2019-05-04'), 
  ('Ookura', 'Osaka', '2019-03-22'), 
  ('Takagi', 'Nara', '2019-05-18'), 
  ('Hori', 'Kanagawa', '2019-04-12')

テーブルの継承の使い方(6)

親テーブルと子テーブルからまとめてデータを取得する

それではデータを取得してみます。まずは親テーブルおよび子テーブルに含まれているデータをまとめて取得したい場合には親テーブルに対して SELECT コマンドを実行してください。

select * from myschema.customer;

テーブルの継承の使い方(7)

親テーブルおよび親テーブルを継承したすべての子テーブルからデータを取得しました。

どのテーブルから取得したデータなのかを明示する

親テーブルおよび子テーブルからデータを取得した場合、どちらのテーブルから取得したのかが分かりません。どちらのテーブルから取得したのかを明確にするには、 tableoid という特別なカラムの値を取得します。

select name, address, tableoid from myschema.customer;

テーブルの継承の使い方(8)

tableoid はテーブル毎に割り当てられた数値なので、テーブル名で表示したい場合にはシステムカタログの pg_class を使って実際のテーブル名を取得します。

select name, address, pg_class.relname
  from myschema.customer
  join pg_class on pg_class.oid = myschema.customer.tableoid;

テーブルの継承の使い方(9)

親テーブルだけのデータおよび子テーブルだけのデータを取得する

子テーブルだけのデータを取得したい場合には、そのまま子テーブルに対して SELECT コマンドを実行してください。

select name, address, billingdate from myschema.feecustomer;

テーブルの継承の使い方(10)

親テーブルだけのデータを取得したい場合には、 SELECT コマンドで FROM のあとにテーブル名を指定する時にテーブル名の前に ONLY を付けて実行します。

select name, address from only myschema.customer;

テーブルの継承の使い方(11)

このようにテーブルを継承することで、親テーブルと子テーブルからまとめてデータを取得したり、それぞれのテーブルだけからデータを取得することができます。

親テーブルのカラムに設定した制約が継承されるかどうか

テーブルを継承すると親テーブルで定義されているカラムはすべて子テーブルに継承されますが、親テーブルでカラムに設定されていた制約は継承されるものとされないものがあります。

簡単なテストをするために次のような親テーブルを作成しました。

create table parenttbl (
  num1 integer primary key,
  num2 integer unique,
  num3 integer default 5,
  num4 integer not null,
  num5 integer check (num4 != 0)
);

親テーブルを作成後、 psql メタコマンドの \d コマンドを使ってテーブルに含まれるカラムの情報を取得してみます。

\d parenttbl

親テーブルのカラムに設定した制約が継承されるかどうか(1)

親テーブルには PRIMARY KEY 制約、 UNIQUE 制約、 DEFAULT 制約、 NOT NULL 制約、 CHECK 制約が設定されています。 num1 カラムに NOT NULL 制約が設定されているのは PRIMARY KEY 制約を設定すると自動で NOT NULL 制約も設定されるためです。

次に親テーブルを継承した子テーブルを作成します。

create table childtbl (
) inherits(parenttbl);

子テーブルを作成後、 psql メタコマンドの \d コマンドを使ってテーブルに含まれるカラムの情報を取得してみます。

\d childtbl

親テーブルのカラムに設定した制約が継承されるかどうか(2)

子テーブルには DEFAULT 制約、 NOT NULL 制約、 CHECK 制約が設定されており、PRIMARY KEY 制約や UNIQUE 制約は設定されていません。ただし PRIMARY KEY 制約によって設定された NOT NULL 制約は設定されています。

このようにテーブルを継承した場合、DEFAULT 制約、 NOT NULL 制約、 CHECK 制約はそのまま継承されますが、PRIMARY KEY 制約や UNIQUE 制約は継承されません。また外部キー制約も継承されません。

なお CHECK 制約だけは NO INHERIT を指定することで子テーブルに制約を継承されないようにすることができます。

create table parenttbl (
  num integer check (num4 != 0) no inherit
);

複数の親テーブルから継承する

テーブルの継承は複数のテーブルから継承することができます。その場合、継承する子テーブルは複数の親テーブルで定義されているすべてのカラムを継承することになります。

CREATE TABLE table_name (
  column_name data_type
) INHERITS ( parent_table_a, parent_table_b, parent_tabl_c [, ... ] )

複数の親テーブルで同じ名前のカラムが定義されている場合、カラムは統合されるため子テーブルで同じ名前のカラムが 2 つ作成されることはありませんが、同じ名前のカラムは同じデータ型である必要があります。また NOT NULL 制約や CHECK 制約はいずれかの親テーブルのカラムに設定されていた場合、子テーブルでも継承されます。

簡単なテストをするために次のような親テーブルを 2 つ作成しました。

create table parenttbl_a (
  num_1 integer,
  num_2 integer not null,
  str_a char(10)
);
create table parenttbl_b (
  num_1 integer,
  num_2 integer,
  str_b varchar(8)
);

2 つ親テーブルには num_1 と num_2 という同じ名前のカラムがあります。また num_2 カラムについては parenttbl_a テーブルの方だけに NOT NULL 制約が設定されています。

次に 2 つの親テーブルを継承した子テーブルを作成します。

create table childtbl (
  str_child varchar(15)
) inherits(parenttbl_a, parenttbl_b);

複数の親テーブルから継承する(1)

子テーブル作成時に親テーブルに同じ名前のカラムが存在するため通知が表示されます。エラーではないのでテーブルは作成されます。

子テーブルを作成後、 psql メタコマンドの \d コマンドを使って子テーブルに含まれるカラムの情報を取得してみます。

\d childtbl

複数の親テーブルから継承する(2)

子テーブルには親テーブルの両方に存在する num_1 カラムおよび num_2 カラムについては統合して継承し、それに加えてそれぞれの親テーブルにしかない str_a カラムと str_b カラム、そして子テーブルで定義した str_child カラムが含まれていることが確認できます。また統合されたカラムに対して、どちらか片方の親テーブルでだけ設定された NOT NULL 制約も子テーブルで継承されています。

このように複数のテーブルから継承してテーブルを作成することができます。

親テーブルのカラムに対する更新や削除

テーブルを継承した場合、親テーブルのカラムに対して更新や削除を行った場合、継承した子テーブル側でも反映されます。

簡単なテストをするために次のような親テーブルを作成しました。

create table parenttbl (
  id integer,
  name varchar(10)
);

次に親テーブルを継承した子テーブルを作成します。

create table childtbl (
  address varchar(10)
) inherits(parenttbl);

子テーブルを作成後、 psql メタコマンドの \d コマンドを使って子テーブルに含まれるカラムの情報を取得してみます。

\d childtbl

親テーブルのカラムに対する更新や削除(1)

親テーブルで定義されていたカラムがそのまま継承されています。

それでは ALTER TABLE コマンドを使って親テーブルのカラム名を変更してみます。

alter table parenttbl rename id to staffid;

親テーブルのカラムに対する更新や削除(2)

親テーブルの id カラムの名前を staffid に変更しました。

それでは parenttbl を継承している子テーブルのカラムに関する情報を確認してみます。

親テーブルのカラムに対する更新や削除(3)

親テーブルに対するカラム名の変更が反映されて、子テーブルに継承されていたカラムも名前が変更されているのが確認できます。

次に ALTER TABLE コマンドを使って親テーブルのカラム名を削除してみます。

alter table parenttbl drop column staffid;

親テーブルのカラムに対する更新や削除(4)

親テーブルの staffid カラムを削除しました。

それでは parenttbl を継承している子テーブルのカラムに関する情報を確認してみます。

親テーブルのカラムに対する更新や削除(5)

親テーブルに対するカラムの削除が反映されて、子テーブルに継承されていたカラムが削除されているのが確認できます。

このようにテーブルを継承した場合には、親テーブルに含まれるカラムに対して変更を行ったり削除した場合は親テーブルを継承している子テーブル側に反映されます。

-- --

テーブルを継承する方法について解説しました。

( Written by Tatsuo Ikura )

プロフィール画像

著者 / TATSUO IKURA

これから IT 関連の知識を学ばれる方を対象に、色々な言語でのプログラミング方法や関連する技術、開発環境構築などに関する解説サイトを運営しています。