【完全ガイド】Oracle LOB(BLOB/CLOB)操作 徹底解説|DBMS_LOB・SecureFiles・暗号化まで

【完全ガイド】Oracle LOB(BLOB/CLOB)操作 徹底解説|DBMS_LOB・SecureFiles・暗号化まで

Oracle で大容量データを扱う際の必須スキル、LOB(Large Object):

CREATE TABLE documents (
  doc_id     NUMBER PRIMARY KEY,
  doc_title  VARCHAR2(200),
  doc_body   CLOB,     -- 大容量テキスト
  doc_image  BLOB      -- バイナリ画像
) LOB (doc_body, doc_image) STORE AS SECUREFILE;

LOB は最大 128 TBまで格納可能。しかし、通常のカラムと同じ感覚で扱うと問題:

  • メモリを圧迫する(大容量を一括ロード)
  • REDO ログが肥大化
  • バックアップ・リストアに時間
  • ネットワーク転送のボトルネック
  • アプリ側でストリーミングが必須
  • DBMS_LOB の理解が必須
  • SecureFiles vs BasicFilesの選択
  • 圧縮・重複排除・暗号化の活用

現場では:

  • 画像・PDF・動画の格納
  • JSON/XML の大容量ドキュメント
  • ログデータ・監査データ
  • メールアーカイブ
  • 文書管理システム
  • Web アプリのファイルアップロード
  • AI・機械学習モデルの保存

さらに、Oracle 11g で導入された SecureFiles は BasicFiles を完全に置き換えるものですが、日本語圏で体系的な解説がほぼないのが現状:

  • SecureFiles と BasicFiles の違いを知らない
  • DBMS_LOB.LOADCLOBFROMFILE の使い方
  • 一時 LOB(Temporary LOB) の存在
  • CACHE vs NOCACHE の判断
  • INLINE vs OUT OF ROW の設計
  • Rails/Java/Python でのストリーミング
  • Oracle Text による全文検索

本記事では、Oracle LOB(BLOB/CLOB)操作完全ガイドを、リファレンスとして実用的に整理します。4種類の LOB 型、テーブル作成、CRUD 操作、DBMS_LOB パッケージ、SecureFiles、圧縮・暗号化、パフォーマンスチューニング、Rails/Java/Python 対応、Oracle Text、実践シナリオ、FAQまで完全網羅。この1本で Oracle LOB を根本から使いこなせるようになります。


目次

結論:BLOB / CLOB の使い分け

時間がない方向けに、最速の理解を示します。

4種類の LOB

用途
BLOBバイナリ画像、PDF、動画
CLOB文字(DB キャラセット)大容量テキスト、JSON、XML
NCLOBUnicode 文字多言語テキスト
BFILE外部ファイル参照外部ストレージ

最小限のセットアップ

-- ✅ 現代的(SecureFiles)
CREATE TABLE docs (
  id     NUMBER PRIMARY KEY,
  body   CLOB
) LOB (body) STORE AS SECUREFILE (
  ENABLE STORAGE IN ROW  -- 小さいものはインライン
  COMPRESS HIGH           -- 圧縮
  DEDUPLICATE             -- 重複排除
);

5大操作

-- ① INSERT
INSERT INTO docs (id, body) VALUES (1, 'text content...');

-- ② SELECT
SELECT body FROM docs WHERE id = 1;

-- ③ UPDATE
UPDATE docs SET body = 'new content' WHERE id = 1;

-- ④ サイズ取得
SELECT DBMS_LOB.GETLENGTH(body) FROM docs;

-- ⑤ 部分読み取り
SELECT DBMS_LOB.SUBSTR(body, 100, 1) FROM docs;

覚えるべき 3 つのポイント

1. LOB は SecureFiles で作成(11g 以降推奨)
2. 大容量は DBMS_LOB でストリーミング
3. アプリ側で少しずつ読み書き(メモリ節約)

詳細は以下で解説します。


まず理解する:LOB の全体像

LOB とは

