2016-03-03 15 views
7

Oracle 10gOracle 11gを使用します。N関数は既存のクエリに問題を引き起こす可能性がありますか?

また、.netで書かれた疑似SQLコード(SqlAlchemy for Pythonのようなもの)からクエリを自動的に作成するレイヤーがあります。

私たちの層は、現在、非ANSI文字が含まれている場合、それは自動的にUnicodeのバイト(のような\00E0)として書かれた特殊文字でUNISTRを構成する、単一引用符'で任意の文字列をラップして。このアルゴリズムは、同じ文字列フィールドが時々'my simple string'として渡され、時にはUNISTR('my string with special chars like \00E0')としてラップされたクエリを作成でき
INSERT INTO ... (...) SELECT ... FROM DUAL UNION ALL SELECT ... FROM DUAL ...

は、今、私たちは、以下の構築物を用いて複数の挿入を行うための方法を作成しました。

説明された条件は、ORA-12704: character set mismatchを引き起こします。

一つの解決策は、INSERT ALL構文を使用することですが、それは今で用いたものに比べ非常に遅いです。

もう1つの解決策は、で既にラップされているものを除き、Nを任意の文字列の前に置くようにレイヤーに指示することです。これは簡単です。

これが既存のクエリに何らかの副作用を引き起こす可能性があるかどうかを知りたいだけです。

注:DB上のすべてのフィールドは、NCHARまたはNVARCHAR2のいずれかです。


のOracle REF:あなたが求めているものをhttp://docs.oracle.com/cd/B19306_01/server.102/b14225/ch7progrunicode.htm

+1

ターゲットの列サイズがわかっている場合は、キャストすることもできます。または、レイヤーが適切な一括挿入メカニズムをサポートしている可能性があります。しかし、確実に 'n '...' 'を使うことは、データベース文字セットから各国文字セットへの挿入時のリテラルの暗黙の変換を避けるだけですか? –

+0

@AlexPoole誠実に、私はあなたの質問を理解していない... – Teejay

+1

