はじめに
データベースを作成し、テーブルを定義したら、次に必要になるのが実際のデータの登録です。PostgreSQLでデータを追加する際に使用するのが「INSERT文」です。
本記事では、PostgreSQL 16におけるINSERT文の基本的な使い方から、実務で役立つ応用テクニックまでを段階的に解説します。データベース初心者の方でも、この記事を読めばINSERT文を使いこなせるようになります。
前回の記事でテーブルの作成方法を学びましたが、今回はそのテーブルに実際のデータを格納する方法を習得していきましょう。
この記事で分かること
この記事を読むことで、以下の内容が理解できるようになります。
- INSERT文の基本的な構文と使い方
- 単一行データの追加方法
- 複数行データを一度に追加する方法
- 特定の列だけにデータを追加する方法
- DEFAULT値やNULL値の扱い方
- INSERT文実行時によくあるエラーと対処法
- 実務で使える効率的なデータ追加テクニック
PostgreSQLでのデータ操作の基礎となるINSERT文を、実践的な例を交えながら丁寧に説明していきます。
稼働環境
本記事で使用する環境は以下の通りです。
- OS:Windows Server 2025(64bit)
- データベース:PostgreSQL 16
- クライアントツール:psql(PostgreSQL付属のコマンドラインツール)
なお、PostgreSQL 16の基本的なインストールと初期設定が完了していることを前提としています。
INSERT文の基本構文
INSERT文とは
INSERT文は、PostgreSQLのテーブルに新しいデータ(レコード)を追加するためのSQL文です。データベースを活用する上で最も基本的かつ重要な操作の一つです。
基本的な構文
INSERT文の基本構文は次の通りです。
INSERT INTO テーブル名 (列名1, 列名2, 列名3, ...)
VALUES (値1, 値2, 値3, ...);この構文では、どのテーブルのどの列に、どんな値を追加するかを明確に指定します。
実践:単一行データの追加
サンプルテーブルの準備
まず、データを追加するためのサンプルテーブルを作成します。従業員情報を管理する「employees」テーブルを例に進めていきましょう。
CREATE TABLE employees (
employee_id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
department VARCHAR(50),
salary INTEGER,
hire_date DATE,
email VARCHAR(100)
);このテーブルには、従業員ID、名前、部署、給与、入社日、メールアドレスの情報を格納できます。
全ての列に値を指定してデータを追加
すべての列に値を指定する場合の例です。
INSERT INTO employees (employee_id, name, department, salary, hire_date, email)
VALUES (1, '山田太郎', '営業部', 350000, '2024-04-01', 'yamada@example.com');このINSERT文を実行すると、「INSERT 0 1」というメッセージが表示され、1行のデータが正常に追加されたことが確認できます。
列名を省略した書き方
テーブルの全ての列に順番通りに値を指定する場合は、列名を省略することもできます。
INSERT INTO employees
VALUES (2, '佐藤花子', '開発部', 400000, '2024-04-15', 'sato@example.com');ただし、この方法はテーブル構造が変更された際にエラーが発生しやすいため、実務では列名を明示的に指定する方法が推奨されます。
特定の列だけに値を指定
必須項目(NOT NULL制約がある列)以外は、値を省略することができます。
INSERT INTO employees (name, department, hire_date)
VALUES ('鈴木一郎', '総務部', '2024-05-01');この場合、employee_idは自動採番(SERIAL型)、salaryとemailにはNULLが格納されます。
複数行データの一括追加
PostgreSQLでは、一度のINSERT文で複数行のデータを追加することができます。これにより、処理効率が大幅に向上します。
複数行追加の構文
INSERT INTO employees (name, department, salary, hire_date, email)
VALUES
('田中次郎', '営業部', 330000, '2024-06-01', 'tanaka@example.com'),
('高橋美咲', '開発部', 380000, '2024-06-10', 'takahashi@example.com'),
('伊藤健太', 'マーケティング部', 360000, '2024-07-01', 'ito@example.com');このように、VALUESの後にカンマ区切りで複数の値セットを指定できます。実行すると「INSERT 0 3」と表示され、3行が追加されたことが分かります。
一括追加のメリット
複数行を一度に追加する方法には以下のメリットがあります。
- データベースへのアクセス回数が減り、処理速度が向上する
- トランザクション処理が効率的になる
- SQLコードがシンプルで読みやすくなる
大量データを登録する際は、できるだけこの方法を活用しましょう。
DEFAULT値とNULLの活用
DEFAULT値の使用
テーブル作成時にDEFAULT値を設定した列は、INSERT時に明示的に値を指定しなくても、デフォルト値が自動的に設定されます。
まず、DEFAULT値を持つテーブルを作成します。
CREATE TABLE products (
product_id SERIAL PRIMARY KEY,
product_name VARCHAR(100) NOT NULL,
price INTEGER DEFAULT 0,
stock_quantity INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);DEFAULT値を利用したINSERT文の例です。
INSERT INTO products (product_name, price)
VALUES ('ノートパソコン', 85000);この場合、stock_quantityには0、created_atには現在の日時が自動的に設定されます。
DEFAULTキーワードの明示的な使用
DEFAULT値を明示的に指定することもできます。
INSERT INTO products (product_name, price, stock_quantity, created_at)
VALUES ('マウス', 2500, DEFAULT, DEFAULT);NULL値の扱い
NULL値を明示的に挿入する場合は、次のように記述します。
INSERT INTO employees (name, department, salary, hire_date, email)
VALUES ('木村舞', '開発部', NULL, '2024-08-01', NULL);ただし、NOT NULL制約が設定されている列にNULLを挿入しようとするとエラーになるので注意が必要です。
他のテーブルからデータをコピー
SELECT文の結果をそのままINSERTすることも可能です。これは既存データのコピーやバックアップ作成に便利です。
INSERT INTO SELECT構文
-- バックアップテーブルの作成
CREATE TABLE employees_backup (LIKE employees INCLUDING ALL);
-- データのコピー
INSERT INTO employees_backup
SELECT * FROM employees
WHERE department = '開発部';この方法を使えば、特定条件に合致するデータだけを別テーブルにコピーできます。
列を選択してコピー
必要な列だけを選択してコピーすることもできます。
INSERT INTO employees_backup (name, department, hire_date)
SELECT name, department, hire_date
FROM employees
WHERE salary >= 350000;RETURNING句で追加したデータを取得
PostgreSQLの便利な機能として、INSERT文の実行結果を直接取得できるRETURNING句があります。
RETURNING句の基本
INSERT INTO employees (name, department, salary, hire_date)
VALUES ('中村雄介', '営業部', 340000, '2024-09-01')
RETURNING employee_id, name, hire_date;この文を実行すると、追加されたデータのemployee_id、name、hire_dateが結果として返されます。自動採番されたIDを即座に取得したい場合などに非常に便利です。
複数行追加時のRETURNING
複数行を追加した際も、すべての行の情報を取得できます。
INSERT INTO employees (name, department, salary, hire_date)
VALUES
('小林あゆみ', 'マーケティング部', 355000, '2024-09-10'),
('加藤誠', '総務部', 325000, '2024-09-15')
RETURNING *;アスタリスク(*)を使用すると、追加されたすべての列の値が返されます。
データ型と値の指定方法
文字列型(VARCHAR, TEXT)
文字列はシングルクォート(’)で囲みます。
INSERT INTO employees (name, email)
VALUES ('渡辺拓也', 'watanabe@example.com');文字列内にシングルクォートを含める場合は、2つ重ねて記述します。
INSERT INTO employees (name)
VALUES ('O''Brien');数値型(INTEGER, NUMERIC)
数値はクォートなしで記述します。
INSERT INTO employees (salary)
VALUES (375000);日付・時刻型(DATE, TIMESTAMP)
日付や時刻はシングルクォートで囲んで指定します。
INSERT INTO employees (hire_date)
VALUES ('2024-10-01');
-- 時刻を含む場合
INSERT INTO products (created_at)
VALUES ('2024-10-01 14:30:00');現在の日時を挿入する場合は、関数を使用します。
INSERT INTO products (product_name, created_at)
VALUES ('キーボード', CURRENT_TIMESTAMP);真偽値型(BOOLEAN)
真偽値は、TRUE/FALSE、’t’/’f’、’yes’/’no’、1/0などで指定できます。
CREATE TABLE users (
user_id SERIAL PRIMARY KEY,
username VARCHAR(50),
is_active BOOLEAN DEFAULT TRUE
);
INSERT INTO users (username, is_active)
VALUES ('tanaka_user', TRUE);エスケープと特殊文字の扱い
シングルクォートのエスケープ
前述の通り、シングルクォートを2つ重ねることでエスケープできます。
INSERT INTO employees (name)
VALUES ('D''Angelo');バックスラッシュを含む文字列
PostgreSQLでは、標準SQLの文字列リテラルを使用する場合、バックスラッシュは特別な処理は不要です。
INSERT INTO file_paths (path)
VALUES ('C:\\Program Files\\PostgreSQL\\16');ただし、エスケープ文字列を使う場合(E’…’構文)は、バックスラッシュを2つ重ねます。
INSERT INTO file_paths (path)
VALUES (E'C:\\\\Program Files\\\\PostgreSQL\\\\16');INSERT文のパフォーマンス最適化
一括INSERTの活用
前述の通り、複数行を一度に追加することで大幅な性能向上が期待できます。
-- 非効率な方法(複数回実行)
INSERT INTO employees (name, department) VALUES ('社員A', '営業部');
INSERT INTO employees (name, department) VALUES ('社員B', '開発部');
INSERT INTO employees (name, department) VALUES ('社員C', '総務部');
-- 効率的な方法(1回で実行)
INSERT INTO employees (name, department)
VALUES
('社員A', '営業部'),
('社員B', '開発部'),
('社員C', '総務部');トランザクションの活用
大量データを追加する場合は、明示的にトランザクションを使用することで性能が向上します。
BEGIN;
INSERT INTO employees (name, department, salary, hire_date)
VALUES
('社員D', 'マーケティング部', 340000, '2024-11-01'),
('社員E', '営業部', 335000, '2024-11-05'),
('社員F', '開発部', 390000, '2024-11-10');
-- さらに多くのデータ...
COMMIT;トランザクション内で処理することで、すべてのINSERTが成功した場合のみデータが確定されます。
COPYコマンドの利用
CSV形式などの大量データを一括で取り込む場合は、COPYコマンドが最も高速です。
COPY employees (name, department, salary, hire_date)
FROM 'C:\\data\\employees.csv'
WITH (FORMAT csv, HEADER true, ENCODING 'UTF8');これはINSERT文よりもはるかに高速にデータを取り込むことができます。
つまずきポイントと解決策
エラー1:列数と値の数が一致しない
エラーメッセージ例:
ERROR: INSERT has more expressions than target columns原因: 指定した列の数と、VALUESで指定した値の数が一致していません。
解決策: 列名と値の数を確認し、一致させます。
-- エラーになる例
INSERT INTO employees (name, department)
VALUES ('山本太郎', '営業部', 350000); -- 値が3つで列が2つ
-- 正しい例
INSERT INTO employees (name, department, salary)
VALUES ('山本太郎', '営業部', 350000);エラー2:NOT NULL制約違反
エラーメッセージ例:
ERROR: null value in column "name" violates not-null constraint原因: NOT NULL制約が設定されている列に値を指定していません。
解決策: 必須項目には必ず値を指定します。
-- エラーになる例
INSERT INTO employees (department, salary)
VALUES ('営業部', 350000); -- nameが未指定
-- 正しい例
INSERT INTO employees (name, department, salary)
VALUES ('吉田健一', '営業部', 350000);エラー3:データ型の不一致
エラーメッセージ例:
ERROR: invalid input syntax for type integer: "abc"原因: 列のデータ型と指定した値の型が一致していません。
解決策: 適切なデータ型で値を指定します。
-- エラーになる例
INSERT INTO employees (salary)
VALUES ('三十五万円'); -- 数値型に文字列
-- 正しい例
INSERT INTO employees (salary)
VALUES (350000);エラー4:主キー(UNIQUE制約)の重複
エラーメッセージ例:
ERROR: duplicate key value violates unique constraint "employees_pkey"原因: すでに存在する主キーまたはユニーク制約の値を挿入しようとしています。
解決策: SERIAL型を使用している場合は、employee_idを省略して自動採番に任せます。
-- エラーになる可能性がある例
INSERT INTO employees (employee_id, name)
VALUES (1, '林美穂'); -- すでにemployee_id=1が存在
-- 正しい例(自動採番を利用)
INSERT INTO employees (name)
VALUES ('林美穂');エラー5:外部キー制約違反
エラーメッセージ例:
ERROR: insert or update on table "orders" violates foreign key constraint原因: 外部キーで参照している値が、参照先のテーブルに存在しません。
解決策: 参照先のテーブルに該当する値が存在することを確認します。
-- まず参照先のデータを確認
SELECT employee_id FROM employees WHERE employee_id = 999;
-- 存在する値を指定
INSERT INTO orders (employee_id, order_date)
VALUES (1, '2024-10-01'); -- employee_id=1が存在する場合エラー6:日付フォーマットの誤り
エラーメッセージ例:
ERROR: invalid input syntax for type date: "2024/10/01"原因: 日付の形式が不適切です。PostgreSQLは通常、’YYYY-MM-DD’形式を期待します。
解決策: 適切な日付形式で指定します。
-- エラーになる例
INSERT INTO employees (hire_date)
VALUES ('2024/10/01');
-- 正しい例
INSERT INTO employees (hire_date)
VALUES ('2024-10-01');データ追加の確認方法
INSERT文を実行した後は、正しくデータが追加されたか確認することが重要です。
SELECT文での確認
-- 全データの確認
SELECT * FROM employees;
-- 最近追加されたデータの確認(employee_idが最大のもの)
SELECT * FROM employees
ORDER BY employee_id DESC
LIMIT 5;
-- 特定条件でのデータ確認
SELECT * FROM employees
WHERE hire_date >= '2024-09-01';COUNT関数でのデータ件数確認
-- テーブルの総レコード数
SELECT COUNT(*) FROM employees;
-- 部署別のレコード数
SELECT department, COUNT(*)
FROM employees
GROUP BY department;実務で役立つINSERT文のテクニック
ON CONFLICT句(UPSERT)
PostgreSQL 9.5以降では、ON CONFLICT句を使用して、重複時の動作を制御できます。
-- 重複時は何もしない
INSERT INTO employees (employee_id, name, department)
VALUES (1, '更新太郎', '営業部')
ON CONFLICT (employee_id) DO NOTHING;
-- 重複時は更新する
INSERT INTO employees (employee_id, name, department, salary)
VALUES (1, '更新太郎', '営業部', 360000)
ON CONFLICT (employee_id)
DO UPDATE SET
name = EXCLUDED.name,
department = EXCLUDED.department,
salary = EXCLUDED.salary;この機能は「UPSERT(UPdate + inSERT)」と呼ばれ、データの存在チェックと追加/更新を一度に行えます。
WITH句(CTE)との組み合わせ
複雑なデータ加工を行ってからINSERTする場合、WITH句が便利です。
WITH new_employees AS (
SELECT
'新入社員' || generate_series(1, 5) AS name,
'研修部' AS department,
300000 AS salary,
CURRENT_DATE AS hire_date
)
INSERT INTO employees (name, department, salary, hire_date)
SELECT * FROM new_employees;シーケンスの現在値確認
SERIAL型で自動採番された最新の値を確認する方法です。
-- 最後に使用されたシーケンス値を確認
SELECT currval('employees_employee_id_seq');
-- 次に使用されるシーケンス値を確認(値は進む)
SELECT nextval('employees_employee_id_seq');まとめ
本記事では、PostgreSQL 16におけるINSERT文の基本から応用まで、幅広く解説してきました。
重要なポイントをおさらいしましょう。
INSERT文はデータベースにデータを追加する基本操作であり、正しい構文と適切なデータ型の理解が不可欠です。単一行の追加だけでなく、複数行を一度に追加することで処理効率を大幅に向上させることができます。
DEFAULT値やNULL値の扱い、RETURNING句による追加データの即座取得など、PostgreSQL固有の便利な機能も積極的に活用しましょう。エラーが発生した際は、エラーメッセージをよく読み、列数や データ型、制約条件を確認することで、ほとんどの問題は解決できます。
実務では、ON CONFLICT句を使ったUPSERT処理や、トランザクションを活用した大量データの効率的な追加など、状況に応じた最適な方法を選択することが重要です。
次回の記事では、追加したデータを検索・抽出するSELECT文について詳しく解説します。INSERT文でデータを追加できるようになったら、次はそのデータを自在に取り出す技術を身につけていきましょう。