Large Object の略、大容量データを格納する型:

  • 最大 128 TB(DB ブロックサイズ 32K 時)
  • テーブル外に格納(デフォルト)
  • ロケータ(LOB Locator) で参照
  • 専用 API(DBMS_LOB)で操作

従来型との違い

最大サイズ特徴
VARCHAR24000 バイト(32K で拡張可)通常の文字列
RAW2000 バイトバイナリ小
LONG2 GB非推奨
LONG RAW2 GB非推奨
CLOB128 TB推奨(文字)
BLOB128 TB推奨(バイナリ)

LONG は非推奨

-- ❌ LONG(Oracle 8i 以前の旧型)
CREATE TABLE t (col LONG);
-- 制限多数、LOB への移行推奨

-- ✅ CLOB / BLOB
CREATE TABLE t (col CLOB);

LONG の制限:

  • テーブルあたり1つのみ
  • SUBSTR できない
  • インデックス不可
  • 分散処理不可

LOB Locator

LOB の実データは別領域、テーブルの列には**ロケータ(参照)**のみ:

[テーブル行]
  ├─ id: 1
  ├─ title: '...'
  └─ body_lob: [ロケータ] → 実データ(LOB セグメント)

行のサイズを抑えるため。


4種類の LOB 詳細

BLOB(バイナリ)

CREATE TABLE files (
  file_id NUMBER PRIMARY KEY,
  content BLOB
);

用途:

  • 画像(JPEG, PNG, GIF)
  • PDF、Excel、Word
  • 動画、音声
  • ZIP アーカイブ
  • 暗号化データ

CLOB(文字)

CREATE TABLE articles (
  article_id NUMBER PRIMARY KEY,
  body       CLOB   -- DB キャラセット
);

用途:

  • 記事本文
  • JSON ドキュメント
  • XML
  • ソースコード
  • ログテキスト

NCLOB(Unicode 文字)

CREATE TABLE i18n_docs (
  doc_id  NUMBER PRIMARY KEY,
  content NCLOB   -- 国際キャラセット(AL16UTF16 等)
);

用途:

  • 多言語テキスト
  • CJK(中国・日本・韓国)
  • 絵文字対応

注意: DB キャラセットが AL32UTF8 なら CLOB で十分。

BFILE(外部ファイル)

-- ディレクトリ登録
CREATE DIRECTORY doc_dir AS '/u01/documents';

CREATE TABLE ext_files (
  file_id  NUMBER PRIMARY KEY,
  ext_ref  BFILE
);

INSERT INTO ext_files 
VALUES (1, BFILENAME('DOC_DIR', 'sample.pdf'));

特徴:

  • 参照のみ(DB 内に格納しない)
  • 読み取り専用
  • バックアップ・レプリケーション対象外

用途: 大容量ファイル、外部連携。ディレクトリ設定の詳細は Oracle Directory 確認の記事も参照してください。


テーブル作成の詳細

基本形(BasicFiles)

-- 古い形式(BasicFiles)
CREATE TABLE t (id NUMBER, body CLOB);

Oracle 11g+ でもデフォルトは BasicFiles。SecureFiles を明示的に指定推奨。

SecureFiles(推奨)

CREATE TABLE t (
  id   NUMBER PRIMARY KEY,
  body CLOB
) LOB (body) STORE AS SECUREFILE (
  ENABLE STORAGE IN ROW  -- インライン化
  CHUNK 8192              -- 8K チャンク
  RETENTION AUTO          -- 自動保持
  CACHE                   -- バッファキャッシュ利用
  COMPRESS HIGH           -- 圧縮(Advanced Compression 必要)
  DEDUPLICATE             -- 重複排除
  ENCRYPT                 -- 暗号化(TDE 必要)
);

主要オプション

STORAGE:

ENABLE STORAGE IN ROW   -- 3964 バイト以下はインライン
DISABLE STORAGE IN ROW  -- 常にアウトオブライン

CHUNK:

CHUNK 8192   -- LOB の物理単位(DB ブロックサイズの倍数)

