Problem med teckenuppsättning är ganska vanliga, låt mig försöka ge några allmänna anteckningar.
I princip måste du överväga fyra olika teckenuppsättningsinställningar.
1 och 2:NLS_CHARACTERSET
och NLS_NCHAR_CHARACTERSET
Exempel:AL32UTF8
De definieras endast i din databas kan du fråga dem med
SELECT *
FROM V$NLS_PARAMETERS
WHERE PARAMETER IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET');
Dessa inställningar definierar vilka tecken (i vilket format) som kan lagras i din databas - varken mer eller mindre. Det kräver viss ansträngning (se Migrering av teckenuppsättningar och/eller Oracle Database Migration Assistant för Unicode) om du måste ändra den på befintlig databas.
3:NLS_LANG
Exempel:AMERICAN_AMERICA.AL32UTF8
Detta värde definieras endast på din klient. NLS_LANG har inget att göra med möjligheten att lagra tecken i en databas. Den används för att låta Oracle veta vilken teckenuppsättning du använder på klientsidan. När du ställer in NLS_LANG-värdet (till exempel till AL32UTF8) säger du bara till Oracle-databasen "min klient använder teckenuppsättningen AL32UTF8" - det betyder inte nödvändigtvis att din klient verkligen använder AL32UTF8! (se nedan #4)
NLS_LANG kan definieras av miljövariabeln NLS_LANG
eller av Windows-registret på HKLM\SOFTWARE\Wow6432Node\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG
(för 32 bitar), resp. HKLM\SOFTWARE\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG
(för 64 bitar). Beroende på din applikation kan det finnas andra sätt att specificera NLS_LANG, men låt oss hålla oss till grunderna. Om NLS_LANG-värdet inte tillhandahålls använder Oracle det som standard till AMERICAN_AMERICA.US7ASCII
Formatet för NLS_LANG är NLS_LANG=language_territory.charset
. {teckenuppsättningen } del av NLS_LANG är inte visas i valfri systemtabell eller vy. Alla komponenter i NLS_LANG-definitionen är valfria, så följande definitioner är alla giltiga:NLS_LANG=.WE8ISO8859P1
, NLS_LANG=_GERMANY
, NLS_LANG=AMERICAN
, NLS_LANG=ITALIAN_.WE8MSWIN1252
, NLS_LANG=_BELGIUM.US7ASCII
.
Som nämnts ovan {charset}-delen av NLS_LANG
är inte tillgänglig i databasen i någon systemtabell/vy eller någon funktion. Strängt taget är detta sant, men du kan köra den här frågan:
SELECT DISTINCT CLIENT_CHARSET
FROM V$SESSION_CONNECT_INFO
WHERE (SID, SERIAL#) = (SELECT SID, SERIAL# FROM v$SESSION WHERE AUDSID = USERENV('SESSIONID'));
Den bör returnera teckenuppsättning från din nuvarande NLS_LANG
inställning - men baserat på min erfarenhet är värdet ofta NULL eller Unknown
, d.v.s. inte tillförlitlig.
Hitta mer mycket användbar information här:NLS_LANG FAQ
Observera att vissa tekniker inte använder NLS_LANG
, inställningar där har ingen effekt, till exempel:
-
ODP.NET Managed Driver är inte
NLS_LANG
känslig. Det är bara .NET-språkkänsligt. (se Dataleverantör för .NET Developer's Guide) -
OraOLEDB (från Oracle) använder alltid UTF-16 (se OraOLEDB-leverantörsspecifika funktioner)
-
Javabaserad JDBC (till exempel SQL Developer) har sina egna metoder för att hantera teckenuppsättningar (se Databas JDBC Developer's Guide - Globalization Support för ytterligare detaljer)
4:Den "riktiga" teckenuppsättningen för din terminal, din applikation eller kodningen av .sql
filer
Exempel:UTF-8
Om du arbetar på en Windows-terminal (dvs med SQL*plus) kan du fråga teckentabellen med kommandot chcp
, på Unix/Linux är motsvarigheten locale charmap
eller echo $LANG
. Du kan få en lista över alla Windows-kodsidors-identifierare här:Code Page Identifiers. Obs, för UTF-8 (chcp 65001
) det finns några problem, se den här diskussionen.
Om du arbetar med .sql
filer och en redigerare som TOAD eller SQL-Developer måste du kontrollera sparalternativen. Vanligtvis kan du välja värden som UTF-8
, ANSI
, ISO-8859-1
, etc.ANSI
betyder Windows ANSI-kodtabell, vanligtvis CP1252
, kan du checka in ditt register på HKLM\SYSTEM\ControlSet001\Control\Nls\CodePage\ACP
eller här:National Language Support (NLS) API-referens
[Microsoft tog bort denna referens, ta den från webbarkivet National Language Support (NLS) API-referens]
Hur ställer man in alla dessa värden?
Den viktigaste punkten är att matcha NLS_LANG
och din "riktiga" teckenuppsättning av din terminal, resp. applikation eller kodningen av din .sql
filer
Några vanliga par är:
-
CP850 ->
WE8PC850
-
CP1252 eller ANSI (vid "västerländsk" PC) ->
WE8MSWIN1252
-
ISO-8859-1 ->
WE8ISO8859P1
-
ISO-8859-15 ->
WE8ISO8859P15
-
UTF-8 ->
AL32UTF8
Eller kör den här frågan för att få mer:
SELECT VALUE AS ORACLE_CHARSET, UTL_I18N.MAP_CHARSET(VALUE) AS IANA_NAME
FROM V$NLS_VALID_VALUES
WHERE PARAMETER = 'CHARACTERSET';
Vissa tekniker gör ditt liv enklare, t.ex. ODP.NET (ohanterad drivrutin) eller ODBC-drivrutin från Oracle ärver automatiskt teckenuppsättningen från NLS_LANG
värde, så villkor från ovan är alltid sant.
Krävs det att ställa in klientens NLS_LANG-värde lika med databasen NLS_CHARACTERSET
värde?
Nej, inte nödvändigtvis! Till exempel, om du har databasen teckenuppsättning NLS_CHARACTERSET=AL32UTF8
och klienten teckenuppsättning NLS_LANG=.ZHS32GB18030
då kommer det att fungera utan problem (förutsatt att din klient verkligen använder GB18030), även om dessa teckenuppsättningar är helt olika. GB18030 är en teckenuppsättning som vanligtvis används för kinesiska, som UTF-8
den stöder alla Unicode-tecken.
Om du till exempel har NLS_CHARACTERSET=AL32UTF8
och NLS_LANG=.WE8ISO8859P1
det kommer också att fungera (igen, förutsatt att din klient verkligen använder ISO-8859-P1). Databasen kan dock lagra tecken som din klient inte kan visa, istället kommer klienten att visa en platshållare (t.ex. ¿
).
Hur som helst, det är fördelaktigt att ha matchande NLS_LANG- och NLS_CHARACTERSET-värden, om lämpligt. Om de är lika kan du vara säker på att alla tecken som kan lagras i databasen också kan visas och alla tecken du anger i din terminal eller skriver i din .sql-fil kan också lagras i databasen och ersätts inte av platshållare.
Tillägg
Så många gånger kan du läsa råd som "NLS_LANG-teckenuppsättningen måste vara densamma som din databasteckenuppsättning" (även här på SO). Detta är helt enkelt inte sant och en populär myt!
Här är 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- och databasteckenuppsättningar är AL32UTF8
, men karaktärerna matchar inte. Anledningen är min cmd.exe
och därmed även SQL*Plus använder Windows CP1252. Därför måste jag ställa in NLS_LANG i enlighet med detta:
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.
Tänk också på det här exemplet:
CREATE TABLE ARABIC_LANGUAGE (
LANG_CHAR VARCHAR2(20),
LANG_NCHAR NVARCHAR2(20));
INSERT INTO ARABIC_LANGUAGE VALUES ('العربية', 'العربية');
Du skulle behöva ställa in två olika värden för NLS_LANG
för ett enda påstående - vilket inte är möjligt.