Các vấn đề với bộ ký tự khá phổ biến, hãy để tôi thử đưa ra một số lưu ý chung.
Về nguyên tắc, bạn phải xem xét bốn cài đặt bộ ký tự khác nhau.
1 và 2:NLS_CHARACTERSET
và NLS_NCHAR_CHARACTERSET
Ví dụ:AL32UTF8
Chúng được định nghĩa chỉ trên cơ sở dữ liệu của mình, bạn có thể thẩm vấn họ bằng
SELECT *
FROM V$NLS_PARAMETERS
WHERE PARAMETER IN ('NLS_CHARACTERSET', 'NLS_NCHAR_CHARACTERSET');
Các cài đặt này xác định các ký tự (ở định dạng nào) có thể được lưu trữ trong cơ sở dữ liệu của bạn - không hơn, không kém. Nó đòi hỏi một số nỗ lực (xem Di chuyển bộ ký tự và / hoặc Hỗ trợ di chuyển cơ sở dữ liệu Oracle cho Unicode) nếu bạn phải thay đổi nó trên cơ sở dữ liệu hiện có.
3:NLS_LANG
Ví dụ:AMERICAN_AMERICA.AL32UTF8
Giá trị này được xác định chỉ trên khách hàng của bạn. NLS_LANG không liên quan gì đến khả năng lưu trữ các ký tự trong cơ sở dữ liệu. Nó được sử dụng để cho Oracle biết bạn đang sử dụng bộ ký tự nào ở phía máy khách. Khi bạn đặt giá trị NLS_LANG (ví dụ thành AL32UTF8) thì bạn chỉ cần nói với cơ sở dữ liệu Oracle "máy khách của tôi sử dụng bộ ký tự AL32UTF8" - điều đó không nhất thiết có nghĩa là máy khách của bạn thực sự đang sử dụng AL32UTF8! (xem bên dưới # 4)
NLS_LANG có thể được định nghĩa bởi biến môi trường NLS_LANG
hoặc bằng Windows Registry tại HKLM\SOFTWARE\Wow6432Node\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG
(cho 32 bit), tương ứng. HKLM\SOFTWARE\ORACLE\KEY_%ORACLE_HOME_NAME%\NLS_LANG
(cho 64 bit). Tùy thuộc vào ứng dụng của bạn, có thể có những cách khác để chỉ định NLS_LANG, nhưng chúng ta hãy bám vào những điều cơ bản. Nếu giá trị NLS_LANG không được cung cấp thì Oracle mặc định nó thành AMERICAN_AMERICA.US7ASCII
Định dạng của NLS_LANG là NLS_LANG=language_territory.charset
. { Bộ ký tự } một phần của NLS_LANG không hiển thị trong bất kỳ bảng hoặc chế độ xem hệ thống nào. Tất cả các thành phần của định nghĩa NLS_LANG là tùy chọn, vì vậy các định nghĩa sau đều hợp lệ:NLS_LANG=.WE8ISO8859P1
, NLS_LANG=_GERMANY
, NLS_LANG=AMERICAN
, NLS_LANG=ITALIAN_.WE8MSWIN1252
, NLS_LANG=_BELGIUM.US7ASCII
.
Như đã nêu ở trên phần {charset} của NLS_LANG
không có sẵn trong cơ sở dữ liệu ở bất kỳ bảng / chế độ xem hệ thống hoặc bất kỳ chức năng nào. Nói một cách chính xác thì điều này đúng, tuy nhiên bạn có thể chạy truy vấn này:
SELECT DISTINCT CLIENT_CHARSET
FROM V$SESSION_CONNECT_INFO
WHERE (SID, SERIAL#) = (SELECT SID, SERIAL# FROM v$SESSION WHERE AUDSID = USERENV('SESSIONID'));
Nó sẽ trả về bộ ký tự từ NLS_LANG
hiện tại của bạn cài đặt - tuy nhiên dựa trên kinh nghiệm của tôi, giá trị thường là NULL hoặc Unknown
, tức là không đáng tin cậy.
Tìm thêm thông tin rất hữu ích tại đây:NLS_LANG FAQ
Lưu ý, một số công nghệ không sử dụng NLS_LANG
, các cài đặt ở đó không có bất kỳ ảnh hưởng nào, ví dụ:
-
ODP.NET Managed Driver không phải là
NLS_LANG
nhạy cảm. Nó chỉ nhạy cảm với ngôn ngữ .NET. (xem Nhà cung cấp dữ liệu để biết Hướng dẫn của nhà phát triển .NET) -
OraOLEDB (của Oracle) luôn sử dụng UTF-16 (xem Các tính năng cụ thể của nhà cung cấp OraOLEDB)
-
JDBC dựa trên Java (ví dụ:SQL Developer) có các phương pháp riêng để xử lý các tập ký tự (xem Hướng dẫn dành cho nhà phát triển JDBC của cơ sở dữ liệu - Hỗ trợ toàn cầu hóa để biết thêm chi tiết)
4:Bộ ký tự "thực" của thiết bị đầu cuối, ứng dụng của bạn hoặc mã hóa của .sql
tệp
Ví dụ:UTF-8
Nếu bạn làm việc trên thiết bị đầu cuối Windows (tức là với SQL * plus), bạn có thể thẩm vấn trang mã bằng lệnh chcp
, trên Unix / Linux thì tương đương là locale charmap
hoặc echo $LANG
. Bạn có thể nhận danh sách tất cả các mã định danh trang mã Windows từ đây:Số nhận dạng trang mã. Lưu ý, đối với UTF-8 (chcp 65001
) có một số vấn đề, hãy xem cuộc thảo luận này.
Nếu bạn làm việc với .sql
và một trình soạn thảo như TOAD hoặc SQL-Developer, bạn phải kiểm tra các tùy chọn lưu. Thông thường, bạn có thể chọn các giá trị như UTF-8
, ANSI
, ISO-8859-1
, v.v. ANSI
nghĩa là bộ mã ANSI của Windows, thường là CP1252
, bạn có thể kiểm tra Sổ đăng ký của mình tại HKLM\SYSTEM\ControlSet001\Control\Nls\CodePage\ACP
hoặc tại đây:Tham chiếu API Hỗ trợ Ngôn ngữ Quốc gia (NLS)
[Microsoft đã xóa tham chiếu này, đưa tham chiếu này ở dạng lưu trữ trên web về API Hỗ trợ ngôn ngữ quốc gia (NLS) Tham chiếu]
Làm cách nào để đặt tất cả các giá trị này?
Điểm quan trọng nhất là phải khớp với NLS_LANG
và bộ ký tự "thực" của thiết bị đầu cuối của bạn, tương ứng. ứng dụng hoặc mã hóa .sql
của bạn tệp
Một số cặp phổ biến là:
-
CP850 ->
WE8PC850
-
CP1252 hoặc ANSI (trong trường hợp PC "Phương Tây") ->
WE8MSWIN1252
-
ISO-8859-1 ->
WE8ISO8859P1
-
ISO-8859-15 ->
WE8ISO8859P15
-
UTF-8 ->
AL32UTF8
Hoặc chạy truy vấn này để nhận thêm một số thông tin khác:
SELECT VALUE AS ORACLE_CHARSET, UTL_I18N.MAP_CHARSET(VALUE) AS IANA_NAME
FROM V$NLS_VALID_VALUES
WHERE PARAMETER = 'CHARACTERSET';
Một số công nghệ giúp cuộc sống của bạn dễ dàng hơn, ví dụ:ODP.NET (trình điều khiển không thay đổi) hoặc trình điều khiển ODBC từ Oracle tự động kế thừa bộ ký tự từ NLS_LANG
giá trị, vì vậy điều kiện từ trên luôn đúng.
Có bắt buộc phải đặt giá trị NLS_LANG của ứng dụng khách bằng với cơ sở dữ liệu NLS_CHARACTERSET
không giá trị?
Không, không nhất thiết! Ví dụ:nếu bạn có cơ sở dữ liệu bộ ký tự NLS_CHARACTERSET=AL32UTF8
và khách hàng bộ ký tự NLS_LANG=.ZHS32GB18030
thì nó sẽ hoạt động mà không có vấn đề gì (miễn là khách hàng của bạn thực sự sử dụng GB18030), mặc dù các bộ ký tự này hoàn toàn khác nhau. GB18030 là một bộ ký tự thường được sử dụng cho tiếng Trung Quốc, như UTF-8
nó hỗ trợ tất cả các ký tự Unicode.
Nếu bạn có, ví dụ:NLS_CHARACTERSET=AL32UTF8
và NLS_LANG=.WE8ISO8859P1
nó cũng sẽ hoạt động (một lần nữa, miễn là khách hàng của bạn thực sự sử dụng ISO-8859-P1). Tuy nhiên, cơ sở dữ liệu có thể lưu trữ các ký tự mà máy khách của bạn không thể hiển thị, thay vào đó máy khách sẽ hiển thị một trình giữ chỗ (ví dụ:¿
).
Dù sao, sẽ có lợi khi có các giá trị NLS_LANG và NLS_CHARACTERSET phù hợp, nếu phù hợp. Nếu chúng bằng nhau, bạn có thể chắc chắn rằng bất kỳ ký tự nào có thể được lưu trữ trong cơ sở dữ liệu cũng có thể được hiển thị và bất kỳ ký tự nào bạn nhập vào thiết bị đầu cuối hoặc viết trong tệp .sql của bạn cũng có thể được lưu trữ trong cơ sở dữ liệu và không bị thay thế bởi trình giữ chỗ.
Bổ sung
Vì vậy, nhiều lần bạn có thể đọc những lời khuyên như "Bộ ký tự NLS_LANG phải giống bộ ký tự trong cơ sở dữ liệu của bạn" (cũng có ở đây trên SO). Điều này chỉ đơn giản là không đúng và là một huyền thoại phổ biến!
Đây là bằng chứng:
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.
Cả bộ ký tự máy khách và cơ sở dữ liệu đều là AL32UTF8
, tuy nhiên các ký tự không khớp. Lý do là, cmd.exe
của tôi và do đó SQL * Plus cũng sử dụng Windows CP1252. Do đó tôi phải đặt NLS_LANG cho phù hợp:
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.
Cũng xem xét ví dụ này:
CREATE TABLE ARABIC_LANGUAGE (
LANG_CHAR VARCHAR2(20),
LANG_NCHAR NVARCHAR2(20));
INSERT INTO ARABIC_LANGUAGE VALUES ('العربية', 'العربية');
Bạn sẽ cần đặt hai giá trị khác nhau cho NLS_LANG
cho một tuyên bố duy nhất - điều này không thể thực hiện được.