Problemy z zestawem znaków są dość powszechne, spróbuję podać kilka ogólnych uwag.
W zasadzie musisz wziąć pod uwagę cztery różne ustawienia zestawu znaków.
1 i 2:NLS_CHARACTERSET
i NLS_NCHAR_CHARACTERSET
Przykład:AL32UTF8
Są one zdefiniowane tylko w swojej bazie danych, możesz je przesłuchać za pomocą
SELECT *
FROM V$NLS_PARAMETERS
WHERE PARAMETER IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET');
Te ustawienia określają, które znaki (w jakim formacie) mogą być przechowywane w Twojej bazie danych — nie więcej, nie mniej. Wymaga to trochę wysiłku (zobacz Migracja zestawu znaków i/lub Asystent migracji bazy danych Oracle dla Unicode), jeśli musisz to zmienić w istniejącej bazie danych.
3:NLS_LANG
Przykład:AMERICAN_AMERICA.AL32UTF8
Ta wartość jest zdefiniowana tylko na twoim kliencie. NLS_LANG nie ma nic wspólnego z możliwością przechowywania znaków w bazie danych. Służy do poinformowania Oracle, jakiego zestawu znaków używasz po stronie klienta. Kiedy ustawisz wartość NLS_LANG (na przykład na AL32UTF8), po prostu poinformujesz bazę danych Oracle „mój klient używa zestawu znaków AL32UTF8” - niekoniecznie oznacza to, że twój klient naprawdę używa AL32UTF8! (patrz poniżej 4)
NLS_LANG można zdefiniować za pomocą zmiennej środowiskowej NLS_LANG
lub przez Rejestr systemu Windows pod adresem HKLM\SOFTWARE\Wow6432Node\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG
(dla wersji 32-bitowej), ewent. HKLM\SOFTWARE\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG
(dla wersji 64-bitowej). W zależności od aplikacji mogą istnieć inne sposoby określenia NLS_LANG, ale trzymajmy się podstaw. Jeśli wartość NLS_LANG nie zostanie podana, Oracle domyślnie ustawi ją na AMERICAN_AMERICA.US7ASCII
Format NLS_LANG to NLS_LANG=language_territory.charset
. {zestaw znaków } częścią NLS_LANG nie pokazane w dowolnej tabeli systemowej lub widoku. Wszystkie składniki definicji NLS_LANG są opcjonalne, więc wszystkie poniższe definicje są poprawne:NLS_LANG=.WE8ISO8859P1
, NLS_LANG=_GERMANY
, NLS_LANG=AMERICAN
, NLS_LANG=ITALIAN_.WE8MSWIN1252
, NLS_LANG=_BELGIUM.US7ASCII
.
Jak wspomniano powyżej, {charset} część NLS_LANG
nie jest dostępna w bazie danych w żadnej tabeli/widoku systemowym ani żadnej funkcji. Ściśle mówiąc to prawda, jednak możesz uruchomić to zapytanie:
SELECT DISTINCT CLIENT_CHARSET
FROM V$SESSION_CONNECT_INFO
WHERE (SID, SERIAL#) = (SELECT SID, SERIAL# FROM v$SESSION WHERE AUDSID = USERENV('SESSIONID'));
Powinien zwrócić zestaw znaków z twojego obecnego NLS_LANG
ustawienie - jednak z mojego doświadczenia wynika, że wartość jest często NULL lub Unknown
, czyli nie jest wiarygodne.
Więcej przydatnych informacji znajdziesz tutaj:NLS_LANG FAQ
Uwaga, niektóre technologie nie wykorzystują NLS_LANG
, ustawienia tam nie działają, na przykład:
-
Zarządzany sterownik ODP.NET nie jest
NLS_LANG
wrażliwy. Jest wrażliwy tylko na ustawienia regionalne platformy .NET. (zobacz Dostawca danych dla .NET Developer's Guide) -
OraOLEDB (od Oracle) zawsze używaj UTF-16 (zobacz Specyficzne funkcje dostawcy OraOLEDB)
-
JDBC oparty na Javie (na przykład SQL Developer) ma własne metody radzenia sobie z zestawami znaków (więcej szczegółów znajdziesz w Database JDBC Developer's Guide - Globalization Support)
4:"Prawdziwy" zestaw znaków twojego terminala, twojej aplikacji lub kodowania .sql
pliki
Przykład:UTF-8
Jeśli pracujesz na terminalu Windows (np. z SQL*plus), możesz sprawdzić stronę kodową poleceniem chcp
, w systemie Unix/Linux odpowiednikiem jest locale charmap
lub echo $LANG
. Listę wszystkich identyfikatorów stron kodowych systemu Windows można pobrać stąd:Identyfikatory stron kodowych. Uwaga, dla UTF-8 (chcp 65001
) są pewne problemy, zobacz tę dyskusję.
Jeśli pracujesz z .sql
plików i edytora typu TOAD lub SQL-Developer musisz sprawdzić opcje zapisu. Zwykle możesz wybrać wartości takie jak UTF-8
, ANSI
, ISO-8859-1
itp.ANSI
oznacza stronę kodową Windows ANSI, zwykle CP1252
, możesz sprawdzić w swoim Rejestrze pod adresem HKLM\SYSTEM\ControlSet001\Control\Nls\CodePage\ACP
lub tutaj:Dokumentacja API National Language Support (NLS)
[Microsoft usunął to odniesienie, weź je z archiwum internetowego Dokumentacja API obsługi języków narodowych (NLS)]
Jak ustawić wszystkie te wartości?
Najważniejszym punktem jest dopasowanie NLS_LANG
i twój "prawdziwy" zestaw znaków twojego terminala, ewent. aplikacji lub kodowania twojego pliku .sql
pliki
Niektóre popularne pary to:
-
CP850 ->
WE8PC850
-
CP1252 lub ANSI (w przypadku „zachodniego” komputera PC) ->
WE8MSWIN1252
-
ISO-8859-1 ->
WE8ISO8859P1
-
ISO-8859-15 ->
WE8ISO8859P15
-
UTF-8 ->
AL32UTF8
Lub uruchom to zapytanie, aby uzyskać więcej informacji:
SELECT VALUE AS ORACLE_CHARSET, UTL_I18N.MAP_CHARSET(VALUE) AS IANA_NAME
FROM V$NLS_VALID_VALUES
WHERE PARAMETER = 'CHARACTERSET';
Niektóre technologie ułatwiają życie, m.in. ODP.NET (sterownik unmanged) lub sterownik ODBC firmy Oracle automatycznie dziedziczy zestaw znaków z NLS_LANG
wartość, więc warunek z góry jest zawsze prawdziwy.
Czy wymagane jest ustawienie wartości NLS_LANG klienta równej bazie danych NLS_CHARACTERSET
wartość?
Nie, niekoniecznie! Na przykład, jeśli masz bazę danych zestaw znaków NLS_CHARACTERSET=AL32UTF8
i klient zestaw znaków NLS_LANG=.ZHS32GB18030
wtedy będzie działać bez problemu (pod warunkiem, że twój klient naprawdę używa GB18030), chociaż te zestawy znaków są zupełnie inne. GB18030 to zestaw znaków powszechnie używany w języku chińskim, np. UTF-8
obsługuje wszystkie znaki Unicode.
Jeśli masz na przykład NLS_CHARACTERSET=AL32UTF8
i NLS_LANG=.WE8ISO8859P1
to też zadziała (znowu, pod warunkiem, że twój klient naprawdę używa ISO-8859-P1). Jednak baza danych może przechowywać znaki, których twój klient nie jest w stanie wyświetlić, zamiast tego klient wyświetli symbol zastępczy (np. ¿
).
W każdym razie korzystne jest posiadanie pasujących wartości NLS_LANG i NLS_CHARACTERSET, jeśli jest to odpowiednie. Jeśli są równe, możesz być pewien, że każdy znak, który może być przechowywany w bazie danych, może być również wyświetlony, a każdy znak, który wprowadzisz w terminalu lub zapiszesz w pliku .sql, może być również przechowywany w bazie danych i nie jest zastępowany przez symbol zastępczy.
Suplement
Tak wiele razy można przeczytać porady, takie jak „Zestaw znaków NLS_LANG musi być taki sam, jak zestaw znaków bazy danych” (również tutaj w SO). To po prostu nieprawda i popularny mit!
Oto dowód:
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.
Zestawy znaków klienta i bazy danych to AL32UTF8
, jednak znaki się nie zgadzają. Powodem jest to, że mój cmd.exe
dlatego też SQL*Plus używa Windows CP1252. Dlatego muszę odpowiednio ustawić NLS_LANG:
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.
Rozważ także ten przykład:
CREATE TABLE ARABIC_LANGUAGE (
LANG_CHAR VARCHAR2(20),
LANG_NCHAR NVARCHAR2(20));
INSERT INTO ARABIC_LANGUAGE VALUES ('العربية', 'العربية');
Będziesz musiał ustawić dwie różne wartości dla NLS_LANG
dla jednego oświadczenia - co nie jest możliwe.