Oracle
 sql >> Base de données >  >> RDS >> Oracle

OdbcConnection renvoie les caractères chinois sous la forme ?

Les problèmes de jeu de caractères sont assez courants, permettez-moi d'essayer de donner quelques notes générales.

En principe, vous devez considérer quatre différents paramètres de jeu de caractères.

1 et 2 :NLS_CHARACTERSET et NLS_NCHAR_CHARACTERSET

Exemple :AL32UTF8

Ils sont définis uniquement sur votre base de données, vous pouvez les interroger avec

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

Ces paramètres définissent quels caractères (dans quel format) peuvent être stockés dans votre base de données - ni plus, ni moins. Cela demande un certain effort (voir Migration de jeu de caractères et/ou Assistant de migration de base de données Oracle pour Unicode) si vous devez le modifier sur une base de données existante.

3 :NLS_LANG

Exemple :AMERICAN_AMERICA.AL32UTF8

Cette valeur est définie uniquement sur votre client. NLS_LANG n'a rien à voir avec la possibilité de stocker des caractères dans une base de données. Il est utilisé pour informer Oracle du jeu de caractères que vous utilisez côté client. Lorsque vous définissez la valeur NLS_LANG (par exemple sur AL32UTF8), vous dites simplement à la base de données Oracle "mon client utilise le jeu de caractères AL32UTF8" - cela ne signifie pas nécessairement que votre client utilise vraiment AL32UTF8 ! (voir ci-dessous #4)

NLS_LANG peut être défini par la variable d'environnement NLS_LANG ou par le registre Windows sur HKLM\SOFTWARE\Wow6432Node\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG (pour 32 bits), resp. HKLM\SOFTWARE\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG (pour 64 bits). Selon votre application, il peut y avoir d'autres façons de spécifier NLS_LANG, mais restons-en aux bases. Si la valeur NLS_LANG n'est pas fournie, Oracle la définit par défaut sur AMERICAN_AMERICA.US7ASCII

Le format de NLS_LANG est NLS_LANG=language_territory.charset . Le {jeu de caractères } partie de NLS_LANG n'est pas affiché dans n'importe quelle table ou vue système. Tous les composants de la définition NLS_LANG sont facultatifs, donc les définitions suivantes sont toutes valides :NLS_LANG=.WE8ISO8859P1 , NLS_LANG=_GERMANY , NLS_LANG=AMERICAN , NLS_LANG=ITALIAN_.WE8MSWIN1252 , NLS_LANG=_BELGIUM.US7ASCII .

Comme indiqué ci-dessus, la partie {charset} de NLS_LANG n'est disponible dans la base de données d'aucune table/vue système ni d'aucune fonction. Strictement parlant, c'est vrai, mais vous pouvez exécuter cette requête :

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

Il doit renvoyer le jeu de caractères de votre NLS_LANG actuel paramètre - cependant, d'après mon expérience, la valeur est souvent NULL ou Unknown , c'est-à-dire non fiable.

Trouvez plus d'informations très utiles ici :NLS_LANG FAQ

