Oracle NLS_LANG 常见问题

本文为Oracle NLS_LANG 常见问题的阅读笔记,只记录其中要点,并提供必要解读。

英文版在这里,中文版在这里

我读的英文版,择要点翻译为中文。

NLS表示 National Language Suppor。详见Oracle官方文档:Database Globalization Support Guide

NLS_LANG 参数基础知识

区域设置(locale)是一组与特定语言和国家/地区相对应的信息,用于满足语言和文化要求。传统上,与区域设置相关的数据支持日期、时间、数字和货币等的格式化和解析。

设置 NLS_LANG 环境参数是指定 Oracle 软件区域设置行为的最简单方法。

NLS_LANG 参数包含三个组成部分:语言、地域和字符集。

bash 复制代码
NLS_LANG = language_territory.charset

这三个部分都是可选的,没有设置的部分使用默认值。其中:

  • 语言:指定约定,例如用于 Oracle 消息、排序、日期名称和月份名称的语言。
  • 地域:指定约定,例如默认日期、货币和数字格式。
  • 字符集:指定客户端应用程序使用的字符集。

以下命令列出所有可用的locale:

bash 复制代码
locale -a

但是,非常重要的一点是,修改locale的设置不一定会影响数据库中语言,领土和客户端字符集的设置。JAVA客户端如SQLcl会受影响,但OCI客户端SQL Plus这不会受影响。所以最好的方式还是显示的设置NLS_LANG。

常见的 NLS_LANG 误区

将 NLS_LANG 设置为数据库的字符集可能正确,但通常不正确。

这点不太认同,因为数据库服务器建议字符集为AL32UTF8,即Unicode。客户端也是建议设为Unicode。例如,比较常见的设置为:

bash 复制代码
NLS_LANG=AMERICAN_AMERICA.AL32UTF8

如果 Oracle 未设置 NLS_LANG ,则其默认值为 AMERICAN_AMERICA.US7ASCII

这点说明了为何总是要显式的设置NLS_LANG。NLS_LANG的配置并非来自于OS的locale设置:

bash 复制代码
[oracle@xy23ai ~]$ locale
LANG=en_US.utf-8
LC_CTYPE="en_US.utf-8"
LC_NUMERIC="en_US.utf-8"
LC_TIME="en_US.utf-8"
LC_COLLATE="en_US.utf-8"
LC_MONETARY="en_US.utf-8"
LC_MESSAGES="en_US.utf-8"
LC_PAPER="en_US.utf-8"
LC_NAME="en_US.utf-8"
LC_ADDRESS="en_US.utf-8"
LC_TELEPHONE="en_US.utf-8"
LC_MEASUREMENT="en_US.utf-8"
LC_IDENTIFICATION="en_US.utf-8"
LC_ALL=

检查当前的 NLS_LANG 设置

OS层面:

bash 复制代码
echo $NLS_LANG

注意,以下输出中的字符集是数据库字符集,和客户端字符集无关:

sql 复制代码
SQL> SELECT USERENV ('language') FROM DUAL;

USERENV('LANGUAGE')
____________________________
AMERICAN_AMERICA.AL32UTF8

例如,在此设置下,欧元符号显示仍不正确:

bash 复制代码
SQL> select '€' from dual;

'???'
---------
???

NLS_LANG相关参数的优先级

可以在三个级别设置 NLS 参数,优先级从低到高为:数据库、实例和会话。

三个层面的设置可以从以下系统表中查询,优先级从高到低:

  • NLS_SESSION_PARAMETERS
  • NLS_INSTANCE_PARAMETERS
  • NLS_DATABASE_PARAMETERS

以下是从SQLcl中查询的,也就是JAVA客户端:

bash 复制代码
col parameter for a30
col value for a30
set pages 9999
select * from NLS_SESSION_PARAMETERS;

