この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:6
ページ更新者:atom
更新日時:2026-06-11 07:07:02

タイトル: DDL
SEOタイトル: SQL DDL (CREATE/ALTER/DROP) 完全ガイド

この記事の要点
  • DDL (Data Definition Language) はデータベースの構造を定義する SQL の総称
  • 主要コマンド: CREATE / ALTER / DROP / TRUNCATE / RENAME / COMMENT
  • 対象オブジェクト: テーブル / インデックス / ビュー / プロシージャ / トリガー / 関数 / スキーマ
  • DDL は多くの DB で自動コミット(途中までの変更を ROLLBACK できない、PostgreSQL は例外)
  • MySQL 8 / PostgreSQL の Online DDL でテーブルロック無しに ALTER 可能

DDL とは

DDL (Data Definition Language: データ定義言語) は、データベースのスキーマ構造(テーブル・インデックス・ビュー等)を定義・変更・削除する SQL コマンドの総称です。データ自体を操作する DML や、権限を操作する DCL とは区別されます。

区分正式名称主なコマンド役割
DDLData Definition LanguageCREATE / ALTER / DROP / TRUNCATE / RENAMEスキーマ定義
DMLData Manipulation LanguageSELECT / INSERT / UPDATE / DELETEデータ操作
DCLData Control LanguageGRANT / REVOKE権限制御
TCLTransaction Control LanguageCOMMIT / ROLLBACK / SAVEPOINTトランザクション

主要 DDL コマンド一覧

コマンド機能
CREATEオブジェクト作成CREATE TABLE users (...)
ALTERオブジェクト変更ALTER TABLE users ADD email VARCHAR(255)
DROPオブジェクト削除DROP TABLE users
TRUNCATE全行削除(高速・ロールバック不可)TRUNCATE TABLE logs
RENAME名前変更RENAME TABLE old TO new
COMMENTコメント付与(PG/Oracle)COMMENT ON TABLE users IS 'ユーザー'

CREATE TABLE: テーブル定義の基本