Notez que certaines technologies n'utilisent pas NLS_LANG , les paramètres n'ont aucun effet, par exemple :

  • Le pilote géré ODP.NET n'est pas NLS_LANG sensible. Il n'est sensible qu'aux paramètres régionaux .NET. (voir Guide du développeur du fournisseur de données pour .NET)

  • OraOLEDB (d'Oracle) utilise toujours UTF-16 (voir Fonctionnalités spécifiques au fournisseur OraOLEDB)

  • JDBC basé sur Java (par exemple SQL Developer) a ses propres méthodes pour gérer les jeux de caractères (voir Database JDBC Developer's Guide - Globalization Support pour plus de détails)

4 :Le "vrai" jeu de caractères de votre terminal, votre application ou l'encodage de .sql fichiers

Exemple :UTF-8

Si vous travaillez sur un terminal Windows (c'est-à-dire avec SQL*plus) vous pouvez interroger la page de code avec la commande chcp , sous Unix/Linux l'équivalent est locale charmap ou echo $LANG . Vous pouvez obtenir une liste de tous les identificateurs de pages de code Windows à partir d'ici :Identifiants de page de code. Remarque, pour UTF-8 (chcp 65001 ) il y a des problèmes, voir cette discussion.

Si vous travaillez avec .sql fichiers et un éditeur comme TOAD ou SQL-Developer, vous devez vérifier les options de sauvegarde. Généralement, vous pouvez choisir des valeurs telles que UTF-8 , ANSI , ISO-8859-1 , etc.ANSI signifie la page de code Windows ANSI, généralement CP1252 , vous pouvez vérifier dans votre Registre à HKLM\SYSTEM\ControlSet001\Control\Nls\CodePage\ACP ou ici :Référence de l'API National Language Support (NLS)

[Microsoft a supprimé cette référence, prenez-la dans l'archive Web National Language Support (NLS) API Reference]

Comment définir toutes ces valeurs ?

Le point le plus important est de faire correspondre NLS_LANG et votre "vrai" jeu de caractères de votre terminal, resp. application ou l'encodage de votre .sql fichiers

Certaines paires courantes sont :

  • CP850 -> WE8PC850

  • CP1252 ou ANSI (en cas de PC "occidental") -> WE8MSWIN1252

  • ISO-8859-1 -> WE8ISO8859P1

  • ISO-8859-15 -> WE8ISO8859P15

  • UTF-8 -> AL32UTF8

Ou exécutez cette requête pour en savoir plus :

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

Certaines technologies vous facilitent la vie, par ex. ODP.NET (pilote non géré) ou le pilote ODBC d'Oracle hérite automatiquement du jeu de caractères de NLS_LANG valeur, donc la condition ci-dessus est toujours vraie.

Est-il nécessaire de définir la valeur NLS_LANG du client égale à la base de données NLS_CHARACTERSET valeur ?

Non pas forcément! Par exemple, si vous avez la base de données jeu de caractères NLS_CHARACTERSET=AL32UTF8 et le client jeu de caractères NLS_LANG=.ZHS32GB18030 alors cela fonctionnera sans aucun problème (à condition que votre client utilise vraiment GB18030), bien que ces jeux de caractères soient complètement différents. GB18030 est un jeu de caractères couramment utilisé pour le chinois, comme UTF-8 il prend en charge tous les caractères Unicode.

Si vous avez, par exemple NLS_CHARACTERSET=AL32UTF8 et NLS_LANG=.WE8ISO8859P1 cela fonctionnera également (encore une fois, à condition que votre client utilise vraiment ISO-8859-P1). Cependant, la base de données peut stocker des caractères que votre client n'est pas en mesure d'afficher, à la place, le client affichera un espace réservé (par exemple ¿ ).

Quoi qu'il en soit, il est avantageux d'avoir des valeurs NLS_LANG et NLS_CHARACTERSET correspondantes, le cas échéant. S'ils sont égaux, vous pouvez être sûr que tout caractère pouvant être stocké dans la base de données peut également être affiché et que tout caractère que vous entrez dans votre terminal ou écrivez dans votre fichier .sql peut également être stocké dans la base de données et n'est pas remplacé par un espace réservé.

Supplément

Tant de fois, vous pouvez lire des conseils comme "Le jeu de caractères NLS_LANG doit être le même que le jeu de caractères de votre base de données" (également ici sur SO). Ce n'est tout simplement pas vrai et c'est un mythe populaire !

Voici la preuve :

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.

Les jeux de caractères du client et de la base de données sont AL32UTF8 , mais les caractères ne correspondent pas. La raison en est, mon cmd.exe et donc aussi SQL*Plus utilisent Windows CP1252. Par conséquent, je dois définir NLS_LANG en conséquence :

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.

Considérez également cet exemple :

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

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

Vous auriez besoin de définir deux valeurs différentes pour NLS_LANG pour une seule déclaration - ce qui n'est pas possible.