LOGGING:

LOGGING       -- REDO 記録(デフォルト)
NOLOGGING     -- REDO なし(高速だが復旧不可)
FILESYSTEM_LIKE_LOGGING  -- SecureFiles 専用、メタデータのみ

CACHE:

CACHE        -- バッファキャッシュ利用(頻繁アクセス)
NOCACHE      -- 直接読み書き(大容量、一回限り)
CACHE READS  -- 読み取りのみキャッシュ

基本 CRUD 操作

INSERT

方法A: リテラル(4000 バイト以下):

INSERT INTO docs (id, body) 
VALUES (1, 'Small text content');

方法B: EMPTY_CLOB / EMPTY_BLOB:

-- 空の LOB を作成、後で書き込み
INSERT INTO docs (id, body) 
VALUES (1, EMPTY_CLOB())
RETURNING body INTO :lob_locator;

-- ロケータで書き込み
DBMS_LOB.WRITE(:lob_locator, ...);

方法C: PL/SQL 変数:

DECLARE
  v_body CLOB := 'Long text content...';
BEGIN
  INSERT INTO docs (id, body) VALUES (1, v_body);
END;
/

SELECT

方法A: 直接(メモリ許容範囲):

SELECT body FROM docs WHERE id = 1;
-- 全内容をロード

方法B: DBMS_LOB で部分読み取り:

DECLARE
  v_lob    CLOB;
  v_chunk  VARCHAR2(4000);
  v_offset INTEGER := 1;
BEGIN
  SELECT body INTO v_lob FROM docs WHERE id = 1;
  
  v_chunk := DBMS_LOB.SUBSTR(v_lob, 4000, v_offset);
  -- 4000 バイトずつ処理
END;
/

UPDATE

-- 全置換
UPDATE docs SET body = 'new content' WHERE id = 1;

-- 部分更新(DBMS_LOB)
DECLARE
  v_lob CLOB;
BEGIN
  SELECT body INTO v_lob FROM docs WHERE id = 1 FOR UPDATE;
  DBMS_LOB.APPEND(v_lob, ' additional text');
  COMMIT;
END;
/

DELETE

DELETE FROM docs WHERE id = 1;
-- LOB データも自動削除

DBMS_LOB パッケージ主要関数

GETLENGTH

LOB のサイズ取得:

SELECT DBMS_LOB.GETLENGTH(body) FROM docs WHERE id = 1;
-- CLOB: 文字数、BLOB: バイト数

SUBSTR

部分抽出:

SELECT DBMS_LOB.SUBSTR(body, 100, 1) FROM docs WHERE id = 1;
-- 位置1から100文字

INSTR

文字列検索:

SELECT DBMS_LOB.INSTR(body, 'error') FROM docs WHERE id = 1;
-- 'error' の位置

APPEND

追記:

DBMS_LOB.APPEND(dst_lob, src_lob);

COPY

LOB 間コピー:

DBMS_LOB.COPY(dst, src, amount, dst_offset, src_offset);

READ / WRITE

バッファ経由:

DECLARE
  v_lob    CLOB;
  v_buffer VARCHAR2(4000);
  v_amount INTEGER := 4000;
  v_offset INTEGER := 1;
BEGIN
  SELECT body INTO v_lob FROM docs WHERE id = 1;
  
  LOOP
    DBMS_LOB.READ(v_lob, v_amount, v_offset, v_buffer);
    -- v_buffer を処理
    v_offset := v_offset + v_amount;
    EXIT WHEN v_amount < 4000;
  END LOOP;
END;
/

LOADCLOBFROMFILE / LOADBLOBFROMFILE

ファイルから LOB へ読み込み:

DECLARE
  v_bfile  BFILE := BFILENAME('DOC_DIR', 'document.pdf');
  v_blob   BLOB;
  v_dest_offset INTEGER := 1;
  v_src_offset  INTEGER := 1;