PARAMETER                  VALUE
__________________________ _______________________________
NLS_LANGUAGE               AMERICAN
NLS_TERRITORY              AMERICA
NLS_CURRENCY               $
NLS_ISO_CURRENCY           AMERICA
NLS_NUMERIC_CHARACTERS     .,
NLS_CALENDAR               GREGORIAN
NLS_DATE_FORMAT            DD-MON-RR
NLS_DATE_LANGUAGE          AMERICAN
NLS_SORT                   BINARY
NLS_TIME_FORMAT            HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT       DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT         HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT    DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY          $
NLS_COMP                   BINARY
NLS_LENGTH_SEMANTICS       BYTE
NLS_NCHAR_CONV_EXCP        FALSE

17 rows selected.

SQL> select * from NLS_INSTANCE_PARAMETERS;

PARAMETER                  VALUE
__________________________ ___________
NLS_LANGUAGE               AMERICAN
NLS_TERRITORY              AMERICA
NLS_SORT
NLS_DATE_LANGUAGE
NLS_DATE_FORMAT
NLS_CURRENCY
NLS_NUMERIC_CHARACTERS
NLS_ISO_CURRENCY
NLS_CALENDAR
NLS_TIME_FORMAT
NLS_TIMESTAMP_FORMAT
NLS_TIME_TZ_FORMAT
NLS_TIMESTAMP_TZ_FORMAT
NLS_DUAL_CURRENCY
NLS_COMP                   BINARY
NLS_LENGTH_SEMANTICS       BYTE
NLS_NCHAR_CONV_EXCP        FALSE

17 rows selected.

SQL> select * from NLS_DATABASE_PARAMETERS;

PARAMETER                  VALUE
__________________________ _______________________________
NLS_RDBMS_VERSION          23.0.0.0.0
NLS_NCHAR_CONV_EXCP        FALSE
NLS_LENGTH_SEMANTICS       BYTE
NLS_COMP                   BINARY
NLS_DUAL_CURRENCY          $
NLS_TIMESTAMP_TZ_FORMAT    DD-MON-RR HH.MI.SSXFF AM TZR
NLS_TIME_TZ_FORMAT         HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_FORMAT       DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_FORMAT            HH.MI.SSXFF AM
NLS_SORT                   BINARY
NLS_DATE_LANGUAGE          AMERICAN
NLS_DATE_FORMAT            DD-MON-RR
NLS_CALENDAR               GREGORIAN
NLS_NUMERIC_CHARACTERS     .,
NLS_NCHAR_CHARACTERSET     AL16UTF16
NLS_CHARACTERSET           AL32UTF8
NLS_ISO_CURRENCY           AMERICA
NLS_CURRENCY               $
NLS_TERRITORY              AMERICA
NLS_LANGUAGE               AMERICAN

20 rows selected.

以下是从SQL Plus中查询的结果,也就是OCI客户端:

sql 复制代码
SQL> select * from NLS_SESSION_PARAMETERS;

PARAMETER                      VALUE
------------------------------ ------------------------------
NLS_LANGUAGE                   AMERICAN
NLS_TERRITORY                  AMERICA
NLS_CURRENCY                   $
NLS_ISO_CURRENCY               AMERICA
NLS_NUMERIC_CHARACTERS         .,
NLS_CALENDAR                   GREGORIAN
NLS_DATE_FORMAT                DD-MON-RR
NLS_DATE_LANGUAGE              AMERICAN
NLS_SORT                       BINARY
NLS_TIME_FORMAT                HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT           DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT             HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT        DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY              $
NLS_COMP                       BINARY
NLS_LENGTH_SEMANTICS           BYTE
NLS_NCHAR_CONV_EXCP            FALSE

17 rows selected.

SQL> select * from NLS_INSTANCE_PARAMETERS;

PARAMETER                      VALUE
------------------------------ ------------------------------
NLS_LANGUAGE                   AMERICAN
NLS_TERRITORY                  AMERICA
NLS_SORT
NLS_DATE_LANGUAGE
NLS_DATE_FORMAT
NLS_CURRENCY
NLS_NUMERIC_CHARACTERS
NLS_ISO_CURRENCY
NLS_CALENDAR
NLS_TIME_FORMAT
NLS_TIMESTAMP_FORMAT
NLS_TIME_TZ_FORMAT
NLS_TIMESTAMP_TZ_FORMAT
NLS_DUAL_CURRENCY
NLS_COMP                       BINARY
NLS_LENGTH_SEMANTICS           BYTE
NLS_NCHAR_CONV_EXCP            FALSE

