【问题标题】:Oracle12: lpad function does not manage 2 bytes characters as Oracle11Oracle 12c:lpad 函数不像 Oracle 11g 那样管理 2 字节字符
【发布时间】:2021-03-11 22:30:44
【问题描述】:

我面临一个非常奇怪的问题,即 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)都是如此,所以我不清楚为什么这两个版本在相同的设置下应该有不同的行为,并且以防万一,如何管理。

重要提示:

  1. 这两个数据库都管理欧洲字符(包括一些东欧字母表中的特殊字符,如希腊语等)和俄语字符(西里尔字母),因此将区域切换到“RUSSIA”并不是一个真正的选择;
  2. 使用 nvarchar2 代替 varchar2 可以解决问题(它会切换到 UTF16 的国家字符集),但这意味着将 4 TB 数据库中的所有 varchar2 列都切换到 nvarchar2,这非常麻烦,并且可能会导致大量浪费空间;
  3. 问题出现在管理已存储在数据库中的数据的存储过程中,因此这看起来不像是客户端配置错误。

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 个字符。

谢谢

【问题讨论】:

  • community.oracle.com/tech/developers/discussion/2251929/… ... 我想知道您是否可以使用 lengthb 做一些事情,然后添加您需要的字符数,就像这个建议一样?
  • 请问edit 您正在使用LPAD 查询的问题以及每个版本的输出示例。
  • @MT0 添加了几个例子
  • @jad 不是一个真正的选择,因为这需要在所有存储过程、视图、查询等中更改对 LPAD、SUBSTR 等函数的所有调用,这不是我能做到的由于外部限制而这样做。

标签: oracle oracle11g character-encoding oracle12c cyrillic


【解决方案1】:

我认为问题与 UNICODE 中的模糊字体有关。您可以在此处找到说明:

http://unicode.org/reports/tr11/#Ambiguous

如果你使用在 oracle 中

lengthc function 

总是返回字符的实际长度, 而

 lenghtb function 

返回字符占用的字节数。

可能的解决方案是使用以下形式:

我尝试使用占用 2 个字节的 UNISTR('\4F4F')

 select lpad('pippo'||UNISTR('\4F4F'),10+lengthc(UNISTR('\4F4F')),'x') from dual;

并且显示的长度是所需的长度

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-31
    • 2017-03-10
    • 1970-01-01
    • 1970-01-01
    • 2015-07-30
    • 2012-02-06
    • 1970-01-01
    相关资源
    最近更新 更多