Oracle impdp インポートできていないレコードがあった 文字コードと文字サイズの問題

Oracle
スポンサーリンク

未解決だで。

インポート先のカラムのバイト長を広げるやり方で解決しました。

事象

impdpコマンドでインポートして完了したけど、一部のレコードがインポートできていなかった。

実行したインポートコマンドはオプションも含めると以下の感じ。

impdp SYSTEM dumpfile=ダンプファイル.dmp table_exists_action=replace logfile=20220213_hogetmp.log schemas=スキーマ名 directory=ディレクトリ名

impdb実行時にも大量のエラーを出力していた。→ちゃんと実行後のログは確認しようね^^

原因

 

原因を調べる。

logfile=

オプションで指定したログファイルに色々とエラーが吐かれていたので確認した。

 

すると、以下の2点がポイントっぽい事がわかった。

  • インポート時とエクスポート時の文字コードが違うっぽい
  • 文字列の幅がでかすぎてインポートできない

それぞれ見てみる。

 

1点目、インポート時とエクスポート時の文字コードが違うっぽい

 

impdp時のログから抜粋:

import done in AL32UTF8 character set and AL16UTF16 NCHAR character set
export done in JA16EUC character set and AL16UTF16 NCHAR character set

import のときが、「AL32UTF8」character set

export のときが、「JA16EUC」character set

で違ってる。

NCHAR character set の方は、どちCHARACTER SETらも「AL16UTF16」で同じ。

 

  • どっちかがNLS_LANGになるのかしら?
    • 関係なし
    • NLS_LANGはOracleクライアント側の文字コード
    • 上記に設定されているのはいずれもサーバ側の文字コード

 

character set と、NCHAR character set の違い

 

CHARACTER SET は、

データベースにデータを格納するときのキャラクタ・セットを指定します。

中略

AL16UTF16キャラクタ・セットは、データベース・キャラクタ・セットとして指定できません。

Oracle公式

 

インポート先OracleのCHARACTER SET 確認

 

自分の手元のサーバ、インポート先のDBを確認してみる。

SQL> SELECT
*
FROM
NLS_DATABA 2 3 4 SE_PARAMETERS
WHERE
PARAMETER='NLS_CHARACTERSET'; 5 6

PARAMETER
--------------------------------------------------------------------------------
VALUE
--------------------------------------------------------------------------------
NLS_CHARACTERSET
AL32UTF8


SQL>

AL32UTF8。

てことは、

こっちがログのimportのところに載ってた前者側(左側)の

character set

か。

ちゅーことは、

JA16EUC

でDBを作り直せば一致する?

 

なぜ、「作り直せば」(文字コード変更じゃだめなの!?)というと、、、↓

データベースキャラクタセット(NLS_CHARACTER_SET)

データベースクライアントの設定(NLS_LANG)

参考

NLS_CHARACTER_SETのほうは、

データベース作成後に変更は出来ません。

てことらしー。

 

 

 

 

NCHAR character set は、

データ型がNCHARNCLOBまたはNVARCHAR2として定義された列にデータを格納する際に使用する各国語キャラクタ・セット

Oracle公式

上記3つのデータ型のときのみ使用されると。ここらへんは国によって変わってくるからか?

 

 

2点目、文字列の幅がでかすぎてインポートできない

 

impdp時のログから抜粋:

ORA-02374: conversion error loading table "スキーマ名"."テーブル名"
ORA-12899: value too large for column カラム名 (actual: 63, maximum: 60)
ORA-02372: data for row: テーブル名 : 0X'A5AFA5EBB5B9A5C1A5E3A5B3A1A6A5D5A5A9A5EBA5C9A5B5A4'

3つエラーコードが出てる。それぞれ意味を調べる。

 

ORA-02374: conversion error loading table

エラーコード: ORA-02374
詳細: 表string.stringのロードで変換エラーが発生しました
原因: 表に行をロードできませんでした。行内の1つ以上の列に変換エラーが発生しています。
アクション: ロードできない行の詳細は、続いて表示されるメッセージを参照してください。このエラーを回避するには、インポートされる表の定義と、エクスポートされる表の定義が一致していることを確認してください。

参考

詳細は次に出てくるエラーを見ろ、と。

あと、エクスポート元と、インポート先のテーブルの定義を一致させろと。

 

ORA-12899: value too large for column

  • 挿入や更新時にカラムサイズを超えた値をセットしようとした

参考