ステートメントごとに挿入されている行の数はいくつですか? 'INSERT ALL'が' UNION ALL'よりも遅い場合、私の答え[here](http://stackoverflow.com/a/11663076/409172)で説明されているように、Oracleの解析問題に遭遇している可能性があります。巨大なSQL文の長い解析時間を避けるために、 'INSERT ALL'をより小さなチャンクに分割するだけで十分でしょう。 –

答えて

2

Basicly、文字列がまたはN機能なしで保存されているかの違いがあります。自分で考えるため

あなたは確認することができます。

SQL> create table test (val nvarchar2(20)); 

Table TEST created. 

SQL> insert into test select n'test' from dual; 

1 row inserted. 

SQL> insert into test select 'test' from dual; 

1 row inserted. 

SQL> select dump(val) from test; 
DUMP(VAL)                  
-------------------------------------------------------------------------------- 
Typ=1 Len=8: 0,116,0,101,0,115,0,116            
Typ=1 Len=8: 0,116,0,101,0,115,0,116 

あなたが同じように何の副作用を見ることができないとして。

あなたがここに興味があるなら、これはとても美しく働く理由が原因でユニコード

の優雅であるが、それは

https://www.youtube.com/watch?v=MijmeoH9LT4

+0

文字列リテラルのどこでもNを適用するとパフォーマンスが低下することはありますか? – Teejay

+2

"文字列リテラルでどこでもNを適用するとパフォーマンスが低下することはありますか?"いいえ、nchar列に挿入されたchar値が暗黙的または明示的にncharに変換されたため、できませんでした。 –

+0

@Mikhailov Valentineありがとうございます。だから、 'N'では、暗黙のうちに起こるプロセスを明示的に*明示していますか? – Teejay

1

私はあなたがエラー"ORA-12704: character set mismatch"理由を得ることを前提と説明素敵なビデオです引用符内のデータはcharと見なされますが、フィールドはncharなので、charは異なる文字セットを使用して照合されます。一方はNLS_CHARACTERSET、もう1つはNLS_NCHAR_CHARACTERSETです。

あなたがUNISTR機能を使用する場合のOracle docsが言うように、それは(も文字にエンコードされた値を変換し、どのような場合には)charからncharにデータを変換します

「UNISTRは、引数として文字列を取りますリテラルまたは が文字データに解決し、それを国別文字 に設定した式を返します。

あなたは明示的にのみデコードなしNLS_NCHAR_CHARACTERSETで値を取得NTO_NCHARを使用して値を変換します。このようにコード化された値がある場合は、"\00E0"はデコードされず、変更されないと見なされます。

だから、次のような挿入を持っている場合:最初の挿入フィールドの

insert into select N'my string with special chars like \00E0', 
    UNISTR('my string with special chars like \00E0') from dual .... 

あなたのデータは次のようになります。'my string with special chars like \00E0'ない'my string with special chars like à'。これは私が認識している唯一の副作用です。他の問合せではすでにNLS_NCHAR_CHARACTERSETエンコーディングが使用されているはずです。明示的な変換を使用しても問題ありません。

そして、すべての値をN'my string with special chars like à'として挿入するだけではどうですか?最初にあなたは '上位レベル'のソフトウェアで異なるエンコーディングを使用する場合、それらをUTF-16にエンコードします(私はあなたがncharsのためにUTF-16を使用すると仮定します)。

+0

* "エラーが発生したと仮定します" ORA-12704:文字セットが一致していないため、引用符内のデータがcharと見なされますがフィールドはncharです。*いいえ、非UnicodeとUnicodeを混在させているため、テキストは 'UNION ALL'となります。 – Teejay

+0

* "\ 00E0"のようにエンコードされた値があると、デコードされずにそのままであるとみなされます "*特殊文字を含む文字列は自動的にレイヤーによってUNISTRでラップされます。これがミキシングが浮かんでいる理由です。これが他の文字列にNが必要な理由です。 – Teejay

+0

*「そして、ちょうどすべての値を 'N'my文字列として特別な文字で挿入するのはなぜですか?」というように、UNISTR( '\ 00E0')'とN'à''? – Teejay

-1
  • n機能の使用 - すでに上記の回答があります。

データベースのキャラクタセットを変更する機会があれば、それは実際にあなたの人生を楽にします。私は巨大なプロダクションシステムに取り組んでいましたが、ストレージスペースが安いため、誰もがAL32UTF8に移行し、国際化の面倒がゆっくりと過去の悲惨な思い出になる傾向があることがわかりました。

最も簡単なことは、データベースインスタンスの文字セットとしてAL32UTF8を使用し、どこでもvarchar2を使用することです。私たちは、JDBC経由でバインド変数として標準のJavaのUnicode文字列を読み書きしています。

SQL挿入の巨大なテキストは複数の理由のために十分に拡張しないことがあり構築するためのあなたのアイデア:

  • が最大許容SQL文の固定長である - それは10000の挿入
  • では動作しません。
  • バインド変数を使用することをお勧めします。次に、n'xxx 'とunistrのどちらかの混乱を避けてください。
  • 新しいSQL文を動的に作成するという考えは、まったくリソースです。 Oracleが何かの実行計画をキャッシュすることは許可されておらず、Oracleは各呼び出し時にlooong文を解析しにくくします。

あなたが達成しようとしているのは、大量挿入です。 http://viralpatel.net/blogs/batch-insert-in-java-jdbc/

挿入速度も(実行する必要がある)トリガーと外部キー制約の影響を受けることに注意してください(これは実行する必要があります)。検証済み)。したがって、数千を超える行を挿入しようとしている場合は、トリガーと外部キー制約を無効にし、挿入後に有効にすることを検討してください。 (トリガーコールは失われますが、挿入後の制約の検証は影響を与えます)。

ロールバックセグメントのサイズも考慮してください。何百万ものレコードを挿入している場合は、膨大なロールバックセグメントが必要になります。これにより、記憶メディアが大幅に交換される可能性があります。 1000レコードごとにコミットするのがよい経験則です。

(Oracleは共有ロックの代わりにバージョン管理を使用しているため、コミットされていない変更がある表が一貫して読み込めます。同じテーブルを更新しようとする他の人間と干渉する)

+0

* "最大許容SQL文の長さが固定されているため、10000個の挿入では機能しません" *、単純に真ではありません。 Oracle **には長さ制限が固定されていません**、http://stackoverflow.com/questions/14355819/what-is-the-maximum-statement-length-in-oracleを参照してください。ところで、私たちの層は自動的にクエリをあらかじめ定義されたサイズに分割するので、そのようなことを心配する必要はありません。 – Teejay

+0

* "達成しようとしているのは一括挿入です.OracleドライバのJDBCバッチモードを使用してください" *一括挿入する方法がある、つまり書式付きテキストファイルから開始する方法があることは知っていますが、 。また、SqlServerとPostgresのクエリも作成します。ところで、誰もJavaについて言及していません.netと協力しています。 – Teejay

+0

* "トリガーと外部キーの制約を無効にすることを検討してください" *設定にはトリガーはありません。とにかく、トリガーはしばしばデータなしではできないことであることに注意してください。 – Teejay

関連する問題