BEGIN
  INSERT INTO docs (id, doc_binary) 
  VALUES (1, EMPTY_BLOB())
  RETURNING doc_binary INTO v_blob;
  
  DBMS_LOB.OPEN(v_bfile, DBMS_LOB.LOB_READONLY);
  DBMS_LOB.LOADBLOBFROMFILE(
    dest_lob    => v_blob,
    src_bfile   => v_bfile,
    amount      => DBMS_LOB.GETLENGTH(v_bfile),
    dest_offset => v_dest_offset,
    src_offset  => v_src_offset
  );
  DBMS_LOB.CLOSE(v_bfile);
  COMMIT;
END;
/

CLOB 版:

DBMS_LOB.LOADCLOBFROMFILE(
  dest_lob    => v_clob,
  src_bfile   => v_bfile,
  amount      => DBMS_LOB.LOBMAXSIZE,
  dest_offset => v_dest_offset,
  src_offset  => v_src_offset,
  bfile_csid  => 0,
  lang_context   => 0,
  warning     => v_warning
);

一時 LOB(Temporary LOB)

PL/SQL 内で一時的に使う:

DECLARE
  v_temp CLOB;
BEGIN
  DBMS_LOB.CREATETEMPORARY(v_temp, TRUE);  -- session キャッシュ
  
  DBMS_LOB.WRITEAPPEND(v_temp, 5, 'Hello');
  DBMS_LOB.WRITEAPPEND(v_temp, 6, ' World');
  
  -- 使用
  INSERT INTO docs (id, body) VALUES (1, v_temp);
  
  DBMS_LOB.FREETEMPORARY(v_temp);  -- 明示的に解放
END;
/

用途:

  • アセンブル: 少しずつ組み立て
  • 中間処理: 加工用
  • メモリ効率: 大量データの分割処理

Oracle 21c+ では自動解放もあるが、明示的解放が安全。


SecureFiles vs BasicFiles

完全比較

項目BasicFilesSecureFiles
導入Oracle 8iOracle 11g
パフォーマンス標準高速
圧縮不可可能(要ライセンス)
重複排除不可可能(要ライセンス)
暗号化不可可能(TDE)
ロギングLOGGING/NOLOGGING+ FILESYSTEM_LIKE_LOGGING
推奨

SecureFiles 有効化(DB レベル)

-- パラメータ設定
ALTER SYSTEM SET db_securefile = 'PREFERRED' SCOPE = BOTH;
-- ALWAYS: 常に SecureFiles
-- PREFERRED: SecureFiles 優先
-- PERMITTED: 明示指定時のみ
-- FORCE: SecureFiles 強制

パラメータ確認は Oracle パラメータ確認(V$PARAMETER)の記事も参照してください。

既存 BasicFiles の移行

-- オンライン変換(12c+)
ALTER TABLE t MOVE LOB(body) STORE AS SECUREFILE (
  CACHE COMPRESS HIGH DEDUPLICATE
);

圧縮・重複排除・暗号化

圧縮

CREATE TABLE t (
  id NUMBER, body CLOB
) LOB (body) STORE AS SECUREFILE (COMPRESS HIGH);

レベル:

  • NOCOMPRESS: 圧縮なし(デフォルト)
  • COMPRESS LOW: 軽量
  • COMPRESS MEDIUM: 標準
  • COMPRESS HIGH: 高圧縮

トレードオフ: ストレージ削減 vs CPU 使用。

Advanced Compression オプション必要

重複排除

CREATE TABLE t (
  id NUMBER, body CLOB
) LOB (body) STORE AS SECUREFILE (DEDUPLICATE);

同じ内容の LOB は1つだけ格納、参照カウント方式。

用途:

  • メール本文(同じ本文の複数受信者)
  • テンプレート
  • 添付ファイル

暗号化

CREATE TABLE t (
  id NUMBER, body CLOB
) LOB (body) STORE AS SECUREFILE (ENCRYPT USING 'AES256');

TDE(Transparent Data Encryption) 必要:

  • ウォレット設定
  • キー管理

