コマンド道場

CREATE TABLE(表を作る)の使い方

書いた文と、実際にできた表の姿

できた表 mokuhyou

NULL制約
busho_idinteger不可主キー
kingakunumeric(10,0)不可
memotext

この表は文を実際に流して作り、データベース自身が持つ表の情報を読んで描いています。int と書いた列の型が integer になりPRIMARY KEY と書いただけの busho_id が NULL 不可になっていることに注目してください。書いたことと、実際に付く定義は同じではありません。

CREATE TABLE は、これから入る値の約束を決める文です。 列の名前と型を並べるだけでなく、NOT NULL・既定値・主キー・外部キーを書いておくと、おかしなデータが入る前にデータベースが止めてくれます。⚠️ ここで決めたことはあとから変えにくい——データが入ってから型を変えるのは移行作業になります。

この書き方が使えない製品があります そのまま使える: PostgreSQL / MySQL / MariaDB / SQLite

  • SQL Server では使えません問い合わせの結果から表を作る(CREATE TABLE ... AS SELECT)
  • Oracle Database では使えませんtext 型の列 / この問題のほかの構文は未確認です

未確認: IBM Db2

製品ごとの対応表(根拠つき)を見る

このページの実行結果と図は、すべて本物のデータベースに同じ SQL を流して得たものです。手で書いた「たぶんこうなる」ではありません。

01列の名前と型を決める

結論: CREATE TABLE 表名 (列名 型, 列名 型, …) で表ができます。

型は「その列に入れてよい値」の約束です。数を入れる列に文字の型を選ぶと、あとで合計や比較ができません。逆に、電話番号や郵便番号のように計算しない数字は文字の型にします(先頭の 0 が消えるため)。

金額には numeric を使ってください。桁数と小数点以下の桁を指定でき、丸め方が決まっているためです。realdouble precision は速い代わりに誤差が出るので、お金には向きません。

⚠️ 型の名前は製品で違います。 PostgreSQL の text は Oracle には無く、VARCHAR2CLOB になります。int のような標準的な型はだいたい通ります。

1 行ずつ、何を決めているか
  1. CREATE TABLE mokuhyou (作る表の名前
  2. busho_id int PRIMARY KEY,★列名・型・制約の順に書く
  3. kingaku numeric(10,0) NOT NULL,★桁を指定できる型
  4. memo text最後の列にカンマは付けない
  5. );括弧を閉じて終わり

列と列はカンマで区切ります。最後の列のうしろにカンマを書くとエラーになるので、行を足し引きしたときによく落とします。

表を作って、できた定義を確かめる
CREATE TABLE mokuhyou (
  busho_id int PRIMARY KEY,
  kingaku  numeric(10,0) NOT NULL,
  memo     text
);

SELECT column_name, data_type, is_nullable
FROM information_schema.columns
WHERE table_name = 'mokuhyou'
ORDER BY ordinal_position;
column_namedata_typeis_nullable
busho_idintegerNO
kingakunumericNO
memotextYES

3

information_schema はデータベース自身が持つ「表の情報」です。書いた intinteger になりPRIMARY KEY を書いた busho_idis_nullableNO になっています。

⚠️ この書き方は Oracle Database では使えません(text 型の列)。製品ごとの対応表

02NOT NULL と既定値は別のもの

結論: NOT NULL は「NULL を入れさせない」、DEFAULT は「書かなかったときに何を入れるか」です。

この 2 つはよく混同されますが、守っているものが違います。既定値だけを付けた列は、INSERT明示的に NULL を渡せば NULL が入ります。「必ず値がある」を保証したいなら、既定値とは別に NOT NULL を付けてください。

逆に NOT NULL だけを付けて既定値を書かないと、その列を省いた INSERT はエラーになります。毎回書かせたいのか、省けるようにしたいのかで決めます。

⚠️ NULL は「0」でも「空文字」でもなく、値が無いことです。合計や比較の対象から外れるので、集計が合わない原因になります。

列を書かないと既定値が入る
CREATE TABLE mokuhyou (
  busho_id int PRIMARY KEY,
  kingaku  numeric(10,0) NOT NULL DEFAULT 0,
  memo     text
);

INSERT INTO mokuhyou (busho_id) VALUES (2);

SELECT busho_id, kingaku, memo FROM mokuhyou;
busho_idkingakumemo
20NULL

1

kingaku を書かなかったので既定値の 0 が入りました。memo は既定値を決めていないので NULL です。「書かなかった」の結果が列によって違うことが分かります。

