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.