パフォーマンスチューニング

INLINE vs OUT OF ROW

-- インライン(3964 バイト以下)
ENABLE STORAGE IN ROW   -- 推奨(小規模 LOB)

-- アウトオブライン
DISABLE STORAGE IN ROW  -- 常に別領域

推奨: ENABLE STORAGE IN ROW(デフォルト)。

CACHE 設定

CACHE          -- バッファキャッシュ使用
NOCACHE        -- 使用しない(大容量)
CACHE READS    -- 読み取りのみ

基準:

  • 頻繁アクセス: CACHE
  • 一度限り: NOCACHE

LOGGING

LOGGING       -- REDO 記録(デフォルト)
NOLOGGING     -- REDO なし
FILESYSTEM_LIKE_LOGGING  -- SecureFiles、メタデータのみ

用途:

  • 本番: LOGGING(復旧のため)
  • 一時テーブル: NOLOGGING(高速)

CHUNK サイズ

CHUNK 8192   -- 8K
CHUNK 32768  -- 32K(大容量向け)

DB ブロックサイズの倍数、大きいほど大容量向き。

インデックス

通常 B-Tree インデックス不可Oracle Text(全文検索)を使う:

CREATE INDEX doc_body_idx ON docs(body) 
INDEXTYPE IS CTXSYS.CONTEXT;

-- 検索
SELECT * FROM docs WHERE CONTAINS(body, 'Oracle') > 0;

Oracle Text は Oracle の全文検索エンジン。


Rails / Java / Python 対応

Rails ActiveRecord

# マイグレーション
class CreateDocs < ActiveRecord::Migration[8.0]
  def change
    create_table :docs do |t|
      t.text :body, limit: 4.gigabytes    # CLOB
      t.binary :image, limit: 4.gigabytes  # BLOB
    end
  end
end

# 使用
doc = Doc.new
doc.body = "Long text content..."
doc.image = File.binread("image.jpg")
doc.save

# 読み取り(ストリーミング)
Doc.find(1).image  # 全量ロード

oracle_enhanced adapter:

  • text → CLOB
  • binary → BLOB
  • ストリーミング API 提供

Rails 8 系の詳細は Rails 8 アップグレードガイドの記事、rails db:migrate 使い方の記事、Solid Queue 使い方の記事も参照してください。

Java (JDBC)

// CLOB 書き込み(ストリーミング)
PreparedStatement ps = conn.prepareStatement(
    "INSERT INTO docs (id, body) VALUES (?, ?)"
);
ps.setInt(1, 1);
try (Reader reader = new FileReader("large.txt")) {
    ps.setCharacterStream(2, reader);
    ps.executeUpdate();
}

// BLOB 書き込み
try (InputStream is = new FileInputStream("image.jpg")) {
    ps.setBinaryStream(2, is);
    ps.executeUpdate();
}

// CLOB 読み取り(ストリーミング)
ResultSet rs = ...;
Clob clob = rs.getClob("body");
try (Reader reader = clob.getCharacterStream()) {
    char[] buf = new char[4096];
    int read;
    while ((read = reader.read(buf)) != -1) {
        // 処理
    }
}

Python (oracledb)

import oracledb

# CLOB 書き込み
cursor.execute("""
    INSERT INTO docs (id, body) VALUES (:id, :body)
""", id=1, body="Long text content...")

# 大容量ファイル書き込み
with open("large.txt", "rb") as f:
    content = f.read()
    cursor.execute("""
        INSERT INTO docs (id, body_blob) VALUES (:id, :body)
    """, id=1, body=content)

# ストリーミング読み取り
cursor.execute("SELECT body FROM docs WHERE id = 1")
lob, = cursor.fetchone()

# 少しずつ読み取り
chunk_size = 65536
offset = 1
while True:
    chunk = lob.read(offset, chunk_size)
    if not chunk:
        break
    # 処理
    offset += chunk_size

実践シナリオ

シナリオ1:ファイルアップロード API

