oracle - Oracle12:lpad 函数不像 Oracle11 那样管理 2 字节字符
问题描述
我面临一个非常奇怪的问题,即 Oracle 12c 没有像 Oracle 11g 那样管理 2 字节字符,从而导致 LPAD 等某些功能出现问题。
我们有两个数据库,一个 11g 和一个 12c,具有相同的 NLS 参数,但是在 LPAD 等函数中,11g 将西里尔字符管理为 1 个字节,而 12c 将它们管理为 2 个字节,导致问题:如果我们需要某个值是 40字符长,其中的每个西里尔字符在填充时将计为 2 个字节,但将显示为 1 个字符,这意味着 LPADded 到 40 的 5 个西里尔字符实际上会生成一个长度为 35 的值。
Oracle 官方文档 ( https://docs.oracle.com/database/121/SQLRF/functions107.htm#SQLRF00663 )中描述了这种行为,但是对于多个版本(包括 11g)都是如此,因此不清楚我为什么这两个版本在相同的设置下应该有不同的行为,以及如何管理它。
重要笔记:
- 两个数据库都管理欧洲字符(包括来自一些东欧字母的特殊字符,如希腊语等)和俄罗斯字符(西里尔文),因此将区域切换为“俄罗斯”并不是一个真正的选择;
- 使用 nvarchar2 而不是 varchar2 解决了这个问题(它切换到国家字符集,即 UTF16),但这意味着将 4 TB 数据库中的所有 varchar2 列都切换到 nvarchar2,这非常麻烦并且可能导致大量空间浪费;
- 问题出现在管理已存储在数据库中的数据的存储过程中,因此这看起来不像是客户端配置错误。
NLS 参数的数据库属性(我删除了日期和货币格式,因为它们并不真正相关):
+-----------------------------------+------------+------------+
| Parameter | 12c | 11g |
+-----------------------------------+------------+------------+
| NLS_CHARACTERSET | AL32UTF8 | AL32UTF8 |
| NLS_COMP | BINARY | BINARY |
| NLS_DATE_LANGUAGE | AMERICAN | AMERICAN |
| NLS_ISO_CURRENCY | AMERICA | AMERICA |
| NLS_LANGUAGE | AMERICAN | AMERICAN |
| NLS_LENGTH_SEMANTICS | BYTE | BYTE |
| NLS_NCHAR_CHARACTERSET | AL16UTF16 | AL16UTF16 |
| NLS_NCHAR_CONV_EXCP | FALSE | FALSE |
| NLS_NUMERIC_CHARACTERS | ., | ., |
| NLS_RDBMS_VERSION | 12.1.0.2.0 | 11.2.0.4.0 |
| NLS_SORT | BINARY | BINARY |
| NLS_TERRITORY | AMERICA | AMERICA |
+-----------------------------------+------------+------------+
V$Parameter 属性(相同,删除日期):
+-----------------------------------+----------------+----------------+
| Parameter | 12c | 11g |
+-----------------------------------+----------------+----------------+
| NLS_COMP | BINARY | BINARY |
| NLS_DATE_LANGUAGE | ENGLISH | ENGLISH |
| NLS_ISO_CURRENCY | UNITED KINGDOM | UNITED KINGDOM |
| NLS_LANGUAGE | ENGLISH | ENGLISH |
| NLS_LENGTH_SEMANTICS | CHAR | CHAR |
| NLS_NCHAR_CONV_EXCP | FALSE | FALSE |
| NLS_NUMERIC_CHARACTERS | ., | ., |
| NLS_SORT | BINARY | BINARY |
| NLS_TERRITORY | UNITED KINGDOM | UNITED KINGDOM |
+-----------------------------------+----------------+----------------+
来自 12c 数据库的示例:
SELECT 'This is a test данные испытаний' as "Original",
lpad(nvl('This is a test данные испытаний', ' '), 40) as "LPADded",
lpad(nvl('данные испытаний', ' '), 40) as "Cyrillic only",
lpad(nvl('This is a test', ' '), 40) as "Non-cyrillic only",
lpad(nvl(to_nchar('данные испытаний'), ' '), 40) as "NChar cyrillic only",
lpad(nvl(to_nchar('This is a test данные испытаний'),
' '),
40) as "NChar mixed"
FROM dual;
结果:
This is a test данные испытаний (original - 31 chars)
This is a test данные испыта (std lpad - 28 chars)
данные испытаний (std lpad cyrillic only - 25 chars)
This is a test (std lpad non-cyrillic only - 40 chars)
данные испытаний (nchar lpad cyrillic only - 40 chars)
This is a test данные испытаний (nchar lpad mixed - 40 chars)
在 11g 数据库中,以上所有内容(当然,原始的除外)的长度为 40 个字符。
谢谢
解决方案
我认为这个问题与 UNICODE 中的模糊字体有关。您可以在此处找到说明:
http://unicode.org/reports/tr11/#Ambiguous
在甲骨文中,如果您使用
lengthc function
总是返回字符的实际长度,而
lenghtb function
返回字符的字节占用。
一种可能的解决方案是使用以下形式:
我尝试使用占用 2 个字节的 UNISTR('\4F4F')
select lpad('pippo'||UNISTR('\4F4F'),10+lengthc(UNISTR('\4F4F')),'x') from dual;
并且显示的长度是所需的长度
推荐阅读
- javascript - 上传到服务器以进行电子生成器自动更新所需的工件列表?
- xpath - 如何使用 XPath 从 div 类中获取 href 锚文本
- mongodb - 匹配当前文档中的数组
- java - 如何为许多具体类型创建抽象工厂
- python-3.x - 使用 for 循环将列添加到 pandas 数据框
- python - 如何编写一个 Django 查询来连接两个没有公共列的表并使用公式计算时间?
- python-3.x - 如果发生 UnicodeEncodeError 是否可以删除文件?
- pandas - 查找最长的段
- c# - JsonSerializer:区分 DateTime 对象的种类
- php - bind_param 不会替换我准备好的语句中的 ?s