sql >> Databasteknik >  >> RDS >> Oracle

OdbcConnection returnerar kinesiska tecken som ?

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.




  1. Hur använder man pg_stat_activity?

  2. Hur man aktiverar MySQL Query Cache

  3. Hur man deklarerar användardefinierat undantag med PRAGMA EXCEPTION_INIT

  4. PostgreSQL FÖRKLARA – Vilka är frågekostnaderna?