17 rows selected.

SQL> select * from NLS_DATABASE_PARAMETERS;

PARAMETER                      VALUE
------------------------------ ------------------------------
NLS_RDBMS_VERSION              23.0.0.0.0
NLS_NCHAR_CONV_EXCP            FALSE
NLS_LENGTH_SEMANTICS           BYTE
NLS_COMP                       BINARY
NLS_DUAL_CURRENCY              $
NLS_TIMESTAMP_TZ_FORMAT        DD-MON-RR HH.MI.SSXFF AM TZR
NLS_TIME_TZ_FORMAT             HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_FORMAT           DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_FORMAT                HH.MI.SSXFF AM
NLS_SORT                       BINARY
NLS_DATE_LANGUAGE              AMERICAN
NLS_DATE_FORMAT                DD-MON-RR
NLS_CALENDAR                   GREGORIAN
NLS_NUMERIC_CHARACTERS         .,
NLS_NCHAR_CHARACTERSET         AL16UTF16
NLS_CHARACTERSET               AL32UTF8
NLS_ISO_CURRENCY               AMERICA
NLS_CURRENCY                   $
NLS_TERRITORY                  AMERICA
NLS_LANGUAGE                   AMERICAN

20 rows selected.

其实输出是完全相同的,但是以下命令的输出并不相同:

sql 复制代码
-- 此时,NLS_LANG未设置,操作系统为Linux。也未用ALTER SESSION显式设置
-- SQLcl中,正确显示
SQL> select '€' from dual;

'€'
______
€

-- SQL*Plus中,错误显示
SQL> select '€' from dual;

'???'
---------
???

这至少说明了两点:

  1. NLS_CHARACTERSET是数据库字符集,和客户端字符集无关
  2. 客户端字符集的取值与客户端是Java还是OCI有关。

会话参数

数据库会话层面。以下只有LANGUAGE和TERRITORY,并没有字符集:

sql 复制代码
SQL> SELECT * FROM NLS_SESSION_PARAMETERS;

PARAMETER                  VALUE
__________________________ _______________________________
NLS_LANGUAGE               AMERICAN
NLS_TERRITORY              AMERICA
NLS_CURRENCY               $
NLS_ISO_CURRENCY           AMERICA
NLS_NUMERIC_CHARACTERS     .,
NLS_CALENDAR               GREGORIAN
NLS_DATE_FORMAT            DD-MON-RR
NLS_DATE_LANGUAGE          AMERICAN
NLS_SORT                   BINARY
NLS_TIME_FORMAT            HH.MI.SSXFF AM
NLS_TIMESTAMP_FORMAT       DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_TZ_FORMAT         HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_TZ_FORMAT    DD-MON-RR HH.MI.SSXFF AM TZR
NLS_DUAL_CURRENCY          $
NLS_COMP                   BINARY
NLS_LENGTH_SEMANTICS       BYTE
NLS_NCHAR_CONV_EXCP        FALSE

17 rows selected.

这些参数或者是继承自NLS_LANG的设置,或者是通过ALTER SESSION设置。

如果未设置 NLS_LANG,则默认值为<Language>_<Territory>.US7ASCII
<Language>_<Territory> 部分的值同NLS_INSTANCE_PARAMETERS 中的值。

语言和领土的设置会受OS locale的设置影响吗?

实例参数

sql 复制代码
SELECT * from NLS_INSTANCE_PARAMETERS;

这些是数据库启动时 init.ora 文件中的设置,或通过ALTER SYSTEM设置。

Oracle 强烈建议您将客户端的 NLS_LANG 至少设置为:

bash 复制代码
NLS_LANG=.<客户端字符集>

数据库参数

sql 复制代码
SELECT * from NLS_DATABASE_PARAMETERS;

如果在数据库创建期间未在 init.ora 中明确设置任何参数,则默认为 AMERICAN_AMERICA。

