Oracleデータベースで外部システムから取り込んだデータや、アプリケーションが挿入したデータに改行コードが混入しているケースは多くあります。改行コードが含まれると、CSVエクスポートの列ずれ・SQL文字列比較の不一致・画面表示の乱れなどの問題が発生します。
この記事では、改行コードの確認方法から、REPLACE・REGEXP_REPLACEでの削除・置換、UPDATEで実際にDBのデータを更新する方法、CLOB型への対応まで、実務で使えるパターンを網羅します。
・改行コード CHR(10)・CHR(13) の種類と違い
・DUMP関数で改行コードの存在を確認する方法
・改行コードを含む行をSELECTで検索する方法
・REPLACE関数で改行コードを削除・スペースに置換する方法
・REGEXP_REPLACE関数で正規表現を使って柔軟に処理する方法
・UPDATEで実際にデータを更新する方法(安全な実行手順付き)
・CLOB/NCLOB型の改行コード処理と注意点
・パフォーマンスを考慮したWHERE条件付き更新
改行コードの種類と CHR 関数
改行コードは文字コード(ASCII)で表されます。Oracleでは CHR() 関数でコードから文字を生成できます。
| コード | CHR関数 | 名称 | 主なOS | 説明 |
|---|---|---|---|---|
| 10 | CHR(10) |
LF (Line Feed) | Unix/Linux/macOS | ラインフィード。単体の改行コード。 |
| 13 | CHR(13) |
CR (Carriage Return) | 旧macOS | キャリッジリターン。単体では旧macで使用。 |
| 13+10 | CHR(13)||CHR(10) |
CRLF | Windows | CRとLFの組み合わせ。Windowsの標準改行コード。 |
WindowsアプリからOracleにデータを挿入するとCRLF(CHR(13)||CHR(10))が混入しやすい。CSVインポートやテキストファイル取込ではLF(CHR(10))が多い。両方が混在することもあるため、最初に確認してから処理しましょう。
改行コードの含まれている行を確認する方法
INSTR関数で改行コードを含む行を検索する
-- CHR(10)(LF)を含む行を検索 SELECT id, col_name FROM your_table WHERE INSTR(col_name, CHR(10)) > 0; -- CHR(13)(CR)を含む行も検索 SELECT id, col_name FROM your_table WHERE INSTR(col_name, CHR(13)) > 0;
-- LFまたはCRを含む行を一度に検索 SELECT id, col_name FROM your_table WHERE INSTR(col_name, CHR(10)) > 0 OR INSTR(col_name, CHR(13)) > 0;
REGEXP_LIKE で正規表現で検索する
-- LFまたはCRを含む行を正規表現で検索 SELECT id, col_name FROM your_table WHERE REGEXP_LIKE(col_name, CHR(10)||'|'||CHR(13));
DUMP関数で改行コードの種類を詳細確認する
DUMP 関数は文字列の各文字をASCIIコード(10進数)で表示します。改行コードの存在と種類を確認できます。
-- 特定の行のカラムをDUMPで確認 SELECT DUMP(col_name) AS dump_result FROM your_table WHERE id = 1; -- 結果例 -- Typ=1 Len=13: 72,101,108,108,111,44,13,10,87,111,114,108,100 -- ↑ 44(,) ↑13=CR ↑10=LF
10 → LF(ラインフィード)
13 → CR(キャリッジリターン)
13,10 と連続している → CRLF(Windows改行)
これで対象テーブルにどの改行コードが使われているか判断できます。
件数を確認する
-- 改行コードを含む件数を確認(更新前の確認に使う)
SELECT
COUNT(*) AS total_rows,
SUM(CASE WHEN INSTR(col_name, CHR(10)) > 0 THEN 1 ELSE 0 END) AS lf_rows,
SUM(CASE WHEN INSTR(col_name, CHR(13)) > 0 THEN 1 ELSE 0 END) AS cr_rows,
SUM(CASE WHEN INSTR(col_name, CHR(13)||CHR(10)) > 0 THEN 1 ELSE 0 END) AS crlf_rows
FROM your_table;
REPLACE関数で改行コードを削除する
REPLACE(文字列, 検索文字, 置換文字) で改行コードを空文字に置換(=削除)します。
LFのみ削除
SELECT REPLACE(col_name, CHR(10), '') AS cleaned FROM your_table;
CRとLFを両方削除(CRLF対応)
-- CRとLFを両方削除(順番はどちらが先でもOK) SELECT REPLACE(REPLACE(col_name, CHR(13), ''), CHR(10), '') AS cleaned FROM your_table; -- CRLFをまず削除してから残りのCR/LFを削除する方法(より厳密) SELECT REPLACE(REPLACE(REPLACE(col_name, CHR(13)||CHR(10), ''), CHR(13), ''), CHR(10), '') AS cleaned FROM your_table;
削除せずスペースに置換する
改行コードを削除すると単語が連続して読みにくくなる場合、スペースや区切り文字に置換する方法が有効です。
-- CRLFをスペースに置換(見やすい形にする) SELECT REPLACE(REPLACE(col_name, CHR(13), ''), CHR(10), ' ') AS cleaned FROM your_table; -- 例: 結果 -- 変換前: 'Hello,\r\nWorld!' -- 変換後: 'Hello, World!'
REGEXP_REPLACE関数で正規表現を使って削除する
REGEXP_REPLACE を使うと、[\r\n](CR または LF)を一度に処理できます。REPLACE のネストより可読性が高く、より柔軟な処理が可能です。
基本: CR または LF を削除
-- [\r\n] は CR または LF にマッチ SELECT REGEXP_REPLACE(col_name, '[\r\n]', '') AS cleaned FROM your_table; -- + で連続する改行コードも1回で削除 SELECT REGEXP_REPLACE(col_name, '[\r\n]+', '') AS cleaned FROM your_table;
改行コードをスペースに置換(連続改行は1つにまとめる)
-- 連続する改行コードを1つのスペースに置換 SELECT REGEXP_REPLACE(col_name, '[\r\n]+', ' ') AS cleaned FROM your_table;
REPLACE vs REGEXP_REPLACE の使い分け
| REPLACE | REGEXP_REPLACE | |
|---|---|---|
| 構文 | REPLACE(str, chr, replace) |
REGEXP_REPLACE(str, pattern, replace) |
| パフォーマンス | 速い(固定文字列マッチ) | やや遅い(正規表現エンジン) |
| 可読性 | ネストが深くなりやすい | 1回でCR/LF両方を処理できる |
| 柔軟性 | 固定文字列のみ | 正規表現パターンが使える |
| CLOBへの適用 | 可(Oracle 11g以降) | 可(Oracle 10g以降) |
| 推奨場面 | 単純なLF/CR削除 | 複数パターン・連続改行処理 |
UPDATEで実際にデータを更新する
SELECT で確認してから UPDATE するのが安全な手順です。
ステップ1:更新前に件数と内容を確認する
-- 更新対象の件数を確認 SELECT COUNT(*) FROM your_table WHERE INSTR(col_name, CHR(10)) > 0 OR INSTR(col_name, CHR(13)) > 0; -- 更新後の値をSELECTで確認 SELECT id, REPLACE(REPLACE(col_name, CHR(13), ''), CHR(10), '') AS preview FROM your_table WHERE INSTR(col_name, CHR(10)) > 0 OR INSTR(col_name, CHR(13)) > 0 AND ROWNUM <= 10;
ステップ2:UPDATEを実行する
-- REPLACE関数でLF・CRを削除 UPDATE your_table SET col_name = REPLACE(REPLACE(col_name, CHR(13), ''), CHR(10), '') WHERE INSTR(col_name, CHR(10)) > 0 OR INSTR(col_name, CHR(13)) > 0; -- 更新件数の確認 SELECT SQL%ROWCOUNT FROM DUAL; -- PL/SQL内の場合
-- REGEXP_REPLACEを使う場合 UPDATE your_table SET col_name = REGEXP_REPLACE(col_name, '[\r\n]+', '') WHERE REGEXP_LIKE(col_name, '[\r\n]');
ステップ3:確認してCOMMIT
-- 更新後の確認 SELECT id, col_name FROM your_table WHERE INSTR(col_name, CHR(10)) > 0 OR INSTR(col_name, CHR(13)) > 0; -- 0件になれば成功 COMMIT; -- 問題なければ確定 -- ROLLBACK; -- 問題があれば取り消し
OracleのDMLは自動COMMITされません。COMMIT前であればROLLBACKで元に戻せます。必ず更新後にSELECTで結果を確認し、問題なければCOMMITしましょう。
複数カラムを一括で更新する
-- 複数カラムを1回のUPDATEで処理
UPDATE your_table
SET col1 = REPLACE(REPLACE(col1, CHR(13), ''), CHR(10), ''),
col2 = REPLACE(REPLACE(col2, CHR(13), ''), CHR(10), ''),
col3 = REGEXP_REPLACE(col3, '[\r\n]+', '')
WHERE INSTR(col1, CHR(10)) > 0
OR INSTR(col1, CHR(13)) > 0
OR INSTR(col2, CHR(10)) > 0
OR INSTR(col2, CHR(13)) > 0
OR INSTR(col3, CHR(10)) > 0
OR INSTR(col3, CHR(13)) > 0;
CLOB/NCLOB型の改行コード処理
CLOB(大容量文字列型)は通常のVARCHAR2と同じように REPLACE や REGEXP_REPLACE で処理できます(Oracle 11g以降)。ただしいくつかの注意点があります。
CLOB に対して REPLACE を使う
-- CLOBカラムの改行コードを削除 UPDATE your_table SET clob_col = REPLACE(REPLACE(clob_col, CHR(13), ''), CHR(10), '') WHERE INSTR(clob_col, CHR(10)) > 0 OR INSTR(clob_col, CHR(13)) > 0;
・CLOBは最大4GBのデータを格納できるが、
REPLACEの処理はメモリを大量消費することがある・非常に大きなCLOBを一括更新するとUNDOセグメント不足(ORA-01555等)が発生することがある
・大量のCLOBを更新する場合は分割COMMIT(ROWID範囲やROWNUM指定)を検討する
・
REGEXP_REPLACE はCLOBのサイズが32767バイトを超えるとエラーになるケースがある(バージョン依存)大きなCLOBを分割して更新する
-- ROWID範囲で分割して更新(ORA-01555対策)
DECLARE
CURSOR c IS SELECT ROWID FROM your_table
WHERE INSTR(clob_col, CHR(10)) > 0;
v_rowid ROWID;
v_count NUMBER := 0;
BEGIN
OPEN c;
LOOP
FETCH c INTO v_rowid;
EXIT WHEN c%NOTFOUND;
UPDATE your_table
SET clob_col = REPLACE(REPLACE(clob_col, CHR(13), ''), CHR(10), '')
WHERE ROWID = v_rowid;
v_count := v_count + 1;
IF MOD(v_count, 1000) = 0 THEN
COMMIT;
END IF;
END LOOP;
COMMIT;
CLOSE c;
DBMS_OUTPUT.PUT_LINE('更新件数: ' || v_count);
END;
/
前後の改行コードだけを削除する(LTRIM/RTRIM活用)
文字列の先頭・末尾の改行コードだけを取り除きたい場合、LTRIM / RTRIM が使えます。
-- 先頭の改行コードを削除 SELECT LTRIM(col_name, CHR(10)||CHR(13)) AS cleaned FROM your_table; -- 末尾の改行コードを削除 SELECT RTRIM(col_name, CHR(10)||CHR(13)) AS cleaned FROM your_table; -- 先頭・末尾両方の改行コードを削除 SELECT TRIM(BOTH CHR(10) FROM TRIM(BOTH CHR(13) FROM col_name)) AS cleaned FROM your_table;
パフォーマンスを考慮したWHERE条件付き更新
改行コードを含まない行にも REPLACE を適用すると処理時間と UNDO ログが増加します。WHERE句で改行コードを含む行のみに絞り込むことでパフォーマンスを改善できます。
-- NG: 全行に REPLACE を適用(無駄なUNDOが発生) UPDATE your_table SET col_name = REPLACE(REPLACE(col_name, CHR(13), ''), CHR(10), ''); -- OK: 改行コードを含む行のみ更新(パフォーマンス改善) UPDATE your_table SET col_name = REPLACE(REPLACE(col_name, CHR(13), ''), CHR(10), '') WHERE INSTR(col_name, CHR(10)) > 0 OR INSTR(col_name, CHR(13)) > 0;
col_name にファンクション索引(INSTR(col_name, CHR(10)))を作成するとWHERE句の検索が高速になります。ただし頻繁にINSERT/UPDATEが発生する場合は索引のオーバーヘッドも考慮してください。実務でよくある改行コード処理パターン
CSV取込データのクレンジング
-- 外部CSVから取り込んだデータの改行コードを一括削除
UPDATE import_data
SET item_name = REPLACE(REPLACE(item_name, CHR(13), ''), CHR(10), ''),
description = REPLACE(REPLACE(description, CHR(13), ''), CHR(10), ' ')
WHERE INSTR(item_name, CHR(10)) > 0
OR INSTR(item_name, CHR(13)) > 0
OR INSTR(description, CHR(10)) > 0
OR INSTR(description, CHR(13)) > 0;
COMMIT;
改行コードを含む行だけをエクスポートして確認する
-- 改行コードを可視化してSELECT(確認用)
SELECT id,
REPLACE(REPLACE(col_name, CHR(13), '[CR]'), CHR(10), '[LF]') AS visible
FROM your_table
WHERE INSTR(col_name, CHR(10)) > 0 OR INSTR(col_name, CHR(13)) > 0;
改行コードをHTMLのタグに変換する(表示用)
-- UPDATE しないで SELECT で変換(HTML表示用)
SELECT id,
REPLACE(REPLACE(col_name, CHR(13)||CHR(10), '<br>'), CHR(10), '<br>') AS html_formatted
FROM your_table;
よくあるトラブルと解決法
ケース1:REPLACE後もまだ改行コードが残る
-- 原因: CRLF(13+10)のみ削除したが単独のLF/CRが残っている
-- または全角改行コード(CHR(13330)など)が混在している
-- 確認: DUMP で実際のコードを確認
SELECT DUMP(col_name) FROM your_table WHERE id = 1;
-- 解決: CRLFを先に削除してから単独CR/LFを削除
SELECT REPLACE(REPLACE(REPLACE(col_name,
CHR(13)||CHR(10), ''), -- CRLF を先に削除
CHR(13), ''), -- 単独CR を削除
CHR(10), '' -- 単独LF を削除
) AS cleaned
FROM your_table;
ケース2:REGEXP_REPLACE で ORA-22835 が発生する(CLOB)
-- ORA-22835: Buffer too small for CLOB to CHAR conversion
-- 原因: CLOBのサイズが内部バッファ(32767バイト)を超えている
-- 解決: REPLACE関数に切り替えるか、DBMS_LOBを使う
-- DBMS_LOB.REPLACE(大容量CLOB向け)
DECLARE
v_clob CLOB;
BEGIN
SELECT clob_col INTO v_clob FROM your_table WHERE id = 1 FOR UPDATE;
v_clob := REPLACE(REPLACE(v_clob, CHR(13), ''), CHR(10), '');
UPDATE your_table SET clob_col = v_clob WHERE id = 1;
COMMIT;
END;
/
ケース3:WHERE条件なしでUPDATEしてしまった
-- COMMITしていなければROLLBACKで戻せる
ROLLBACK;
-- COMMITしてしまった場合: フラッシュバック(設定されている場合)
SELECT * FROM your_table AS OF TIMESTAMP TO_TIMESTAMP('2026-03-18 12:00:00', 'YYYY-MM-DD HH24:MI:SS')
WHERE id = 1;
ケース4:NULLカラムに REPLACE を適用するとエラーになる
-- REPLACEはNULLを含む値に適用するとNULLを返す(エラーではない) -- NULL カラムに対して REPLACE を適用した場合の確認 SELECT REPLACE(NULL, CHR(10), '') FROM DUAL; -- 結果: NULL(エラーにはならないがNULLのまま) -- 必要に応じてNVL/NULLIFで対処 SELECT REPLACE(NVL(col_name, ''), CHR(10), '') AS cleaned FROM your_table;
よくある質問
:NEW.col := REPLACE(REPLACE(:NEW.col, CHR(13), ''), CHR(10), '');まとめ
| やりたいこと | SQL | 注意点 |
|---|---|---|
| 改行コードを含む行を検索 | WHERE INSTR(col, CHR(10)) > 0 |
CR/LF両方を確認 |
| 改行コードの種類を確認 | DUMP(col) |
ASCIIコード10=LF, 13=CR |
| 削除(REPLACE) | REPLACE(REPLACE(col, CHR(13), ''), CHR(10), '') |
CRLF→CR→LFの順に削除が安全 |
| 削除(REGEXP_REPLACE) | REGEXP_REPLACE(col, '[\r\n]+', '') |
CLOBの32767制限に注意 |
| スペースに置換 | REPLACE(col, CHR(10), ' ') |
文字が詰まるのを防ぐ |
| 先頭・末尾だけ削除 | LTRIM/RTRIM(col, CHR(10)||CHR(13)) |
内部の改行は残る |
| UPDATEで削除 | WHERE条件付きで REPLACE を適用 | SELECT確認→UPDATE→COMMIT |
| 大量CLOBの更新 | PL/SQLで分割COMMIT | ORA-01555対策 |
1. DUMP関数で改行コードの種類を確認する
2. SELECT + REPLACE で更新後の値をプレビューする
3. WHERE句で対象行を絞り込んでからUPDATEする
4. COMMIT前にSELECTで確認し、問題なければCOMMITする
