Sunday, August 2, 2009

NLS_LANG คือตัวแปร Environment บนฝั่ง Client ไม่ใช่บน Database Server

อย่างที่ผมได้ให้หัวเรื่องไว้เกี่ยวกับ NLS_LANG คือตัวแปรตัวนี้เป็นตัวแปรบนฝั่ง Client หรืออย่างน้อยก็เป็นตัวแปรของ Client Application (ซึ่งจริง ๆ แล้วอาจจะ Install ไว้บน Database Server ก็ได้) ที่จะติดต่อกับฐานข้อมูล เพื่อให้เห็นภาพลองดูวิธีการตั้งค่าตัวแปรตัวนี้บน Windows ดูกันหน่อยนะครับ

C:\>set NLS_LANG="ARABIC_UNITED ARAB EMIRATES.AR8MSAWIN"
C:\>echo %NLS_LANG%
"ARABIC_UNITED ARAB EMIRATES.AR8MSAWIN"

เรา set NLS_LANG บน client เพื่อบอกว่าขณะนี้เราจะ connect ด้วย environment แบบไหน โดยตอน select ถ้า Character Set ในตัวอย่างคือ (AR8MSAWIN) มีขนาดเล็กกว่า Database Character Set เราจะเห็นเป็น question mark หมายถึงด้วย environment ของเรา ไม่รู้จักตัวอักษรที่ database ส่งมาให้ เช่นถ้า Database Character Set เป็น UTF8 แต่เรา set NLS_LANG ที่เครื่องเป็น US7ASCII ตัวอักษรที่ส่งมาจาก Database ทีไม่อยู่ใน range ที่ US7ASCII รู้จักจะกลายเป็น ???

ในทางกลับกันตอน insert ถ้า database มี Character Set ที่เล็กกว่าของเครื่อง Client ที่ insert เช่น database เป็น US7ASCII แต่เครื่อง client set NLS_LANG=american_america.TH8TISASCII ข้อมูลภาษาไทยที่เรา insert เข้าไปจะกลายเป็น ??? แต่ถ้า database เป็น TH8TISASCII เหมือนกับเครื่อง Client (หรือเป็น Character Set ที่เป็น superset ของ TH8TISASCII จะสามารถ insert ได้ ดังตัวอย่างข้างล่าง เราConnect เข้ากับ orcl3 ซึ่งมี Character Set เป็น US7ASCII (พารามิเตอร์ชื่อ NLS_CHARACTER_SET) โดยใช้ sqlplus บน DOS Command Window

C:\>set nls_lang=american_america.th8tisascii
C:\>sqlplus oe@orcl3
SQL*Plus: Release 10.2.0.1.0 - Production on Thu May 29 15:04:40 2008
Copyright (c) 1982, 2005, Oracle. All rights reserved.
Enter password:
Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options

SQL> select * from nls_database_parameters where parameter = 'NLS_CHARACTERSET';
PARAMETER VALUE
------------------------------ ----------------------------------------
NLS_CHARACTERSET US7ASCII

1 rows selected.

SQL> desc test_char;
Name Null? Type
----------------------------------------- -------- ----------------------------
CNAME NCHAR(10)
VNAME NVARCHAR2(10)

SQL> insert into test_char(cname) values ('ธนากร');
1 row created.

SQL> select * from test_char;
CNAME VNAME
---------- ----------
?????

จากตัวอย่างเรา Insert เข้าไปในตารางในฐานข้อมูลที่มี Character Set ที่เป็น US7ASCII ในขณะที่เครื่อง Client ที่ใช้ในการ Insert มี Character Set (ที่ีตั้งค่าโดย NLS_LANG) ที่มีขนาดใหญ่กว่า US7ASCII (TH8TISASCII มีขนาด 8 บิท และมีจำนวนตัวอักษรมากกว่า US7ASCII ซึ่งมีขนาด 7 บิท) ค่าที่ได้จากการ Insert ตัวอักษรที่ไม่อยู่ในชุดตัวอักษร US7ASCII เลยจึงกลายเป็น '?' ทุกตัว

คราวนี้เราลองมาทดสอบกับฐานข้อมูลที่มี Character Set ที่เป็น TH8TISASCII บ้าง (NLS_LANG ยังคงเป็น TH8TISASCII)

SQL> select * from nls_database_parameters where parameter = 'NLS_CHARACTERSET';
PARAMETER VALUE
------------------------------ ----------------------------------------
NLS_CHARACTERSET TH8TISASCII

1 rows selected.
SQL> create table test_char (cname nchar(10), vname nvarchar2(10));
Table created.

SQL> insert into test_char (cname) values ('ธนากร');
1 row created.

SQL> select * from test_char;
CNAME VNAME
---------- ----------
ธนากร
1 row selected.

ดังนั้นเพื่อให้แน่ใจว่าจะไม่เกิดการสูญเสียข้อมูลมีข้อควรระลึกถึงเกี่ยวกับ NLS_LANG ดังนี้
1. ตั้งค่า NLS_LANG ให้เป็นตัวเดียวกับ Database Character Set เสมอ
2. ถ้าทำอย่างกรณีข้อ 1 ไม่ได้ และต้องทำ DML กับฐานข้อมูล(เช่น Insert, Update, Delete) ให้ตั้งค่า NLS_LANG ให้เป็น Subset ของ Database Character Set
3. ถ้าทำอย่างกรณีข้อ 1 ไม่ได้ และต้องทำการ Select ข้อมูลอย่างเดียว ให้ตั้งค่า NLS_LANG ให้เป็น Superset ของ Database Character Set

หมายเหตุ
1. Database Character Set สามารถดูได้จากตัวแปร 'NLS_CHARACTERSET'
2. คำสั่ง Set NLS_LANG จะมีผลต่อ Session นั้น ๆ เท่านั้น ถ้าเราปิด Command Window แล้วเปิดใหม่จะต้อง Set ค่าตัวนี้ใหม่ ถ้าต้องการให้มีผลถาวรอาจจะเข้าไปตั้งค่า Environment Variables (คลิ๊กขวาที่ My Computer เลือก Properties => คลิ๊กเลือก Advanced Tab แล้วคลิ๊ก Environment Variables)
3. โดยปกติหากเราไม่ได้ตั้งค่า NLS_LANG ค่าดีฟอลต์จะเก็บอยู่ที่ Registry ของเครื่องใน HKEY_LOCAL_MACHINE => SOFTWARE => ORACLE => KEY_OraDb10g_home1 ให้ดูที่ Registry ทางขวามือชื่อ NLS_LANG ซึ่งส่วนที่อยู่หลังจุดจะเป็น Character Set เช่น AMERICAN_AMERICA.TH8TISASCII ก็หมายความว่าเครื่อง Client นี้ (ถ้าไม่ได้ตั้งค่า NLS_LANG ดังวิธีการอื่น ๆ ที่กล่าวมา) มี Character Set เป็น TH8TISASCII

บทความที่เกี่ยวเนื่องกัน
1. การใช้ NCHAR และการกำหนด Character Sets

2 comments:

oattie said...

ขอบคุณครับ กระจ่างขึ้นเยอะเลยครับ

Anonymous said...

ข อ บ คุ ณ ค รั บ

Post a Comment