数据库创建后无法更改这些参数。

其他参考SQL:

bash 复制代码
SQL> SELECT name,value$ from sys.props$ where name like '%NLS%';

NAME                       VALUE$
__________________________ _______________________________
NLS_RDBMS_VERSION          23.0.0.0.0
NLS_NCHAR_CHARACTERSET     AL16UTF16
NLS_NCHAR_CONV_EXCP        FALSE
NLS_LENGTH_SEMANTICS       BYTE
NLS_COMP                   BINARY
NLS_DUAL_CURRENCY          $
NLS_TIMESTAMP_TZ_FORMAT    DD-MON-RR HH.MI.SSXFF AM TZR
NLS_TIME_TZ_FORMAT         HH.MI.SSXFF AM TZR
NLS_TIMESTAMP_FORMAT       DD-MON-RR HH.MI.SSXFF AM
NLS_TIME_FORMAT            HH.MI.SSXFF AM
NLS_SORT                   BINARY
NLS_DATE_LANGUAGE          AMERICAN
NLS_DATE_FORMAT            DD-MON-RR
NLS_CALENDAR               GREGORIAN
NLS_CHARACTERSET           AL32UTF8
NLS_NUMERIC_CHARACTERS     .,
NLS_ISO_CURRENCY           AMERICA
NLS_CURRENCY               $
NLS_TERRITORY              AMERICA
NLS_LANGUAGE               AMERICAN

20 rows selected.

SQL>  SELECT * from v$nls_parameters;

PARAMETER                  VALUE                              CON_ID
__________________________ _______________________________ _________
NLS_LANGUAGE               AMERICAN                                3
NLS_TERRITORY              AMERICA                                 3
NLS_CURRENCY               $                                       3
NLS_ISO_CURRENCY           AMERICA                                 3
NLS_NUMERIC_CHARACTERS     .,                                      3
NLS_CALENDAR               GREGORIAN                               3
NLS_DATE_FORMAT            DD-MON-RR                               3
NLS_DATE_LANGUAGE          AMERICAN                                3
NLS_CHARACTERSET           AL32UTF8                                3
NLS_SORT                   BINARY                                  3
NLS_TIME_FORMAT            HH.MI.SSXFF AM                          3
NLS_TIMESTAMP_FORMAT       DD-MON-RR HH.MI.SSXFF AM                3
NLS_TIME_TZ_FORMAT         HH.MI.SSXFF AM TZR                      3
NLS_TIMESTAMP_TZ_FORMAT    DD-MON-RR HH.MI.SSXFF AM TZR            3
NLS_DUAL_CURRENCY          $                                       3
NLS_NCHAR_CHARACTERSET     AL16UTF16                               3
NLS_COMP                   BINARY                                  3
NLS_LENGTH_SEMANTICS       BYTE                                    3
NLS_NCHAR_CONV_EXCP        FALSE                                   3

19 rows selected.

SQL> SELECT name,value from v$parameter where name like '%nls%';

NAME                       VALUE
__________________________ _______________________________
nls_language               AMERICAN
nls_territory              AMERICA
nls_sort                   BINARY
nls_date_language          AMERICAN
nls_date_format            DD-MON-RR
nls_currency               $
nls_numeric_characters     .,
nls_iso_currency           AMERICA
nls_calendar               GREGORIAN
nls_time_format            HH.MI.SSXFF AM
nls_timestamp_format       DD-MON-RR HH.MI.SSXFF AM
nls_time_tz_format         HH.MI.SSXFF AM TZR
nls_timestamp_tz_format    DD-MON-RR HH.MI.SSXFF AM TZR
nls_dual_currency          $
nls_comp                   BINARY
nls_length_semantics       BYTE
nls_nchar_conv_excp        FALSE

17 rows selected.

SQL> SELECT userenv ('language') from dual;

USERENV('LANGUAGE')
____________________________
AMERICAN_AMERICA.AL32UTF8

SQL> SELECT userenv ('lang') from dual;

USERENV('LANG')
__________________
US

