浅析NLS_COMP、NLS_SORT(三)

oracle数据库提供了两种排序方式:基于二进制(binary)和基于语言(linguistic)。

基于二进制的排序方法,以字符串的数据库字符集编码为基础,编码靠前的字符排序靠前,反之编码靠后的字符排序靠后。采用二进制排序方式,是oracle数据库的默认选择,具有较好的数据操作性能。但是在某些情况下,可能不能满足我们的排序需求,例如,对于中文排序,依据字符编码往往没有语言意义的,而如果可以采用基于偏旁或者笔画的方式来排序中文,则显得比较人性化一点。

为此,oracle数据库提供了另外一种排序方式:基于语言的排序。同时,基于语言的排序,又可细分为基于单一语言和基于多种语言两种方式。

无论是基于二进制的排序还是基于语言的排序,oracle均通过排序键来进行排序和比较的。排序键是一种二进制格式的数据,在基于二进制的排序方法下,排序键取值与字符集编码,在基于语言的排序方法下,排序键基于一定的算法来获取,例如,来单一语言的排序中,排序键由major value和minor value构成,在多语言的排序中则基于ISO 14651来进行计算。不管采用何种计算方式来获取排序键,排序键的值与具有相同参数的NLSSORT函数的返回值是完全相等的。

在当前会话中,具体采用那种排序方式,是由两个参数NLS_COMP、NLS_SORT来控制的。当前会话的NLS_COMP、NLS_SORT参数默认是从nls_instance_parameters继承而来,当然我们也可以通过环境变量或者alter session来手动更改。

NLS_COMP的取值有三个:BINARY (二进制) LINGUISTIC(基于语言) ANSI(为向后兼容而保留)。

NLS_SORT的取值比较多,可以通过查询v$nls_valid_values的parameter=‘SORT' 来获取。

下图显示了,在NLS_COMP的不同取值下,各种sql操作和函数的排序行为。

例如,当我们进行“=”操作时,如果nls_comp取值为binary,则采用binary排序方式,如果nls_comp取值为linguistic,在采用nls_sort参数指定的排序方式,如果nls_comp取值为ansi,在采用nls_sort参数指定的排序方式。


首先,我们来看一下二进制的排序方式(不管采用何种方式,如果在NLS_SORT参数中添加_CI后缀则表示不区分大小写,添加_AI后缀则表示不区分重读音和大小写)。

SQL> select * from tab_nls;

V			       BCODE
------------------------------ --------------------
a			       61
b			       62
A			       41
B			       42

SQL> select v,bcode,nlssort(v,'NLS_SORT=BINARY') c1 from tab_nls order by c1;

V			       BCODE		    C1
------------------------------ -------------------- ------------------------------
A			       41		    4100
B			       42		    4200
a			       61		    6100
b			       62		    6200

SQL> select v,bcode,nlssort(v,'NLS_SORT=BINARY_CI') c1 from tab_nls order by c1;

V			       BCODE		    C1
------------------------------ -------------------- ------------------------------
A			       41		    6100
a			       61		    6100
B			       42		    6200
b			       62		    6200

这里我们可以看出,二进制排序方式是基于数据库字符集的编码来进行排序的的(BCODE列是v列在数据库中字符集编码的16进制表示)


下面我们来看一下基于单一语言的排序。首先需要说明的是,基于单一语言的排序,只针对字符集是unicode的数据库有效,当字符集为非unicode时,如果指定了单一语言的排序方式,则默认使用基于二进制的排序方法。对于单一语言排序,往往存在与之对应的多语言排序(以_M后缀结束),但这不是必须的。

在单一语言排序中,同样存在_CI和_AI的后缀使用:

_CI:不区分大小写,但是区分重读音,可以排序的字符包括基本字符以及标点符号

_AI:不区分大小写,不区分重读音,可以排序的字符包括基本字符和标点符号

无论_AI和_CI,ORACLE均将标点符号如(“-”,“*”),作为未定义的字符处理,付给器较低的权重,已进行比较。这一点与多语言的排序时有区别的,在多语言排序中,这些符号可能会在排序操作过程中北忽略。

在unicode字符集下:

SQL> select parameter,value from nls_database_parameters;