-- テーブル設計
CREATE TABLE uploads (
  upload_id   NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  file_name   VARCHAR2(255) NOT NULL,
  mime_type   VARCHAR2(100),
  file_size   NUMBER,
  content     BLOB
) LOB (content) STORE AS SECUREFILE (
  ENABLE STORAGE IN ROW
  COMPRESS MEDIUM
  CACHE READS
);

-- CHECK 制約:ファイルサイズ上限
ALTER TABLE uploads ADD CONSTRAINT chk_size 
  CHECK (file_size < 10485760);  -- 10 MB

制約エラーは ORA-01400: NOT NULL の記事も参照してください。

シナリオ2:文書管理システム

CREATE TABLE documents (
  doc_id      NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  title       VARCHAR2(500),
  author_id   NUMBER,
  content     CLOB,
  metadata    CLOB,  -- JSON
  created_at  TIMESTAMP DEFAULT SYSTIMESTAMP
) LOB (content, metadata) STORE AS SECUREFILE (
  COMPRESS HIGH
  DEDUPLICATE
);

-- 全文検索
CREATE INDEX doc_content_idx ON documents(content) 
INDEXTYPE IS CTXSYS.CONTEXT;

-- 検索
SELECT title 
FROM documents 
WHERE CONTAINS(content, 'Oracle AND SecureFile') > 0;

シナリオ3:メールアーカイブ

CREATE TABLE emails (
  email_id    NUMBER PRIMARY KEY,
  from_addr   VARCHAR2(255),
  to_addr     VARCHAR2(255),
  subject     VARCHAR2(500),
  body        CLOB,
  attachments BLOB
) LOB (body, attachments) STORE AS SECUREFILE (
  COMPRESS HIGH
  DEDUPLICATE       -- 同じメール本文で削減
);

シナリオ4:ログデータ格納

CREATE TABLE app_logs (
  log_id      NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  log_time    TIMESTAMP DEFAULT SYSTIMESTAMP,
  log_level   VARCHAR2(10),
  message     CLOB
) LOB (message) STORE AS SECUREFILE (
  NOCACHE                    -- 書き込みメイン
  FILESYSTEM_LIKE_LOGGING    -- REDO 軽減
  COMPRESS LOW
);

-- パーティション(月次)
PARTITION BY RANGE (log_time)
INTERVAL (NUMTOYMINTERVAL(1, 'MONTH'))
(...);

パーティション関連は Oracle Tablespace 管理の記事も参照してください。

シナリオ5:Rails でのファイルアップロード

# app/models/document.rb
class Document < ApplicationRecord
  # body: CLOB
  # attachment: BLOB
  
  validates :title, presence: true
  validate :attachment_size
  
  private
  
  def attachment_size
    if attachment.present? && attachment.bytesize > 10.megabytes
      errors.add(:attachment, "too large")
    end
  end
end

# app/controllers/documents_controller.rb
def create
  doc = Document.new(document_params)
  if params[:file].present?
    doc.attachment = params[:file].read
  end
  doc.save
end

シナリオ6:Java Spring からのストリーミング

@RestController
public class FileController {
    
    @PostMapping("/upload")
    public ResponseEntity<Void> upload(@RequestParam("file") MultipartFile file) 
        throws SQLException, IOException {
        
        try (PreparedStatement ps = conn.prepareStatement(
            "INSERT INTO uploads (file_name, content) VALUES (?, ?)"
        )) {
            ps.setString(1, file.getOriginalFilename());
            ps.setBinaryStream(2, file.getInputStream(), file.getSize());
            ps.executeUpdate();
        }
        return ResponseEntity.ok().build();
    }
    
    @GetMapping("/download/{id}")
    public void download(@PathVariable Long id, HttpServletResponse response) 
        throws SQLException, IOException {
        
        try (PreparedStatement ps = conn.prepareStatement(
            "SELECT content FROM uploads WHERE upload_id = ?"
        )) {
            ps.setLong(1, id);
            try (ResultSet rs = ps.executeQuery()) {
                if (rs.next()) {
                    Blob blob = rs.getBlob("content");
                    try (InputStream is = blob.getBinaryStream()) {
                        is.transferTo(response.getOutputStream());
                    }
                }
            }
        }
    }
}

