Oracle
 sql >> база данни >  >> RDS >> Oracle

OdbcConnection връща китайски символи като ?

Проблемите с набора от знаци са доста чести, нека се опитам да дам някои общи бележки.

По принцип трябва да вземете предвид четири различни настройки за набор от знаци.

1 и 2:NLS_CHARACTERSET и NLS_NCHAR_CHARACTERSET

Пример:AL32UTF8

Те са дефинани във вашата база данни, можете да ги разпитвате с

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

Тези настройки определят кои знаци (в какъв формат) могат да се съхраняват във вашата база данни - нито повече, нито по-малко. Изисква известно усилие (вижте Миграция на набор от символи и/или помощник за мигриране на база данни Oracle за Unicode), ако трябва да го промените в съществуваща база данни.

3:NLS_LANG

Пример:AMERICAN_AMERICA.AL32UTF8

Тази стойност е дефинирана само на вашия клиент. NLS_LANG няма нищо общо с възможността за съхраняване на знаци в база данни. Използва се, за да уведоми Oracle какъв набор от знаци използвате от страна на клиента. Когато зададете стойност NLS_LANG (например на AL32UTF8), тогава просто казвате на базата данни на Oracle "моят клиент използва набор от знаци AL32UTF8" - това не означава непременно, че вашият клиент наистина използва AL32UTF8! (вижте по-долу №4)

NLS_LANG може да бъде дефиниран от променливата на средата NLS_LANG или от системния регистър на Windows в HKLM\SOFTWARE\Wow6432Node\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG (за 32 бита), респ. HKLM\SOFTWARE\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG (за 64 бита). В зависимост от вашето приложение може да има и други начини да посочите NLS_LANG, но нека се придържаме към основите. Ако стойността NLS_LANG не е предоставена, Oracle я задава по подразбиране на AMERICAN_AMERICA.US7ASCII

Форматът на NLS_LANG е NLS_LANG=language_territory.charset . {charset } част от NLS_LANG е не показано във всяка системна таблица или изглед. Всички компоненти на дефиницията NLS_LANG са по избор, така че следните дефиниции са валидни:NLS_LANG=.WE8ISO8859P1 , NLS_LANG=_GERMANY , NLS_LANG=AMERICAN , NLS_LANG=ITALIAN_.WE8MSWIN1252 , NLS_LANG=_BELGIUM.US7ASCII .

Както е посочено по-горе, частта {charset} на NLS_LANG не е наличен в базата данни в нито една системна таблица/изглед или която и да е функция. Строго погледнато това е вярно, но можете да изпълните тази заявка:

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

Трябва да върне набор от знаци от текущия ви NLS_LANG настройка - въз основа на моя опит обаче стойността често е NULL или Unknown , т.е. не е надежден.

Намерете още много полезна информация тук:NLS_LANG FAQ

Имайте предвид, че някои технологии не използват NLS_LANG , настройките там нямат никакъв ефект, например:

  • ODP.NET управляваният драйвер не е NLS_LANG чувствителен. Той е чувствителен само към .NET локал. (вижте Доставчик на данни за Ръководство за разработчици на .NET)

  • OraOLEDB (от Oracle) винаги използва UTF-16 (вижте Специфични характеристики на доставчика на OraOLEDB)

  • Базиран на Java JDBC (например SQL Developer) има свои собствени методи за работа с набори от символи (вижте Ръководство за разработчици на база данни JDBC – Поддръжка за глобализация за повече подробности)

4:"Истинският" набор от знаци на вашия терминал, вашето приложение или кодирането на .sql файлове

Пример:UTF-8

Ако работите на Windows терминал (т.е. с SQL*plus), можете да разпитвате кодовата страница с команда chcp , в Unix/Linux еквивалентът е locale charmap или echo $LANG . Можете да получите списък с всички идентификатори на кодови страници на Windows от тук:Идентификатори на кодова страница. Забележка, за UTF-8 (chcp 65001 ) има някои проблеми, вижте тази дискусия.