PARAMETER		       VALUE
------------------------------ ------------------------------
NLS_LANGUAGE		       AMERICAN
NLS_TERRITORY		       AMERICA
NLS_CURRENCY		       $
NLS_ISO_CURRENCY	       AMERICA
NLS_NUMERIC_CHARACTERS	       .,
NLS_CHARACTERSET	       AL32UTF8
NLS_CALENDAR		       GREGORIAN
NLS_DATE_FORMAT 	       DD-MON-RR
NLS_DATE_LANGUAGE	       AMERICAN
NLS_SORT		       BINARY
NLS_TIME_FORMAT 	       HH.MI.SSXFF AM

PARAMETER		       VALUE
------------------------------ ------------------------------
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
NLS_NCHAR_CHARACTERSET	       AL16UTF16
NLS_RDBMS_VERSION	       11.2.0.3.0

已选择20行。
SQL> select v,bcode,nlssort(v,'NLS_SORT=FRENCH_CI') c1 from tab_nls order by c1;

V	   BCODE		C1
---------- -------------------- ------------------------------
a	   61			14000200
A	   41			14000200
a-a	   612D61		14140002002D0200
aa	   6161 		141400020200
aa-	   61612D		1414000202002D00
a-b	   612D62		14190002002D0200
ab	   6162 		141900020200
ab-	   61622D		1419000202002D00
B	   42			19000200
b	   62			19000200

已选择10行。

SQL> select v,bcode,nlssort(v,'NLS_SORT=FRENCH') c1 from tab_nls order by c1;

V	   BCODE		C1
---------- -------------------- ------------------------------
A	   41			14000100
a	   61			14000200
a-a	   612D61		14140002002D0200
aa	   6161 		141400020200
aa-	   61612D		1414000202002D00
a-b	   612D62		14190002002D0200
ab	   6162 		141900020200
ab-	   61622D		1419000202002D00
B	   42			19000100
b	   62			19000200

已选择10行。


在非UNICODE字符集下:

SQL> select parameter,value from nls_database_parameters;

PARAMETER		       VALUE
------------------------------ ------------------------------
NLS_LANGUAGE		       AMERICAN
NLS_TERRITORY		       AMERICA
NLS_CURRENCY		       $
NLS_ISO_CURRENCY	       AMERICA
NLS_NUMERIC_CHARACTERS	       .,
NLS_CHARACTERSET	       ZHS16GBK
NLS_CALENDAR		       GREGORIAN
NLS_DATE_FORMAT 	       DD-MON-RR
NLS_DATE_LANGUAGE	       AMERICAN
NLS_SORT		       BINARY
NLS_TIME_FORMAT 	       HH.MI.SSXFF AM

PARAMETER		       VALUE
------------------------------ ------------------------------
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
NLS_NCHAR_CHARACTERSET	       AL16UTF16
NLS_RDBMS_VERSION	       11.2.0.3.0

已选择20行。

SQL> select v,bcode,nlssort(v,'NLS_SORT=FRENCH') c1 from tab_nls order by c1;

V			       BCODE		    C1
------------------------------ -------------------- ------------------------------
A			       41		    4100
B			       42		    4200
a			       61		    6100
a-a			       612D61		    612D6100
a-b			       612D62		    612D6200
aa			       6161		    616100
aa-			       61612D		    61612D00
ab			       6162		    616200
ab-			       61622D		    61622D00
b			       62		    6200

已选择10行。

SQL> select v,bcode,nlssort(v,'NLS_SORT=FRENCH_CI') c1 from tab_nls order by c1;

V			       BCODE		    C1
------------------------------ -------------------- ------------------------------
A			       41		    4100
B			       42		    4200
a			       61		    6100
a-a			       612D61		    612D6100
a-b			       612D62		    612D6200
aa			       6161		    616100
aa-			       61612D		    61612D00
ab			       6162		    616200
ab-			       61622D		    61622D00
b			       62		    6200

已选择10行。

从这里也可以看出,在单一语言排序方式下,如果数据库字符集为非unicode,则采用binary排序方式,且始终区分大小写


最后,让我们来看一下基于语言的多语言排序。

多语言排序并不受字符集的影响,在多语言排序中,ORACLE官方文档将其划分为三个级别:主级别、次级别和第三级别。随着级别的递增,排序时所要考虑的因素也越多,在主级别,排序仅仅考虑待排序字符的大小,不考虑重读音、大小写和标点符号;在次级别,数据库排序需要考虑字符的大小、重读音,但并不好了大小写和标点符号;在第三级别,则字符大小、重读音、大小写和标点符号均需要考虑。那么我们怎么控制排序的级别哪?是通过_AI和_CI后缀来实现的。

