1.

SQL テーブルの種類と特徴(Heap / IOT / Partition / Temporary / Materialized View)

編集
この記事の要点
  • Heap-organized Table は最も一般的な実装。行は挿入順で空き領域に格納
  • Index-organized Table (IOT) (Oracle) は主キー B-Tree 自体がデータ本体
  • パーティション表 は範囲 / ハッシュ / リストでデータを分割。大量データに必須
  • Temporary Table はセッション / トランザクション限定の一時表
  • External Table は CSV / Parquet など外部ファイルを表として参照
  • Materialized View は集計結果を物理的に保存。リフレッシュで更新
  • DBA_TABLES / INFORMATION_SCHEMA.TABLES でメタ情報を確認

テーブルの基本構造

RDB のテーブルは行 (Row / Tuple) と列 (Column / Attribute) で構成される 2 次元の集合です。物理ストレージへの格納方法はいくつかの種類があります。

Heap-organized Table (デフォルト)

ほとんどの RDB のデフォルト形式。新しい行は空きブロックに順次格納され、主キー順とは関係ありません。検索には別途インデックスが必要です。

CREATE TABLE employees (
    id    NUMBER PRIMARY KEY,
    name  VARCHAR2(100),
    dept  VARCHAR2(50),
    salary NUMBER
);
-- → Heap-organized (Oracle / PG / MySQL InnoDB を除く)

-- MySQL InnoDB は主キーで Clustered Index 化される(IOT に近い)
-- これは Heap ではない点に注意

Index-organized Table (IOT) - Oracle

Oracle 独自の機能。主キー B-Tree の葉ノード自体に行データを格納します。主キー検索が高速、ただし更新と追加列のオーバーヘッドあり。

CREATE TABLE order_items (
    order_id  NUMBER,
    seq_no    NUMBER,
    sku       VARCHAR2(50),
    qty       NUMBER,
    CONSTRAINT pk_oi PRIMARY KEY (order_id, seq_no)
)
ORGANIZATION INDEX                    -- ★ IOT 指定
PCTFREE 5
INCLUDING qty                          -- ここまでが主表、これ以降はオーバーフロー
OVERFLOW TABLESPACE app_data;

-- 用途: 主キー範囲スキャンが多い、列が少なく短い表
-- 注意: 二次索引のサイズが Heap より大きくなる傾向

MySQL の InnoDB は仕様上すべてのテーブルが IOT に近い形(主キーが Clustered Index)。SQL Server も CLUSTERED INDEX を作ると同じ挙動になります。

パーティション表 (Partitioned Table)

1 つの論理的なテーブルを、内部で複数の物理セグメントに分割します。パーティション・プルーニングでクエリが大幅高速化。

RANGE パーティション

-- Oracle
CREATE TABLE sales (
    id NUMBER, sale_date DATE, amount NUMBER
)
PARTITION BY RANGE (sale_date) (
    PARTITION p_2023 VALUES LESS THAN (DATE '2024-01-01'),
    PARTITION p_2024 VALUES LESS THAN (DATE '2025-01-01'),
    PARTITION p_2025 VALUES LESS THAN (DATE '2026-01-01'),
    PARTITION p_max  VALUES LESS THAN (MAXVALUE)
);

-- PostgreSQL (10+)
CREATE TABLE sales (id BIGINT, sale_date DATE, amount NUMERIC)
    PARTITION BY RANGE (sale_date);
CREATE TABLE sales_2024 PARTITION OF sales
    FOR VALUES FROM ('2024-01-01') TO ('2025-01-01');

-- MySQL
CREATE TABLE sales (id BIGINT, sale_date DATE, amount DECIMAL)
PARTITION BY RANGE (TO_DAYS(sale_date)) (
    PARTITION p_2023 VALUES LESS THAN (TO_DAYS('2024-01-01')),
    PARTITION p_2024 VALUES LESS THAN (TO_DAYS('2025-01-01'))
);

HASH / LIST パーティション

-- HASH: 均等分散したいとき
CREATE TABLE users (id NUMBER, name VARCHAR2(50))
PARTITION BY HASH (id) PARTITIONS 8;

-- LIST: 明示的な値のグループ
CREATE TABLE customers (id NUMBER, country VARCHAR2(2))
PARTITION BY LIST (country) (
    PARTITION p_asia    VALUES ('JP', 'CN', 'KR'),
    PARTITION p_america VALUES ('US', 'CA', 'MX'),
    PARTITION p_others  VALUES (DEFAULT)
);

-- 複合(サブパーティション)
PARTITION BY RANGE (sale_date)
SUBPARTITION BY HASH (customer_id) SUBPARTITIONS 4
(...);

Temporary Table (一時表)

セッション or トランザクション限定で存在するテーブル。中間結果の保管、複雑なバッチ処理に便利。

-- Oracle: Global Temporary Table(スキーマに常駐、データだけセッション限定)
CREATE GLOBAL TEMPORARY TABLE temp_calc (
    id NUMBER, value NUMBER
)
ON COMMIT DELETE ROWS;     -- または PRESERVE ROWS