Ако работите с .sql файлове и редактор като TOAD или SQL-Developer, трябва да проверите опциите за запис. Обикновено можете да изберете стойности като UTF-8 , ANSI , ISO-8859-1 и др.ANSI означава ANSI кодовата страница на Windows, обикновено CP1252 , можете да проверите във вашия регистър на адрес HKLM\SYSTEM\ControlSet001\Control\Nls\CodePage\ACP или тук:Справочник за API за поддръжка на национални езици (NLS)

[Microsoft премахна тази справка, приемете я под формата на уеб-архив Справка за API за поддръжка на национални езици (NLS)]

Как да задам всички тези стойности?

Най-важният момент е да съвпадне NLS_LANG и вашия "реален" набор от символи на вашия терминал, респ. приложение или кодирането на вашия .sql файлове

Някои често срещани двойки са:

  • CP850 -> WE8PC850

  • CP1252 или ANSI (в случай на "западен" компютър) -> WE8MSWIN1252

  • ISO-8859-1 -> WE8ISO8859P1

  • ISO-8859-15 -> WE8ISO8859P15

  • UTF-8 -> AL32UTF8

Или изпълнете тази заявка, за да получите още:

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

Някои технологии ви улесняват живота, напр. ODP.NET (неуправляван драйвер) или ODBC драйвер от Oracle автоматично наследява набора от знаци от NLS_LANG стойност, така че условието отгоре винаги е вярно.

Изисква ли се да се зададе стойност на NLS_LANG на клиента, равна на базата данни NLS_CHARACTERSET стойност?

Не, не е задължително! Например, ако имате база данни набор от символи NLS_CHARACTERSET=AL32UTF8 и наклиентата набор от символи NLS_LANG=.ZHS32GB18030 тогава ще работи без проблем (при условие, че вашият клиент наистина използва GB18030), въпреки че тези набори от знаци са напълно различни. GB18030 е набор от знаци, често използван за китайски, като UTF-8 той поддържа всички символи на Unicode.

Ако имате, например NLS_CHARACTERSET=AL32UTF8 и NLS_LANG=.WE8ISO8859P1 също ще работи (отново, при условие че вашият клиент наистина използва ISO-8859-P1). Въпреки това, базата данни може да съхранява знаци, които вашият клиент не може да покаже, вместо това клиентът ще покаже заместител (напр. ¿ ).

Както и да е, полезно е да имате съвпадащи стойности NLS_LANG и NLS_CHARACTERSET, ако е подходящо. Ако те са равни, можете да сте сигурни, че всеки знак, който може да бъде съхранен в базата данни, също може да бъде показан и всеки знак, който въвеждате във вашия терминал или записвате във вашия .sql файл, също може да бъде съхранен в базата данни и не се заменя с заместител.

Допълнение

Толкова много пъти можете да прочетете съвети като „Наборът от знаци NLS_LANG трябва да бъде същият като набора от знаци на вашата база данни“ (също тук на SO). Това просто не е вярно и е популярен мит!

Ето доказателството:

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.

И двата символа на клиента и базата данни са AL32UTF8 , обаче знаците не съвпадат. Причината е моят cmd.exe и по този начин също SQL*Plus използват Windows CP1252. Следователно трябва да задам съответно NLS_LANG:

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.

Помислете и за този пример:

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

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

Ще трябва да зададете две различни стойности за NLS_LANG за едно изявление - което не е възможно.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Как да намерите LEFT OUTER JOIN или ДЯСНО OUTER JOIN с ORACLE JOIN (+)

  2. Грешка при присъединяване към актуализиране

  3. LOWER() Функция в Oracle

  4. Oracle:как да INSERT, ако ред не съществува

  5. Как да използвам динамичен параметър в IN клауза на JPA именувана заявка?