Oracle
 sql >> Datenbank >  >> RDS >> Oracle

OdbcConnection gibt chinesische Schriftzeichen als ?

Probleme mit dem Zeichensatz sind ziemlich häufig, lassen Sie mich versuchen, einige allgemeine Anmerkungen zu machen.

Im Prinzip müssen Sie vier berücksichtigen verschiedene Zeichensatzeinstellungen.

1 und 2:NLS_CHARACTERSET und NLS_NCHAR_CHARACTERSET

Beispiel:AL32UTF8

Sie sind nur definiert auf Ihrer Datenbank, können Sie diese mit

abfragen
    SELECT * 
    FROM V$NLS_PARAMETERS 
    WHERE PARAMETER IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET');

Diese Einstellungen definieren, welche Zeichen (in welchem ​​Format) in Ihrer Datenbank gespeichert werden können - nicht mehr und nicht weniger. Es erfordert etwas Aufwand (siehe Zeichensatzmigration und/oder Oracle-Datenbankmigrationsassistent für Unicode), wenn Sie ihn in einer vorhandenen Datenbank ändern müssen.

3:NLS_LANG

Beispiel:AMERICAN_AMERICA.AL32UTF8

Dieser Wert ist nur definiert auf Ihren Kunden. NLS_LANG hat nichts mit der Fähigkeit zu tun, Zeichen in einer Datenbank zu speichern. Es wird verwendet, um Oracle mitzuteilen, welchen Zeichensatz Sie auf der Clientseite verwenden. Wenn Sie den NLS_LANG-Wert festlegen (z. B. auf AL32UTF8), teilen Sie der Oracle-Datenbank einfach mit, dass "mein Client den Zeichensatz AL32UTF8 verwendet" - dies bedeutet nicht unbedingt, dass Ihr Client wirklich AL32UTF8 verwendet! (siehe unten #4)

NLS_LANG kann durch die Umgebungsvariable NLS_LANG definiert werden oder über die Windows-Registrierung unter HKLM\SOFTWARE\Wow6432Node\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG (für 32 Bit), bzw. HKLM\SOFTWARE\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG (für 64bit). Abhängig von Ihrer Anwendung gibt es möglicherweise andere Möglichkeiten, NLS_LANG anzugeben, aber bleiben wir bei den Grundlagen. Wenn der NLS_LANG-Wert nicht bereitgestellt wird, setzt Oracle ihn standardmäßig auf AMERICAN_AMERICA.US7ASCII

Das Format von NLS_LANG ist NLS_LANG=language_territory.charset . Der {Zeichensatz } Teil von NLS_LANG ist nicht in jeder Systemtabelle oder Ansicht angezeigt. Alle Komponenten der NLS_LANG-Definition sind optional, daher sind die folgenden Definitionen alle gültig:NLS_LANG=.WE8ISO8859P1 , NLS_LANG=_GERMANY , NLS_LANG=AMERICAN , NLS_LANG=ITALIAN_.WE8MSWIN1252 , NLS_LANG=_BELGIUM.US7ASCII .

Wie oben erwähnt, der {charset}-Teil von NLS_LANG ist in der Datenbank in keiner Systemtabelle/Ansicht oder Funktion verfügbar. Genau genommen stimmt das, aber Sie können diese Abfrage ausführen:

SELECT DISTINCT CLIENT_CHARSET
FROM V$SESSION_CONNECT_INFO
WHERE (SID, SERIAL#) = (SELECT SID, SERIAL# FROM v$SESSION WHERE AUDSID = USERENV('SESSIONID'));

Es sollte den Zeichensatz Ihres aktuellen NLS_LANG zurückgeben Einstellung - meiner Erfahrung nach ist der Wert jedoch oft NULL oder Unknown , d. h. nicht zuverlässig.

Weitere sehr nützliche Informationen finden Sie hier:NLS_LANG FAQ

Beachten Sie, dass einige Technologien NLS_LANG nicht verwenden , Einstellungen dort haben keine Auswirkung, zum Beispiel:

  • ODP.NET Managed Driver ist nicht NLS_LANG empfidlich. Es ist nur .NET-Gebietsschema-sensitiv. (siehe Data Provider for .NET Developer's Guide)

  • OraOLEDB (von Oracle) verwendet immer UTF-16 (siehe OraOLEDB Provider Specific Features)

  • Java-basiertes JDBC (z. B. SQL Developer) verfügt über eigene Methoden zum Umgang mit Zeichensätzen (weitere Einzelheiten finden Sie im Database JDBC Developer's Guide - Globalization Support)

4:Der "echte" Zeichensatz Ihres Terminals, Ihrer Anwendung oder die Kodierung von .sql Dateien

Beispiel:UTF-8

Wenn Sie auf einem Windows-Terminal (also mit SQL*plus) arbeiten, können Sie die Codepage mit dem Befehl chcp abfragen , unter Unix/Linux ist das Äquivalent locale charmap oder echo $LANG . Eine Liste aller Windows-Codepage-Identifikatoren erhalten Sie hier:Codepage-Identifikatoren. Hinweis:Für UTF-8 (chcp 65001 ) gibt es einige Probleme, siehe diese Diskussion.

Wenn Sie mit .sql arbeiten Dateien und einem Editor wie TOAD oder SQL-Developer müssen Sie die Speicheroptionen überprüfen. Normalerweise können Sie Werte wie UTF-8 auswählen , ANSI , ISO-8859-1 usw.ANSI bedeutet die Windows-ANSI-Codepage, typischerweise CP1252 , können Sie Ihre Registrierung unter HKLM\SYSTEM\ControlSet001\Control\Nls\CodePage\ACP einchecken oder hier:National Language Support (NLS) API Reference

[Microsoft hat diesen Verweis entfernt, nehmen Sie ihn aus dem Web-Archiv National Language Support (NLS) API Reference]

Wie stellt man all diese Werte ein?

Der wichtigste Punkt ist die Übereinstimmung mit NLS_LANG und Ihrem "echten" Zeichensatz Ihres Terminals, bzw. Anwendung oder die Kodierung Ihrer .sql Dateien

Einige gebräuchliche Paare sind:

  • CP850 -> WE8PC850

  • CP1252 oder ANSI (bei "westlichem" PC) -> WE8MSWIN1252

  • ISO-8859-1 -> WE8ISO8859P1

  • ISO-8859-15 -> WE8ISO8859P15

  • UTF-8 -> AL32UTF8

Oder führen Sie diese Abfrage aus, um mehr zu erhalten:

SELECT VALUE AS ORACLE_CHARSET, UTL_I18N.MAP_CHARSET(VALUE) AS IANA_NAME
FROM V$NLS_VALID_VALUES
WHERE PARAMETER = 'CHARACTERSET';

Einige Technologien machen Ihnen das Leben leichter, z. ODP.NET (nicht verwalteter Treiber) oder ODBC-Treiber von Oracle erbt automatisch den Zeichensatz von NLS_LANG Wert, also ist die obige Bedingung immer wahr.

Ist es erforderlich, den NLS_LANG-Wert des Clients gleich dem NLS_CHARACTERSET der Datenbank zu setzen Wert?

Nein, nicht unbedingt! Zum Beispiel, wenn Sie die Datenbank haben Zeichensatz NLS_CHARACTERSET=AL32UTF8 und der Kunde Zeichensatz NLS_LANG=.ZHS32GB18030 dann funktioniert es problemlos (vorausgesetzt Ihr Client verwendet wirklich GB18030), obwohl diese Zeichensätze völlig unterschiedlich sind. GB18030 ist ein Zeichensatz, der häufig für Chinesisch verwendet wird, wie UTF-8 es unterstützt alle Unicode-Zeichen.

Wenn Sie beispielsweise NLS_CHARACTERSET=AL32UTF8 haben und NLS_LANG=.WE8ISO8859P1 es wird auch funktionieren (vorausgesetzt, Ihr Client verwendet wirklich ISO-8859-P1). Die Datenbank kann jedoch Zeichen speichern, die Ihr Client nicht anzeigen kann, stattdessen zeigt der Client einen Platzhalter an (z. B. ¿ ).

Auf jeden Fall ist es vorteilhaft, passende NLS_LANG- und NLS_CHARACTERSET-Werte zu haben, falls geeignet. Wenn sie gleich sind, können Sie sicher sein, dass jedes Zeichen, das möglicherweise in der Datenbank gespeichert ist, auch angezeigt werden kann und jedes Zeichen, das Sie in Ihr Terminal eingeben oder in Ihre .sql-Datei schreiben, auch in der Datenbank gespeichert werden kann und nicht durch Platzhalter ersetzt wird.

Ergänzung

So oft können Sie Ratschläge lesen wie "Der NLS_LANG-Zeichensatz muss mit dem Zeichensatz Ihrer Datenbank übereinstimmen" (auch hier auf SO). Das ist einfach nicht wahr und ein weit verbreiteter Mythos!

Hier ist der Beweis:

C:\>set NLS_LANG=.AL32UTF8

C:\>sqlplus ...

SQL> SET SERVEROUTPUT ON
SQL> DECLARE
  2  CharSet VARCHAR2(20);
  3  BEGIN
  4     SELECT VALUE INTO Charset FROM nls_database_parameters WHERE parameter = 'NLS_CHARACTERSET';
  5     DBMS_OUTPUT.PUT_LINE('Database NLS_CHARACTERSET is '||Charset);
  6     IF UNISTR('\20AC') = '€' THEN
  7             DBMS_OUTPUT.PUT_LINE ( '"€" is equal to U+20AC' );
  8     ELSE
  9             DBMS_OUTPUT.PUT_LINE ( '"€" is not the same as U+20AC' );
 10     END IF;
 11  END;
 12  /

Database NLS_CHARACTERSET is AL32UTF8
"€" is not the same as U+20AC

PL/SQL procedure successfully completed.

Sowohl der Client- als auch der Datenbankzeichensatz sind AL32UTF8 , die Zeichen stimmen jedoch nicht überein. Der Grund ist meine cmd.exe und damit auch SQL*Plus verwenden Windows CP1252. Daher muss ich NLS_LANG entsprechend setzen:

C:\>chcp
Active code page: 1252

C:\>set NLS_LANG=.WE8MSWIN1252

C:\>sqlplus ...

SQL> SET SERVEROUTPUT ON
SQL> DECLARE
  2  CharSet VARCHAR2(20);
  3  BEGIN
  4     SELECT VALUE INTO Charset FROM nls_database_parameters WHERE parameter = 'NLS_CHARACTERSET';
  5     DBMS_OUTPUT.PUT_LINE('Database NLS_CHARACTERSET is '||Charset);
  6     IF UNISTR('\20AC') = '€' THEN
  7             DBMS_OUTPUT.PUT_LINE ( '"€" is equal to U+20AC' );
  8     ELSE
  9             DBMS_OUTPUT.PUT_LINE ( '"€" is not the same as U+20AC' );
 10     END IF;
 11  END;
 12  /

Database NLS_CHARACTERSET is AL32UTF8
"€" is equal to U+20AC

PL/SQL procedure successfully completed.

Betrachten Sie auch dieses Beispiel:

CREATE TABLE ARABIC_LANGUAGE (
    LANG_CHAR VARCHAR2(20), 
    LANG_NCHAR NVARCHAR2(20));

INSERT INTO ARABIC_LANGUAGE VALUES ('العربية', 'العربية');

Sie müssten zwei unterschiedliche Werte für NLS_LANG festlegen für eine einzelne Anweisung - was nicht möglich ist.