CREATE TABLE users (
    id              BIGINT          NOT NULL AUTO_INCREMENT,
    email           VARCHAR(255)    NOT NULL,
    name            VARCHAR(100)    NOT NULL DEFAULT '',
    age             INT             CHECK (age >= 0 AND age <= 150),
    status          ENUM('active', 'inactive') NOT NULL DEFAULT 'active',
    created_at      DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP,
    updated_at      DATETIME        NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at      DATETIME        NULL,
    PRIMARY KEY (id),
    UNIQUE  KEY uq_users_email (email),
    KEY     idx_users_status (status, created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

カラム定義の主な属性

属性意味
NOT NULLNULL を禁止name VARCHAR(100) NOT NULL
DEFAULT 値既定値status INT DEFAULT 0
CHECK値の制約age INT CHECK (age >= 0)
UNIQUE一意制約email VARCHAR(255) UNIQUE
PRIMARY KEY主キーid BIGINT PRIMARY KEY
FOREIGN KEY外部キーFOREIGN KEY (user_id) REFERENCES users(id)
AUTO_INCREMENT / SERIAL自動採番id BIGINT AUTO_INCREMENT

FOREIGN KEY (外部キー)

CREATE TABLE posts (
    id          BIGINT          NOT NULL AUTO_INCREMENT,
    user_id     BIGINT          NOT NULL,
    title       VARCHAR(255)    NOT NULL,
    PRIMARY KEY (id),
    CONSTRAINT fk_posts_user
        FOREIGN KEY (user_id)
        REFERENCES users(id)
        ON DELETE CASCADE         -- 親削除時に子も削除
        ON UPDATE CASCADE         -- 親更新時に子も更新
) ENGINE=InnoDB;

-- ON DELETE / ON UPDATE のオプション
--   CASCADE     : 親に連動
--   SET NULL    : NULL に
--   RESTRICT    : 子があれば親削除を拒否(デフォルト)
--   NO ACTION   : RESTRICT と同等
--   SET DEFAULT : DEFAULT 値に

ALTER TABLE: スキーマ変更

-- カラム追加
ALTER TABLE users ADD COLUMN phone VARCHAR(20) NULL AFTER email;

-- カラム削除
ALTER TABLE users DROP COLUMN phone;

-- カラム型変更
ALTER TABLE users MODIFY COLUMN name VARCHAR(200) NOT NULL;

-- カラム名変更
ALTER TABLE users CHANGE COLUMN name full_name VARCHAR(200) NOT NULL;

-- インデックス追加
ALTER TABLE users ADD INDEX idx_email (email);
ALTER TABLE users ADD UNIQUE KEY uq_email (email);

-- インデックス削除
ALTER TABLE users DROP INDEX idx_email;

-- 外部キー追加
ALTER TABLE posts
    ADD CONSTRAINT fk_posts_user
    FOREIGN KEY (user_id) REFERENCES users(id);

-- 外部キー削除
ALTER TABLE posts DROP FOREIGN KEY fk_posts_user;

インデックス・ビュー・プロシージャ

-- INDEX
CREATE INDEX idx_users_status ON users(status, created_at);
DROP INDEX idx_users_status ON users;

-- VIEW
CREATE VIEW active_users AS
SELECT id, email, name FROM users WHERE status = 'active';

CREATE OR REPLACE VIEW v_user_post_count AS
SELECT u.id, u.name, COUNT(p.id) AS post_count
FROM users u LEFT JOIN posts p ON p.user_id = u.id
GROUP BY u.id, u.name;

DROP VIEW active_users;

-- STORED PROCEDURE (MySQL)
DELIMITER //
CREATE PROCEDURE add_user(IN p_email VARCHAR(255), IN p_name VARCHAR(100))
BEGIN
    INSERT INTO users (email, name) VALUES (p_email, p_name);
END //
DELIMITER ;

CALL add_user('foo@example.com', 'Foo');

-- TRIGGER
CREATE TRIGGER before_user_update
BEFORE UPDATE ON users
FOR EACH ROW
SET NEW.updated_at = NOW();

-- FUNCTION
CREATE FUNCTION user_count() RETURNS INT
DETERMINISTIC
BEGIN
    DECLARE c INT;
    SELECT COUNT(*) INTO c FROM users;
    RETURN c;
END;

DROP と TRUNCATE の違い

項目DROP TABLETRUNCATE TABLEDELETE FROM
区分DDLDDLDML
対象テーブル自体全行条件指定可
速度速い速い遅い(行ごとに削除)
ROLLBACK不可(多くの DB)不可(多くの DB)
AUTO_INCREMENTテーブル消滅リセット維持
トリガー消滅発火しない発火する

トランザクション中の DDL の挙動

多くの DB で DDL は暗黙的に自動コミットされます。これは「途中まで実行した DDL を ROLLBACK で取り消せない」ことを意味します。

-- MySQL / Oracle: DDL は暗黙コミット
BEGIN;
INSERT INTO users (email) VALUES ('a@b.c');  -- DML
ALTER TABLE users ADD COLUMN age INT;        -- DDL ← この時点で INSERT が暗黙 COMMIT
ROLLBACK;                                    -- ALTER は取り消せない / INSERT も既に COMMIT 済

-- PostgreSQL: DDL もトランザクション内に
BEGIN;
INSERT INTO users (email) VALUES ('a@b.c');
ALTER TABLE users ADD COLUMN age INT;
ROLLBACK;  -- INSERT も ALTER も取り消される ★

Online DDL (MySQL 8 / PostgreSQL)

巨大テーブルの ALTERテーブルロックを取って数時間〜数日かかることがあります。MySQL 8 / PostgreSQL はサービス無停止で ALTERできるアルゴリズムを提供します。

-- MySQL 8 の Online DDL
ALTER TABLE users
    ADD COLUMN email VARCHAR(255),
    ALGORITHM=INPLACE,    -- INSTANT / INPLACE / COPY
    LOCK=NONE;            -- NONE / SHARED / EXCLUSIVE

-- ALGORITHM の意味
--   INSTANT  : メタデータのみ更新(最速、対応操作限定)
--   INPLACE  : テーブル再構築するが行ロック取らない
--   COPY     : 旧形式(テーブル全体ロック、長時間)

-- PostgreSQL: CONCURRENTLY でインデックス追加(行ロックなし)
CREATE INDEX CONCURRENTLY idx_users_email ON users(email);

-- 巨大テーブルは pt-online-schema-change (Percona) や gh-ost (GitHub) で
-- ダウンタイム無しに ALTER 推奨

FAQ

Q: DROP TABLE と DROP TABLE IF EXISTS の違いは?
A: IF EXISTS 付けると、テーブルが無くてもエラーにならず警告のみ。マイグレーションスクリプトで安全に使えます。

Q: TRUNCATE は本当に高速?
A: はい。テーブルファイルを再作成する実装が多く、行数によらず一定時間です。ただし外部キー参照されていると失敗することがあります。

Q: DDL の途中で失敗したらどうなる?
A: MySQL では中途半端な状態が残ることがあります。PostgreSQL ならトランザクション内 DDL なので自動 ROLLBACK されます。