インポート先のカラムサイズを超えちゃったと。

対処法としては、

  • カラムサイズ以下の値をセットする

と記載されておる。
別のサイト見てみる。

原因

データの提供元のキャラクタセットと投入先のデータベース・キャラクタセットにおいて、同じ文字でもバイト数が異なる文字列を使用している。
その代表が半角カタカタである。 Shift JIS の場合、半角カタカナは ‘A6’x 〜 ‘DF’x で 1 バイトで格納できるが、EUC-JP の場合 ‘8E26’x 〜 ‘8E5F’x と長さは 2 バイトになる。同様に UTF-8 なら 3 バイト、UTF-16 なら 2 バイトになる。

エクスポート元とインポート先の文字コードが違うからバイト数が変わってしまう文字があるんやなー。もし文字コードを合わせられるんなら合わせたほうがよさげやな。(データベースの作り変えが必要)

対応

根本的な解決は、移行先のテーブル定義を変更する。このエラーがでるということは、テーブル定義の列定義がバイト・セマンティクス表記であるはずなので、そのバイトサイズを大きくする。その大きさは各キャラクタセットにおける1文字の最大サイズ×キャラクタ数を設定する。
または、キャラクタ・セマンティクスで列定義  を行なう。

やっぱりインポート先のテーブルの定義を変更するってことがご記載されとる。
バイトサイズを大きくする、と。

ORA-02372:

 

data for row:エラーコード: ORA-02372
詳細: 行のデータ: string
原因: 表にデータをロード中に、変換エラーが発生しました。このメッセージには、変換エラーを起こした行内のフィールド値が表示されます。
アクション: 処置は必要ありません。これは情報メッセージです。

参考

ここはただのinfoレベルな参考情報なので気にしないでおk。

 

対応

NLS_LANGをEUC-JPにしてみる→だめだった

Oracleサーバ側の文字コードが、Databaseを作り直さないと変更できないので、意味ないと思うが、試しにOracleクライアント側を変えてやってみる。

 

インポート側のOSの環境変数「NLS_LANG」をEUC-JPにしてインポートし直してみる。

その後、ログとデータを確認する。

 

 

まず環境変数の変更。

 

NLS_LANGをEUC-JPにしたい場合、

setenv NLS_LANG Japanese_Japan.JA16EUC

Oracle公式

 

setenv NLS_LANG Japanese_Japan.JA16EUC
$ setenv NLS_LANG Japanese_Japan.JA16EUC
-bash: setenv: command not found

setenvコマンド無いやんけ。

export コマンドでやる。

$ export NLS_LANG=Japanese_Japan.JA16EUC

$ printenv NLS_LANG
Japanese_Japan.JA16EUC

設定した。

再度、impdp実行。

結果変わらずorz…

(LANG環境変数もEUCにしてみたが結果変わらずだった)

 

カラムの定義を変えてみる(文字のキャパを広げる)

 

その前に、

TRUNCATE TABLE 対象のテーブル

しておく。(定義変えた後でもいいけど)

外部キー制約のせいでTRUNCATE TABLE できない場合は、一時解除する。

VARCHAR(40 BYTE)

VARCHAR(80 BYTE)

に変更してやってみたが、impdpで結局定義ごと入れ替わるので、impdpすると変更した定義が、

VARCHAR(40 BYTE)

に戻ってしまって意味がなかった。

 

カラムの定義を変えた後は、データのみを持ってきて当てるということをしないとだめなんだろうなー。

 

impdp コマンドのオプションに、

「content=data_only」

を付与すればいいだけだった!

さすれば、「定義もデータも含まれたDUMPファイル」から、「データだけ」をインポート(リストア)できる。

 

例)

impdp SYSTEM dumpfile=ダンプファイル.dmp content=data_only logfile=このimpdpコマンドの結果を記録するログファイル名.log schemas=スキーマ名 directory=Oracleで設定してるディレクトリ名

 

データベースを作り直す→できん

 

やり方がわからんね。うん。

qiitaを参考にやってみたが、うまくいかんね。

 

ちなみに拙僧の環境は、Oracle12cをDockerで構築したもの。やり方はqiitaを参考にした。

まとめ

 

カラムの定義を変えて、

移行元からはデータだけ取り出してやるかー。

TRUNCATE TABLE 対象のテーブル

して、

content=data_only オプションをつけたimpdpコマンドでインポートしたらうまく行ったズラ。

 

コメント