Problemer med tegnsæt er ret almindelige, lad mig prøve at give nogle generelle bemærkninger.
I princippet skal du overveje fire forskellige tegnsætindstillinger.
1 og 2:NLS_CHARACTERSET
og NLS_NCHAR_CHARACTERSET
Eksempel:AL32UTF8
De er kun defineret på din database, kan du udspørge dem med
SELECT *
FROM V$NLS_PARAMETERS
WHERE PARAMETER IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET');
Disse indstillinger definerer hvilke tegn (i hvilket format) der kan gemmes i din database - hverken mere eller mindre. Det kræver en vis indsats (se Character Set Migration og/eller Oracle Database Migration Assistant for Unicode), hvis du skal ændre det på eksisterende database.
3:NLS_LANG
Eksempel:AMERICAN_AMERICA.AL32UTF8
Denne værdi er kun defineret på din klient. NLS_LANG har intet at gøre med evnen til at gemme tegn i en database. Det bruges til at lade Oracle vide, hvilket tegnsæt du bruger på klientsiden. Når du indstiller NLS_LANG værdi (for eksempel til AL32UTF8) så fortæller du bare Oracle-databasen "min klient bruger tegnsæt AL32UTF8" - det betyder ikke nødvendigvis, at din klient virkelig bruger AL32UTF8! (se nedenfor #4)
NLS_LANG kan defineres af miljøvariablen NLS_LANG
eller af Windows-registreringsdatabasen på HKLM\SOFTWARE\Wow6432Node\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG
(for 32 bit), hhv. HKLM\SOFTWARE\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG
(til 64 bit). Afhængigt af din applikation kan der være andre måder at specificere NLS_LANG på, men lad os holde os til det grundlæggende. Hvis NLS_LANG-værdien ikke er angivet, indstiller Oracle den som standard til AMERICAN_AMERICA.US7ASCII
Formatet på NLS_LANG er NLS_LANG=language_territory.charset
. {Charset } del af NLS_LANG er ikke vist i enhver systemtabel eller visning. Alle komponenter i NLS_LANG-definitionen er valgfri, så følgende definitioner er alle gyldige:NLS_LANG=.WE8ISO8859P1
, NLS_LANG=_GERMANY
, NLS_LANG=AMERICAN
, NLS_LANG=ITALIAN_.WE8MSWIN1252
, NLS_LANG=_BELGIUM.US7ASCII
.
Som nævnt ovenfor {charset}-delen af NLS_LANG
er ikke tilgængelig i databasen på nogen systemtabel/visning eller nogen funktion. Strengt taget er dette sandt, men du kan køre denne forespørgsel:
SELECT DISTINCT CLIENT_CHARSET
FROM V$SESSION_CONNECT_INFO
WHERE (SID, SERIAL#) = (SELECT SID, SERIAL# FROM v$SESSION WHERE AUDSID = USERENV('SESSIONID'));
Det bør returnere tegnsæt fra din nuværende NLS_LANG
indstilling - men baseret på min erfaring er værdien ofte NULL eller Unknown
, dvs. ikke pålidelige.
Find mere meget nyttig information her:NLS_LANG FAQ
Bemærk, nogle teknologier bruger ikke NLS_LANG
, indstillinger der har ingen effekt, for eksempel:
-
ODP.NET Managed Driver er ikke
NLS_LANG
følsom. Det er kun .NET-lokalitetsfølsomt. (se Data Provider for .NET Developer's Guide) -
OraOLEDB (fra Oracle) brug altid UTF-16 (se OraOLEDB-udbyderspecifikke funktioner)
-
Java-baseret JDBC (for eksempel SQL Developer) har sine egne metoder til at håndtere tegnsæt (se Database JDBC Developer's Guide - Globalization Support for yderligere detaljer)
4:Det "rigtige" tegnsæt for din terminal, din applikation eller kodningen af .sql
filer
Eksempel:UTF-8
Hvis du arbejder på en Windows-terminal (dvs. med SQL*plus), kan du udspørge tegntabellen med kommandoen chcp
, på Unix/Linux er ækvivalenten locale charmap
eller echo $LANG
. Du kan få en liste over alle Windows-kodetabel-id'er her:Kodeside-id'er. Bemærk, for UTF-8 (chcp 65001
) der er nogle problemer, se denne diskussion.
Hvis du arbejder med .sql
filer og en editor som TOAD eller SQL-Developer, skal du tjekke gemmemulighederne. Normalt kan du vælge værdier som UTF-8
, ANSI
, ISO-8859-1
osv.ANSI
betyder Windows ANSI-kodetabel, typisk CP1252
, kan du tjekke i dit register på HKLM\SYSTEM\ControlSet001\Control\Nls\CodePage\ACP
eller her:National Language Support (NLS) API-reference
[Microsoft fjernede denne reference, tag den fra webarkiv National Language Support (NLS) API-reference]
Hvordan indstilles alle disse værdier?
Det vigtigste punkt er at matche NLS_LANG
og dit "rigtige" tegnsæt på din terminal, hhv. applikation eller kodning af din .sql
filer
Nogle almindelige par er:
-
CP850 ->
WE8PC850
-
CP1252 eller ANSI (i tilfælde af "vestlig" pc) ->
WE8MSWIN1252
-
ISO-8859-1 ->
WE8ISO8859P1
-
ISO-8859-15 ->
WE8ISO8859P15
-
UTF-8 ->
AL32UTF8
Eller kør denne forespørgsel for at få mere:
SELECT VALUE AS ORACLE_CHARSET, UTL_I18N.MAP_CHARSET(VALUE) AS IANA_NAME
FROM V$NLS_VALID_VALUES
WHERE PARAMETER = 'CHARACTERSET';
Nogle teknologier gør livet lettere for dig, f.eks. ODP.NET (umanged driver) eller ODBC driver fra Oracle arver automatisk tegnsættet fra NLS_LANG
værdi, så betingelse fra oven er altid sand.
Er det påkrævet at indstille klient NLS_LANG værdi lig med database NLS_CHARACTERSET
værdi?
Nej, ikke nødvendigvis! For eksempel, hvis du har databasen tegnsæt NLS_CHARACTERSET=AL32UTF8
og klienten tegnsæt NLS_LANG=.ZHS32GB18030
så vil det fungere uden problemer (forudsat at din klient virkelig bruger GB18030), selvom disse tegnsæt er helt anderledes. GB18030 er et tegnsæt, der almindeligvis bruges til kinesisk, såsom UTF-8
den understøtter alle Unicode-tegn.
Hvis du f.eks. har NLS_CHARACTERSET=AL32UTF8
og NLS_LANG=.WE8ISO8859P1
det vil også virke (igen, forudsat at din klient virkelig bruger ISO-8859-P1). Dog kan databasen gemme tegn, som din klient ikke er i stand til at vise, i stedet vil klienten vise en pladsholder (f.eks. ¿
).
Under alle omstændigheder er det en fordel at have matchende NLS_LANG- og NLS_CHARACTERSET-værdier, hvis det er passende. Hvis de er ens, kan du være sikker på, at ethvert tegn, der måtte være gemt i databasen, også kan vises, og ethvert tegn, du indtaster i din terminal eller skriver i din .sql-fil, kan også gemmes i databasen og erstattes ikke af pladsholder.
Tillæg
Så mange gange kan du læse råd som "NLS_LANG-tegnsættet skal være det samme som dit databasetegnsæt" (også her på SO). Dette er simpelthen ikke sandt og en populær myte!
Her er beviset:
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.
Både klient- og databasetegnsæt er AL32UTF8
, men karaktererne stemmer ikke overens. Årsagen er min cmd.exe
og dermed også SQL*Plus bruge Windows CP1252. Derfor skal jeg indstille NLS_LANG i overensstemmelse hermed:
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.
Overvej også dette eksempel:
CREATE TABLE ARABIC_LANGUAGE (
LANG_CHAR VARCHAR2(20),
LANG_NCHAR NVARCHAR2(20));
INSERT INTO ARABIC_LANGUAGE VALUES ('العربية', 'العربية');
Du skal indstille to forskellige værdier for NLS_LANG
for et enkelt udsagn - hvilket ikke er muligt.