-- PostgreSQL: セッション限定(スキーマも消える)
CREATE TEMP TABLE temp_calc (id INT, value NUMERIC);
-- セッション終了で消える
-- ON COMMIT DROP / DELETE ROWS / PRESERVE ROWS

-- MySQL
CREATE TEMPORARY TABLE temp_calc (id INT, value DECIMAL);
-- セッション限定

-- SQL Server: # で Local、## で Global
CREATE TABLE #temp_calc (id INT);    -- セッション限定
CREATE TABLE ##temp_calc (id INT);   -- 全セッション共有
区別Oracle GTTPG TEMPMySQL TEMPORARY
スキーマ定義常駐セッションセッション
データセッション or トランザクションセッションセッション
ロック / Undo少ない無し(ローカルバッファ)少ない

External Table (外部表)

CSV / Parquet / S3 上のファイルなどを「表のように SELECT」できる機能。ETL や DWH で頻出。

-- Oracle: ファイルを表として読む
CREATE TABLE ext_orders (
    id NUMBER, sku VARCHAR2(50), qty NUMBER
)
ORGANIZATION EXTERNAL (
    TYPE ORACLE_LOADER
    DEFAULT DIRECTORY data_dir
    ACCESS PARAMETERS (
        RECORDS DELIMITED BY NEWLINE
        FIELDS TERMINATED BY ','
    )
    LOCATION ('orders.csv')
);

-- PostgreSQL: foreign_data_wrapper
CREATE EXTENSION file_fdw;
CREATE SERVER files FOREIGN DATA WRAPPER file_fdw;
CREATE FOREIGN TABLE ext_log (line text) SERVER files
    OPTIONS (filename '/var/log/app.log', format 'text');

-- Snowflake / BigQuery / Redshift Spectrum 等は S3 上の Parquet を直接 SELECT 可

Materialized View (マテリアライズドビュー)

クエリ結果を物理的にテーブルとして保存するビュー。集計の高速化に絶大な効果。

-- Oracle
CREATE MATERIALIZED VIEW mv_daily_sales
BUILD IMMEDIATE
REFRESH FAST ON COMMIT          -- ★ ベース表変更で即時更新(FAST には MV LOG が必要)
AS
SELECT TRUNC(sale_date) AS day, SUM(amount) AS total
FROM   sales
GROUP BY TRUNC(sale_date);

-- PostgreSQL
CREATE MATERIALIZED VIEW mv_daily_sales AS
SELECT date_trunc('day', sale_date) AS day, SUM(amount) AS total
FROM   sales
GROUP BY 1;

-- 更新
REFRESH MATERIALIZED VIEW mv_daily_sales;
REFRESH MATERIALIZED VIEW CONCURRENTLY mv_daily_sales;   -- ロック軽減(要 unique idx)

-- MySQL は標準で MV 無し → 自前で集計テーブル + トリガで模倣

テーブル情報を確認するシステムビュー

-- Oracle
SELECT * FROM user_tables;
SELECT * FROM all_tables;
SELECT * FROM dba_tables;        -- DBA 権限必須
SELECT * FROM user_tab_columns WHERE table_name = 'EMPLOYEES';

-- PostgreSQL
SELECT * FROM information_schema.tables WHERE table_schema = 'public';
SELECT * FROM pg_tables WHERE schemaname = 'public';
\d+ users                         -- psql コマンド

-- MySQL
SHOW TABLES;
SHOW CREATE TABLE users;
SELECT * FROM information_schema.tables WHERE table_schema = DATABASE();

-- SQL Server
SELECT * FROM sys.tables;
SELECT * FROM INFORMATION_SCHEMA.TABLES;
EXEC sp_help 'users';

行サイズと PCTFREE

行サイズが大きいと 1 ブロックに収まる行数が減り、I/O 効率が落ちます。頻繁に UPDATE で行が膨らむ列構成では PCTFREE を大きく取り、Row Migration を防ぎます。

-- 良くないパターン例:
CREATE TABLE articles (
    id    NUMBER PRIMARY KEY,
    title VARCHAR2(500),
    body  CLOB                    -- 行外格納(LOB セグメント)
)
PCTFREE 30                         -- ★ UPDATE が多いので予約を多めに
INITRANS 4;                        -- 同時更新セッションが多いなら ITL 増やす

FAQ

Q: テーブルの行数を高速に知りたい
A: COUNT(*) は遅い。Oracle user_tables.num_rows(要 ANALYZE)、PG pg_class.reltuples、MySQL information_schema.tables.table_rows など統計の概算値が便利。

Q: パーティション表を後から作れる?
A: 既存表を直接パーティション化はできない(Oracle 12.2+ は ALTER TABLE ... MODIFY PARTITION BY で可能)。多くは「新テーブルを作って INSERT SELECT」。

Q: 一時表とテーブル変数の違い (SQL Server)
A: #temp はトランザクション・統計あり、@table はメモリ+簡易・統計無し。大量データは #temp が無難。

編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. 表(テーブル)
  2. 索引(インデックス)
  3. ビュー
  4. 制約
  5. 順序(シーケンス)
  6. シノニム
  7. トリガー
  8. パッケージ
  9. ストアド・ファンクション
  10. ストアド・プロシージャ
  11. ユーザー(USER)