シナリオ7:Data Pump インポート

# LOB を含むテーブルの exp/imp
expdp system/pw tables=docs directory=DP_DIR dumpfile=docs.dmp
impdp system/pw tables=docs directory=DP_DIR dumpfile=docs.dmp

Data Pump の詳細は Oracle Data Pump 使い方の記事も参照してください。

シナリオ8:Docker Oracle での確認

docker exec -it oracle-xe sqlplus scott/tiger <<EOF
CREATE TABLE test_lob (
  id NUMBER, body CLOB
) LOB (body) STORE AS SECUREFILE;

INSERT INTO test_lob VALUES (1, 'Test content');
SELECT DBMS_LOB.GETLENGTH(body) FROM test_lob;
EOF

Docker 関連は docker daemon 接続エラーの記事、Docker no space left on device の記事も参照してください。

シナリオ9:Autonomous DB での LOB

Autonomous DB はデフォルトで SecureFiles、追加設定不要。圧縮・暗号化は自動

シナリオ10:LOB のマイグレーション

-- BasicFiles → SecureFiles
ALTER TABLE t MOVE LOB(body) STORE AS SECUREFILE (
  CACHE 
  COMPRESS HIGH 
  DEDUPLICATE
);

-- インデックス再構築
ALTER INDEX doc_content_idx REBUILD;

予防のベストプラクティス

1. SecureFiles を使う

STORE AS SECUREFILE  -- 常に指定

2. 適切なストレージオプション

ENABLE STORAGE IN ROW  -- 小規模含む
CHUNK 8192              -- 適切なチャンクサイズ

3. 圧縮の検討

COMPRESS MEDIUM  -- 大容量テキストで有効

4. 重複排除の活用

DEDUPLICATE  -- テンプレート・共通コンテンツ

5. ストリーミングでの読み書き

アプリ側で全量ロードを避ける
少しずつ処理

6. 一時 LOB の解放

DBMS_LOB.FREETEMPORARY(v_temp);  -- 必ず解放

7. FILESYSTEM_LIKE_LOGGING

-- ログ・監査で REDO 軽減
FILESYSTEM_LIKE_LOGGING

8. パーティション化

-- 大量 LOB は月次パーティション
PARTITION BY RANGE (created_at) ...

9. アプリでのサイズ制限

CHECK 制約 + アプリバリデーション

10. モニタリング

-- LOB 使用状況
SELECT segment_name, bytes/1024/1024 mb
FROM dba_segments
WHERE segment_type = 'LOBSEGMENT';

トラブルシューティング

ORA-22285: non-existent directory

-- DIRECTORY 未登録
CREATE DIRECTORY doc_dir AS '/u01/documents';
GRANT READ ON DIRECTORY doc_dir TO scott;

Directory 関連は Oracle Directory 確認の記事も参照してください。

ORA-01555: snapshot too old

大容量 LOB での長時間読み取り:

-- UNDO 領域を増やす
ALTER SYSTEM SET undo_retention = 3600;

Snapshot too old は ORA-01555: snapshot too old の記事も参照してください。

ORA-22990: LOB locators cannot span transactions

-- LOB ロケータはトランザクションを跨げない
-- 各トランザクションで再取得

メモリ不足

アプリ側で全量ロード → メモリ枯渇
→ ストリーミング API 使用

遅い書き込み

-- NOLOGGING + APPEND ヒント
INSERT /*+ APPEND */ INTO t ...;

バックアップ肥大化

LOB は行データと別領域 → バックアップ影響大
→ 圧縮・重複排除で軽減

よくある質問(FAQ)

Q1. LONG と LOB の違い

  • LONG: 旧型、非推奨
  • LOB: モダン、CLOB/BLOB 推奨

