sql >> Database >  >> RDS >> Oracle

OdbcConnection retourneert Chinese karakters als ?

Problemen met de tekenset komen vrij vaak voor, ik zal proberen wat algemene opmerkingen te geven.

In principe moet je rekening houden met vier verschillende tekenset-instellingen.

1 en 2:NLS_CHARACTERSET en NLS_NCHAR_CHARACTERSET

Voorbeeld:AL32UTF8

Ze zijn alleen defined gedefinieerd in uw database, u kunt ze ondervragen met

    SELECT * 
    FROM V$NLS_PARAMETERS 
    WHERE PARAMETER IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET');

Deze instellingen bepalen welke tekens (in welk formaat) in uw database kunnen worden opgeslagen - niet meer en niet minder. Het vereist enige inspanning (zie Character Set Migration en/of Oracle Database Migration Assistant for Unicode) als u het op een bestaande database moet wijzigen.

3:NLS_LANG

Voorbeeld:AMERICAN_AMERICA.AL32UTF8

Deze waarde wordt alleen gedefinieerd op uw cliënt. NLS_LANG heeft niets te maken met de mogelijkheid om tekens in een database op te slaan. Het wordt gebruikt om Oracle te laten weten welke tekenset u aan de clientzijde gebruikt. Wanneer u de NLS_LANG-waarde instelt (bijvoorbeeld op AL32UTF8), zegt u gewoon tegen de Oracle-database "mijn klant gebruikt tekenset AL32UTF8" - het betekent niet noodzakelijk dat uw klant echt AL32UTF8 gebruikt! (zie hieronder #4)

NLS_LANG kan worden gedefinieerd door omgevingsvariabele NLS_LANG of via het Windows-register op HKLM\SOFTWARE\Wow6432Node\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG (voor 32 bit), resp. HKLM\SOFTWARE\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG (voor 64-bits). Afhankelijk van uw toepassing zijn er misschien andere manieren om NLS_LANG te specificeren, maar laten we het bij de basis houden. Als de NLS_LANG-waarde niet wordt opgegeven, stelt Oracle deze standaard in op AMERICAN_AMERICA.US7ASCII

Indeling van NLS_LANG is NLS_LANG=language_territory.charset . De {tekenset } een deel van NLS_LANG is niet weergegeven in elke systeemtabel of -weergave. Alle componenten van de NLS_LANG-definitie zijn optioneel, dus de volgende definities zijn allemaal geldig:NLS_LANG=.WE8ISO8859P1 , NLS_LANG=_GERMANY , NLS_LANG=AMERICAN , NLS_LANG=ITALIAN_.WE8MSWIN1252 , NLS_LANG=_BELGIUM.US7ASCII .

Zoals hierboven vermeld, het {charset}-gedeelte van NLS_LANG is niet beschikbaar in de database in een systeemtabel/view of enige functie. Strikt genomen is dit waar, maar u kunt deze query uitvoeren:

SELECT DISTINCT CLIENT_CHARSET
FROM V$SESSION_CONNECT_INFO
WHERE (SID, SERIAL#) = (SELECT SID, SERIAL# FROM v$SESSION WHERE AUDSID = USERENV('SESSIONID'));

Het zou een tekenset moeten retourneren uit je huidige NLS_LANG instelling - maar op basis van mijn ervaring is de waarde vaak NULL of Unknown , d.w.z. niet betrouwbaar.

Vind hier meer zeer nuttige informatie:NLS_LANG FAQ

Let op, sommige technologieën maken geen gebruik van NLS_LANG , instellingen daar hebben geen effect, bijvoorbeeld:

  • ODP.NET Managed Driver is niet NLS_LANG gevoelig. Het is alleen gevoelig voor .NET-landinstellingen. (zie Data Provider for .NET Developer's Guide)

  • OraOLEDB (van Oracle) gebruikt altijd UTF-16 (zie OraOLEDB Provider Specific Features)

  • Op Java gebaseerde JDBC (bijvoorbeeld SQL Developer) heeft zijn eigen methoden om met tekensets om te gaan (zie Database JDBC Developer's Guide - Globalization Support voor meer details)

4:De "echte" tekenset van uw terminal, uw applicatie of de codering van .sql bestanden

Voorbeeld:UTF-8

Als u op een Windows-terminal werkt (d.w.z. met SQL*plus) kunt u de codepagina opvragen met het commando chcp , op Unix/Linux is het equivalent locale charmap of echo $LANG . U kunt hier een lijst krijgen van alle Windows-codepagina-ID's:Codepagina-ID's. Let op, voor UTF-8 (chcp 65001 ) er zijn enkele problemen, zie deze discussie.

Als u werkt met .sql bestanden en een editor zoals TOAD of SQL-Developer moet je de opslagopties aanvinken. Meestal kunt u waarden kiezen zoals UTF-8 , ANSI , ISO-8859-1 , enz.ANSI betekent de Windows ANSI-codepagina, meestal CP1252 , kunt u uw register inchecken op HKLM\SYSTEM\ControlSet001\Control\Nls\CodePage\ACP of hier:National Language Support (NLS) API-referentie

[Microsoft heeft deze referentie verwijderd, neem het van web-archief National Language Support (NLS) API-referentie]

Hoe stel je al deze waarden in?

Het belangrijkste punt is om NLS_LANG . te matchen en uw "echte" tekenset van uw terminal, resp. applicatie of de codering van uw .sql bestanden

Enkele veelvoorkomende paren zijn:

  • CP850 -> WE8PC850

  • CP1252 of ANSI (in het geval van "westerse" pc) -> WE8MSWIN1252

  • ISO-8859-1 -> WE8ISO8859P1

  • ISO-8859-15 -> WE8ISO8859P15

  • UTF-8 -> AL32UTF8

Of voer deze query uit om meer te krijgen:

SELECT VALUE AS ORACLE_CHARSET, UTL_I18N.MAP_CHARSET(VALUE) AS IANA_NAME
FROM V$NLS_VALID_VALUES
WHERE PARAMETER = 'CHARACTERSET';

Sommige technologieën maken u het leven gemakkelijker, b.v. ODP.NET (unmanged driver) of ODBC-driver van Oracle neemt automatisch de tekenset over van NLS_LANG waarde, dus voorwaarde van boven is altijd waar.

Is het vereist om de NLS_LANG-waarde van de client gelijk te stellen aan de database NLS_CHARACTERSET waarde?

Nee, niet noodzakelijk! Als u bijvoorbeeld de database . heeft tekenset NLS_CHARACTERSET=AL32UTF8 en de klant tekenset NLS_LANG=.ZHS32GB18030 dan werkt het zonder enig probleem (mits je cliënt echt GB18030 gebruikt), hoewel deze karaktersets totaal verschillend zijn. GB18030 is een tekenset die veel wordt gebruikt voor Chinees, zoals UTF-8 het ondersteunt alle Unicode-tekens.

Als u bijvoorbeeld NLS_CHARACTERSET=AL32UTF8 . heeft en NLS_LANG=.WE8ISO8859P1 het zal ook werken (opnieuw, op voorwaarde dat uw klant echt ISO-8859-P1 gebruikt). De database kan echter tekens opslaan die uw client niet kan weergeven, in plaats daarvan zal de client een tijdelijke aanduiding weergeven (bijv. ¿ ).

Hoe dan ook, het is nuttig om overeenkomende NLS_LANG- en NLS_CHARACTERSET-waarden te hebben, indien van toepassing. Als ze gelijk zijn, kunt u er zeker van zijn dat elk teken dat in de database is opgeslagen, ook kan worden weergegeven en elk teken dat u in uw terminal invoert of in uw .sql-bestand schrijft, ook in de database kan worden opgeslagen en niet wordt vervangen door een tijdelijke aanduiding.

Aanvulling

Je kunt zo vaak adviezen lezen als "De NLS_LANG-tekenset moet hetzelfde zijn als je database-tekenset" (ook hier op SO). Dit is gewoon niet waar en een populaire mythe!

Hier is het bewijs:

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.

Zowel client- als databasetekensets zijn AL32UTF8 , maar de karakters komen niet overeen. De reden is, mijn cmd.exe en dus ook SQL*Plus gebruikt Windows CP1252. Daarom moet ik NLS_LANG dienovereenkomstig instellen:

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.

Bekijk ook dit voorbeeld:

CREATE TABLE ARABIC_LANGUAGE (
    LANG_CHAR VARCHAR2(20), 
    LANG_NCHAR NVARCHAR2(20));

INSERT INTO ARABIC_LANGUAGE VALUES ('العربية', 'العربية');

U moet twee verschillende waarden instellen voor NLS_LANG voor een enkele verklaring - wat niet mogelijk is.




  1. Gegevensmodel autoreparatiewerkplaats

  2. Hoe selecteer ik alle kolommen uit een tabel, plus extra kolommen zoals ROWNUM?

  3. Een variabele doorgeven aan een trigger

  4. Hoe de RIGHT()-functie werkt in MySQL