SQL> show parameter nls%
NAME                    TYPE   VALUE
----------------------- ------ ----------------------------
nls_calendar            string GREGORIAN
nls_comp                string BINARY
nls_currency            string $
nls_date_format         string DD-MON-RR
nls_date_language       string AMERICAN
nls_dual_currency       string $
nls_iso_currency        string AMERICA
nls_language            string AMERICAN
nls_length_semantics    string BYTE
nls_nchar_conv_excp     string FALSE
nls_numeric_characters  string .,
nls_sort                string BINARY
nls_territory           string AMERICA
nls_time_format         string HH.MI.SSXFF AM
nls_time_tz_format      string HH.MI.SSXFF AM TZR
nls_timestamp_format    string DD-MON-RR HH.MI.SSXFF AM
nls_timestamp_tz_format string DD-MON-RR HH.MI.SSXFF AM TZR

错误 NLS_LANG 设置示例

非常重要的一点(如前所述):

当客户端 NLS_LANG 字符集设置为与数据库字符集相同 的值时,Oracle 会假定发送或接收的数据采用相同的(正确的)编码 ,因此出于性能原因,可能不会进行任何转换或验证。数据只是按照客户端传递的格式逐位存储。

如何为 UNIX 正确设置 NLS_LANG

使用locale命令查看:

bash 复制代码
$ locale LC_CTYPE | head
upper;lower;alpha;digit;xdigit;space;print;graph;blank;cntrl;punct;alnum;combining;combining_level3
toupper;tolower;totitle
16
6
UTF-8
72
86
1
0
1

因此,NLS_LANG的第三部分(字符集部分)应设为UTF-8:

bash 复制代码
NLS_LANG=AMERICAN_AMERICA.AL32UTF8

如何查看数据库中实际存储的内容?

要查找数据库中存储的字符的实际数值,请使用 dump 命令:

该函数调用的语法如下:

sql 复制代码
DUMP( <value> [, <format> [, <offset> [, <length> ] ] ] )

DUMP 返回一个 VARCHAR2 值,其中包含 expr 的数据类型代码、长度(以字节为单位)和内部表示形式。返回的结果始终采用数据库字符集。详见帮助

默认情况下,返回值不包含字符集信息。要检索 expr 的字符集名称,请将 1000 添加到上述任意格式值中。例如,return_fmt 为 1008 时,将以八进制形式返回结果,并提供 expr 的字符集名称。

字符集转换在哪里完成?

出于性能考虑,通常会在客户端进行字符集转换。若数据库使用的字符集不被客户端识别,则转换将在服务器端完成。

如何检查由UNIX操作系统管理的代码点?

使用"od"命令:

bash 复制代码
$ echo -n "€"|od -t x1
0000000 e2 82 ac
0000003

$ printf € | hexdump
0000000 82e2 00ac
0000003

字符€的UTF8编码为0xE2 0x82 0xAC,详见这里

可以用printf打印:

bash 复制代码
## UTF-8 编码
$ printf "\xE2\x82\xAC"
€

$ echo -e "\xE2\x82\xAC"
€

## UTF-32 编码
$ echo -e '\U000020AC'
€

## UTF-16 编码
$ echo -e '\U20AC'
€

$ echo -e $'\u4F60\u597D'
你好

注意,以上的-U不能换成-u,因为-u只支持BMP(Basic Multilingual Plane)字符,而-U支持BMP之外的字符。

您可以使用 Locale Builder 或下方链接来验证您的本地代码页和 NLS_LANG 设置是否正确对应:

那么 SQL*Loader、Import、Export、实用程序等命令行工具怎么样?

Windows下显示当前code page:

bash 复制代码
C:\>chcp
Active code page: 437

Linux下的命令:

bash 复制代码
$ locale charmap
UTF-8

在 Oracle9i 及以上,export实用程序始终以数据库的字符集导出用户数据(包括 Unicode 数据)。import实用程序会自动将数据转换为目标数据库的字符集。