示例如下:

主级别:

SQL> select * from (select t.*,nlssort(v,'NLS_SORT=SCHINESE_PINYIN_M_AI') c1,dbms_random.value() c2 from tab_nls t order by c2) order by c1;

V	   BCODE      C1					C2
---------- ---------- ------------------------------ -------------
A	   41	      01EA			      .90738545862
a	   61	      01EA			      .29131364850
Aa	   4161       01EA01EA			      .98120214993
aA	   6141       01EA01EA			      .04583731581
a-a	   612D61     01EA01EA			      .80005010727
a-A	   612D41     01EA01EA			      .06654449141
u	   75	      025B			      .85688941689
ü          A8B9       025B                            .85685506076

已选择8行。

已用时间:  00: 00: 00.00
SQL> 

次级别:

SQL> select * from (select t.*,nlssort(v,'NLS_SORT=SCHINESE_PINYIN_M_CI') c1,dbms_random.value() c2 from tab_nls t order by c2) order by c1;

V	   BCODE      C1					C2
---------- ---------- ------------------------------ -------------
a	   61	      01EA000002		      .03900464074
A	   41	      01EA000002		      .85093266211
aA	   6141       01EA01EA00000202		      .36865684718
a-a	   612D61     01EA01EA00000202		      .79673747253
a-A	   612D41     01EA01EA00000202		      .34622505373
Aa	   4161       01EA01EA00000202		      .92420504676
u	   75	      025B000002		      .44687837071
ü          A8B9       025B00000214                    .15240488192

已选择8行。

已用时间:  00: 00: 00.01

第三级别:

SQL> select * from (select t.*,nlssort(v,'NLS_SORT=SCHINESE_PINYIN_M') c1,dbms_random.value() c2 from tab_nls t order by c2) order by c1;

V	   BCODE      C1					C2
---------- ---------- ------------------------------ -------------
a	   61	      01EA0000020002		      .50154768256
A	   41	      01EA0000020006		      .14626327119
aA	   6141       01EA01EA00000202000206	      .87899379077
a-a	   612D61     01EA01EA0000020200022F02	      .02659477873
a-A	   612D41     01EA01EA0000020200022F06	      .40281310377
Aa	   4161       01EA01EA00000202000602	      .93223946545
u	   75	      025B0000020002		      .47770920012
ü          A8B9       025B000002140002                .44596977028

已选择8行。

已用时间:  00: 00: 00.00

从上面的示例,可以看出,在多语言排序时,只有第三级别是可以对标点符号进行排序的,在其他两种级别下,标点符号会被忽略,也就是官方文档中说的标点符号输入忽略字符,忽略字符的概念只在多语言排序中存在



需要注意的地方:

1:在某些语言中某个字符的大写字符可能对应多个字符,例如德文中的ß对应的大写字符为SS,此时如果在程序中使用upper、lowner、initcap等函数,可能得不到预期的效果,因为这些函数是基于二进制转换的,并没有基于语义。在这种情况下,我们可以尝试NLS_UPPER、nls_lower、NLS_INITCAP等函数,例如:

SQL> SELECT NLS_UPPER ('große','NLS_SORT=XGERMAN'),upper('große')  FROM DUAL;

NLS_UP UPPER(
------ ------
GROSSE GROßE

2:我们知道,oracle是通过排序键进行排序的,而排序键的数据类型为raw,长度限制为2000字节,因此对于长度超过2000的数据进行某些操作时,可能会得不到预期的效果,例如group by 等;

3:如果我们设置nls_sort nls_comp参数,导致排序操作不是按照二进制方式进行,有可能会影响数据库性能,因为索引是依据二进制格式创建的,这是我们可以创建基于语言的函数索引来提高处理效率,但是,使用基于语言的函数索引是有某些限制的:

为了使优化器走函数索引,索引列必须为not null或者在sql 语句的where子句中指定 where nlssort( 列) is not null;

不是所有的操作都支持基于语言的索引:

浅析NLS_COMP、NLS_SORT(三)_第1张图片

你可能感兴趣的:(浅析NLS_COMP、NLS_SORT(三))