Oracle, PostgreSQL 字符串排序不一致及调整
有一个使用了不同数据库的应,Oracle 和 PostgreSQL,数据库中的记录完全相同,相同的查询语句(相同的排序,至少从字面上来说是的)取到的记录排序却不同,从而产生了 Bug。 简单演示一下默认排序各自在这两种数据库中的行为, 比如说表中有两条记录,'VFORX' 和 'ibbVA'。此处不创建物理表。
Oracle(Linux)
PostgreSQL(Linux)
得到的不同的排序结果
程序中只是简单的使用第一条记录的值,当然程序是有 Bug 的,这是外话。
能立即让它们行为一致的方式是保证排序后结果一致,在 PostgreSQL 中排序时指定 collate "POSIX" 或 "C"
用
问题是解决了,从中也领悟了 collate 在数据库以及排序中的作用。以前隐约记得在创建数据库时不仅要指定数据库使用的字符集,也就是存储所采用的字符集,还会指定一个叫做 collate 的东西,当时并未太在意它意味着什么,而碰到问题后才想起它的重要。
collate 是定义字符集排序规则的关键字,如区不区分大小写,或按照某种语言的自然顺序。现在也回想起为什么 MySQL 的 varchar 类型默认不区分大小写,字段的值是 'AaB', 用 where a = 'aab' 也能查询出来的缘故。
看到
datcollate 也是 en_US.UTF-9, 我们可以在创建数据库时候指定 LC_COLLATE = 'C'
执行上面语句时还必须指定 TEMPLATE = template0, 否则有错。数据库创建后再查看
datcollate 和 datctype 就变成了
切换数据库到 mydb 后
就变成了
或者表的字段级指定 collate 类型
执行上面的语句大概率会出现如下错语
看到的是
需修改成 extended 才行
再重启数据库
假如
返回 NULL 的话就是用了默认的 collation.
Oracle 的 collate 可应用到 VARCHAR2, CHAR, CLOB, 可选值有
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。
Oracle(Linux)
select * from (返回的结果是
select 'VFORX' as value from dual
union
select 'ibbVA' from dual
) order by 1 desc;
1VALUE|
2-----+
3ibbVA|
4VFORX|1select * from (
2 select 'VFORX'
3 union
4 select 'ibbVA'
5) a(c1) order by c1 desc得到的不同的排序结果
1c1 |
2-----+
3VFORX|
4ibbVA|程序中只是简单的使用第一条记录的值,当然程序是有 Bug 的,这是外话。
能立即让它们行为一致的方式是保证排序后结果一致,在 PostgreSQL 中排序时指定 collate "POSIX" 或 "C"
1select * from (
2 select 'VFORX'
3 union
4 select 'ibbVA'
5) a(c1) order by c1 collate "POXIS" desc用
collate "C" 也能得到一样的效果1c1 |
2-----+
3ibbVA|
4VFORX|问题是解决了,从中也领悟了 collate 在数据库以及排序中的作用。以前隐约记得在创建数据库时不仅要指定数据库使用的字符集,也就是存储所采用的字符集,还会指定一个叫做 collate 的东西,当时并未太在意它意味着什么,而碰到问题后才想起它的重要。
collate 是定义字符集排序规则的关键字,如区不区分大小写,或按照某种语言的自然顺序。现在也回想起为什么 MySQL 的 varchar 类型默认不区分大小写,字段的值是 'AaB', 用 where a = 'aab' 也能查询出来的缘故。
PostgreSQL 中如何指定 collate
1SELECT datname, datcollate, datctype
2FROM pg_database
3WHERE datname = 'casdb'; 看到
1datname|datcollate |datctype |
2-------+-----------+-----------+
3casdb |en_US.UTF-8|en_US.UTF-8|datcollate 也是 en_US.UTF-9, 我们可以在创建数据库时候指定 LC_COLLATE = 'C'
1CREATE DATABASE mydb
2 LC_COLLATE = 'C'
3 LC_CTYPE = 'C'
4 TEMPLATE = template0;执行上面语句时还必须指定 TEMPLATE = template0, 否则有错。数据库创建后再查看
1SELECT datname, datcollate, datctype
2FROM pg_database
3WHERE datname = 'mydb'datcollate 和 datctype 就变成了
1datname|datcollate|datctype|
2-------+----------+--------+
3mydb |C |C |切换数据库到 mydb 后
1select * from (
2 select 'VFORX'
3 union
4 select 'ibbVA'
5) a(c1) order by c1 desc就变成了
1c1 |
2-----+
3VFORX|
4ibbVA|或者表的字段级指定 collate 类型
1CREATE TABLE my_table (
2 id serial PRIMARY KEY,
3 name VARCHAR(100) COLLATE "C"
4);
5
6
7ALTER TABLE my_table
8 ALTER COLUMN name TYPE VARCHAR(100) COLLATE "C";Oracle 数据库如何指琮 collate
Oracle 12.2 也开始支持 Schema, 表,列级别的 collate 1CREATE TABLE t1 (
2 id NUMBER,
3 company VARCHAR2(15 CHAR) COLLATE BINARY_CI,
4 CONSTRAINT t1_pk PRIMARY KEY (id)
5);
6
7CREATE TABLE t1 (
8 id NUMBER,
9 company VARCHAR2(15 CHAR),
10 CONSTRAINT t1_pk PRIMARY KEY (id)
11)
12DEFAULT COLLATION BINARY_CI;
13
14CREATE USER test2 IDENTIFIED BY test2
15 DEFAULT TABLESPACE users
16 QUOTA UNLIMITED ON users
17 DEFAULT COLLATION BINARY_CI;执行上面的语句大概率会出现如下错语
SQL Error [43929] [99999]: ORA-43929: Collation cannot be specified if parameter MAX_STRING_SIZE=STANDARD is set.
1SELECT value FROM v$parameter WHERE name = 'max_string_size';看到的是
STANDARD需修改成 extended 才行
1ALTER SYSTEM SET max_string_size=extended SCOPE=SPFILE;再重启数据库
SHUTDOWN IMMEDIATE;Oracle 通常默认的 collate 是 BINARY, 由 NLS_COMP 和 NLS_SORT, NLS_LANGUAGE, NLS_TERRITORY 等决定的
STARTUP UPGRADE;
1SELECT * FROM NLS_DATABASE_PARAMETERS WHERE parameter IN ('NLS_SORT', 'NLS_COMP');1Name |Value |
2---------+--------+
3PARAMETER|NLS_SORT|
4VALUE |BINARY |假如
1SELECT table_name, column_name, collation
2FROM user_tab_columns
3WHERE table_name = 'YOUR_TABLE_NAME';返回 NULL 的话就是用了默认的 collation.
Oracle 的 collate 可应用到 VARCHAR2, CHAR, CLOB, 可选值有
- COLLATE BINARY: 基于二进制,排序最快,大小写敏感
- COLLATE BINARY_CI: Case Insensitive, 比较时(如 >, <, =) 时大小写不敏感,但排序时区分大小写
- COLLATE BINARY_AI: Accent Insensitive, 比较和排序时都不区分大小写
[版权声明]
本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。