⚠️ この書き方は Oracle Database では使えません(text 型の列)。製品ごとの対応表

既定値があっても、明示した NULL は入らない
CREATE TABLE mokuhyou (
  busho_id int PRIMARY KEY,
  kingaku  numeric(10,0) NOT NULL DEFAULT 0
);

INSERT INTO mokuhyou (busho_id, kingaku) VALUES (2, NULL);
ERROR: null value in column "kingaku" of relation "mokuhyou" violates not-null constraint

「null value in column "kingaku" … violates not-null constraint」で落ちます。既定値が効くのは列を書かなかったときだけで、NULL と書けば NULL を入れようとします。

03⚠️ よくある間違い:主キーと外部キーで、おかしなデータを止める

結論: PRIMARY KEY は「行を 1 つに決める列」、REFERENCES は「相手の表に実在する値だけを許す」です。

主キーを書くと、重複した値も NULL も入らなくなります(NOT NULL を書かなくても付きます)。外部キーを書くと、相手の表に無い値を入れようとしたときにデータベースが拒否します。アプリ側の検査を忘れても、最後の砦になります。

⚠️ 外部キーを付けると、参照されている親の行を消せなくなります(既定は拒否)。消し方まで決めてから付けてください。

⚠️ 外部キーが指す相手の列には、主キーか一意制約が必要です。ただの列は指せません。

主キーと外部キーを書いた表の姿

できた表 mokuhyou

NULL既定値制約
busho_idinteger不可主キー・外部キー → busho.busho_id・親を消せない
kingakunumeric(10,0)不可0
memotext

busho_id には主キーと外部キーが両方付いています。NOT NULL と書いていないのに NULL 不可になっているのは、主キーだからです。

相手の表に無い値は入らない
CREATE TABLE mokuhyou (
  busho_id int PRIMARY KEY REFERENCES busho(busho_id),
  kingaku  numeric(10,0) NOT NULL DEFAULT 0
);

INSERT INTO mokuhyou (busho_id) VALUES (99);
ERROR: insert or update on table "mokuhyou" violates foreign key constraint "mokuhyou_busho_id_fkey"

「violates foreign key constraint」で落ちます。busho に 99 番の部署が無いためです。入る前に止まるので、あとから辻褄合わせをしなくて済みます。

04問い合わせの結果から表を作る

結論: CREATE TABLE 表名 AS SELECT … で、問い合わせの結果をそのまま表にできます。

列の名前と型は SELECT から受け継ぎます。集計した結果を取っておきたいときや、作業用の表がほしいときに便利です。

⚠️ 制約は引き継ぎません。 元の表で主キーや NOT NULL が付いていた列でも、この書き方で作った表では NULL 可のただの列になります。必要なら作ったあとで ALTER TABLE で足します。

⚠️ 元の表が変わっても追随しません。 作った時点の写しなので、常に最新にしたいならビューを使ってください。

問い合わせから作った表の姿

できた表 zaiseki

NULL制約
shain_idinteger不可
nametext
salarynumeric(10,0)

型は shain から受け継いでいますが、主キーは付いていません。ここでは ALTER TABLEshain_id にだけ NOT NULL を足しています。制約は自分で足すもの、と覚えてください。

在籍中の社員だけを表にする
CREATE TABLE zaiseki AS
SELECT shain_id, name, salary
FROM shain
WHERE taishoku_on IS NULL;

SELECT count(*) AS kensu FROM zaiseki;
kensu
16

1

18 人のうち在籍中の 16 人だけが入りました。WHERE で絞った結果が、そのまま新しい表になります。

⚠️ この書き方は SQL Server では使えません(問い合わせの結果から表を作る(CREATE TABLE ... AS SELECT))。製品ごとの対応表

自分で打ってみる

このページの例と同じデータが入った本物のデータベースを、この場(あなたのブラウザの中)で起動できます。打った SQL がサーバーへ送られることはありません。

押すと初回だけ約 9MB を読み込みます(2 回目以降はキャッシュされます)。

解いてみる

読んだだけでは書けるようになりません。実行結果で採点します。

関連するトピック

  • INSERT作った表に行を入れる操作です。制約に反したときのエラーもこちらに書いています。
  • IS NULLNOT NULL を決めるときに知っておきたい NULL の扱いはこちらです。
  • SELECT 句CREATE TABLE ... AS SELECT の SELECT 側はこちらです。
  • DELETE外部キーで親の行が消せなくなる話はこちらと合わせて読んでください。

根拠(一次情報)