Q2. VARCHAR2 と CLOB の使い分け

  • VARCHAR2: 4000 バイト以下(拡張で 32K)
  • CLOB: それ以上(最大 128 TB)

Q3. SecureFiles か BasicFiles か

SecureFiles 一択(11g 以降推奨)。

Q4. 圧縮のライセンス

Advanced Compression オプション必要(有償)。

Q5. 暗号化のライセンス

Advanced Security(TDE)必要(有償)。

Q6. 全文検索

Oracle Text(CTXSYS)で CONTAINS()

Q7. Rails での対応

text / binary 型で自動 CLOB/BLOB マッピング。

Q8. LOB のサイズ確認

SELECT DBMS_LOB.GETLENGTH(body) FROM t;

Q9. LOB のインデックス

通常 B-Tree 不可、Oracle Text 使用。

Q10. NULL LOB

IS NULL / IS NOT NULL でチェック可能

Q11. 一時 LOB のセッション有効期間

DBMS_LOB.SESSION 指定でセッション終了時解放。明示的解放推奨。

Q12. Autonomous DB での挙動

自動 SecureFiles、自動圧縮・暗号化。


参考リンク

Oracle 公式


まとめ

Oracle LOB(BLOB/CLOB)操作の要点を再整理します。

4種類の LOB

用途
BLOBバイナリ(画像、PDF、動画)
CLOB文字(テキスト、JSON、XML)
NCLOBUnicode(多言語)
BFILE外部ファイル参照

モダンなテーブル設計

CREATE TABLE t (
  id NUMBER PRIMARY KEY,
  body CLOB
) LOB (body) STORE AS SECUREFILE (
  ENABLE STORAGE IN ROW  -- 小規模インライン
  COMPRESS HIGH           -- 圧縮
  DEDUPLICATE             -- 重複排除
  CACHE                   -- キャッシュ
);

主要 DBMS_LOB 関数

DBMS_LOB.GETLENGTH(lob)          -- サイズ
DBMS_LOB.SUBSTR(lob, amt, off)   -- 部分抽出
DBMS_LOB.INSTR(lob, pattern)     -- 検索
DBMS_LOB.APPEND(dst, src)         -- 追記
DBMS_LOB.COPY(dst, src, ...)      -- コピー
DBMS_LOB.READ(lob, amt, off, buf) -- 読み取り
DBMS_LOB.WRITE(lob, amt, off, buf)-- 書き込み
DBMS_LOB.LOADCLOBFROMFILE(...)    -- ファイル読み込み
DBMS_LOB.LOADBLOBFROMFILE(...)    -- ファイル読み込み
DBMS_LOB.CREATETEMPORARY(lob, ...)-- 一時 LOB
DBMS_LOB.FREETEMPORARY(lob)       -- 一時 LOB 解放

SecureFiles vs BasicFiles

項目BasicFilesSecureFiles
導入8i11g
圧縮
重複排除
暗号化
推奨

パフォーマンス

INLINE (< 3964 bytes) → 高速
COMPRESS → ストレージ削減
DEDUPLICATE → 重複コンテンツで大幅削減
CACHE → 頻繁アクセス向け
NOCACHE → 一度限り、大容量

アプリからの操作

Rails: t.text / t.binary
Java: setCharacterStream / setBinaryStream (JDBC)
Python: oracledb で datetime.now() 相当
共通: ストリーミング推奨(メモリ節約)

これらの知識は、Oracle でのファイルアップロード・文書管理・メールアーカイブ・ログシステム・画像/動画配信・全文検索・Rails / Java / Python 開発など、あらゆる場面で活用できます。本記事をブックマークしておけば、Oracle LOB を確実に使いこなせるようになります。


本記事は2026年6月時点の情報をもとに、Oracle Database 19c〜23ai での動作確認・公式ドキュメントに基づき作成しています。Oracle のバージョンにより挙動が異なる場合があるため、最新の情報は Oracle 公式ドキュメント(docs.oracle.com)もあわせてご確認ください。