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)

最近更新/作成されたページ