也可以显式指定。可以临时将 NLS_LANG 更改为您正在加载的文件的字符集。或使用 .ctl 文件中的 characterset 关键字指定数据文件中数据的字符集。在这种情况下,无论客户端的 NLS_LANG 字符集设置如何,SQL*Loader 都会将数据文件中的数据解释为该字符集。

数据库链接怎么办?

服务器(或客户端)上的 NLS_LANG 对通过数据库链接进行的字符集转换没有影响。Oracle 会将源数据库的字符集转换为目标数据库的字符集(或反向转换)。

什么是字符集或代码页?

字符集只是对符号数值的一种约定。计算机无法识别"A"或"B",它只知道该符号的(二进制)数值,该数值由其操作系统 (OS) 或终端硬件(固件)使用的字符集定义。计算机只能处理数字,因此需要字符集。例如,"ASCII"(旧的 7 位字符集)、"ROMAN8"(UNIX 上的 8 位字符集)或"UTF8"(多字节字符集)。

代码页是 Windows/DOS 编码方案的名称,对于 Oracle NLS,您可以将其视为字符集。您还需要区分字体 (FONT) 和字符集/代码页。操作系统使用字体将数值转换为屏幕上的图形"打印"。Windows 上的 Wingdings 字体就是最好的字体示例,其中"A"在屏幕上不会显示为"A",但对于操作系统而言,数值代表"A"。所以你看不到"A",但在 Windows 系统中,它就是"A",并且会被保存(或使用)为"A"。

为了更好地理解上述解释,只需打开 MS Word,选择 Wingdings 字体,输入你的名字(你会看到符号),然后将其保存为 html 文件。如果你用记事本打开该 html 文件,你会看到在

也有可能你看不到某种字体中你正在使用的代码页中定义的所有符号,这是因为字体的创建者没有为该字体中的所有符号提供图形表示。这就是为什么你有时在更改字体时会在屏幕上看到黑色方块的原因。在 Windows 上,您可以使用"字符映射"工具查看字体中定义的所有符号。

为什么有不同的字符集?

主要有两个原因:

  • 过去,供应商为其硬件和软件定义了不同的"字符集",主要是因为没有官方标准。
  • 新的字符集被定义来支持新的语言。8 位字符集支持的符号数量有限,因此不同的书面语言会使用不同的字符集。

7 位、8 位和 Unicode 字符集有什么区别?

7 位字符集只能识别 128 个符号 (2^7)

8 位字符集可以识别 256 个符号 (2^8)

Unicode (UTF-8) 是一种多字节字符集。Unicode 能够定义超过一百万个字符。有关 Unicode 的更多信息,请参阅白皮书《Oracle Unicode 数据库支持》

如何选择正确的数据库字符集?

选择字符集的一个基本考虑因素是确保它能够处理任何需要立即支持以及在不确定的未来支持的语言。另一个容易被忽视的考虑因素是考虑您可能想要使用或与数据库交互的应用程序和技术。

简而言之,UNICODE是最佳选择。新系统请使用UNICODE,老系统请迁移至UNICODE。

参考

相关推荐
技术卷3 小时前
详解力扣高频SQL50题之1084. 销售分析 III【简单】
sql·leetcode·oracle
Alla T3 小时前
【通识】数据库
数据库·oracle
MickeyCV5 小时前
MySQL数据库本地迁移到云端完整教程
服务器·数据库·mysql·oracle
IT邦德5 小时前
OGG同步Oracle到Kafka不停库,全量加增量
数据库·oracle·kafka
技术卷5 小时前
详解力扣高频SQL50题之550. 游戏玩法分析 IV【中等】
sql·mysql·leetcode·oracle
技术卷10 小时前
详解力扣高频 SQL 50 题之584. 寻找用户推荐人【入门】
sql·leetcode·oracle
ALLSectorSorft17 小时前
教务管理系统学排课教务系统模块设计
数据库·sql·oracle
极简之美19 小时前
spring boot h2数据库无法链接问题
数据库·spring boot·oracle
zone_z1 天前
告别静态文档!Oracle交互式技术架构图让数据库学习“活“起来
数据库·学习·oracle
种树达人1 天前
数据库常用DDL语言
java·数据库·oracle