11

我在这里遇到了一个我无法理解的inusited情况。我将要写的函数的文档也没有点亮这件事。

我有一个带字段的表titulo varchar2(55)。我在巴西,这个字段中的一些字符有口音,我的目标是创建一个没有口音的类似字段(替换为原来的字符áa等等。)。

我可以使用一堆函数来做到这一点replacetranslate以及其他函数,但我在互联网上找到了一个看起来更优雅的函数,然后我使用它。这就是问题所在。

我的更新代码是这样的:

update myTable 
   set TITULO_URL = replace(
                 utl_raw.cast_to_varchar2(
                           nlssort(titulo, 'nls_sort=binary_ai')
                                         )
                            ,' ','_');

正如我所说,目标是将每个重音字符转换为其等效字符,而没有重音符号加上空格字符_

然后我得到了这个错误:

ORA-12899: value too large for column 
     "mySchem"."myTable"."TITULO_URL" (actual: 56, maximum: 55)

起初我虽然可能这些功能正在添加一些字符,让我检查一下。我做了一个选择命令让我得到一个titulo有 55 个字符的行。

select titulo from myTable where length(titulo) = 55

然后我选择一行做一些测试,我选择的行有这个值:('FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD'我确实改变了一点来保存数据,但结果是一样的)

当我执行以下选择语句时,事情变得很奇怪:

select a, length(a), b, length(b)
  from ( select 'FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD' a,
                replace(
                   utl_raw.cast_to_varchar2( 
                               nlssort('FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD', 'nls_sort=binary_ai')
                                           )
                       ,' ','_') b
           from dual
       )

这个 sql 的结果是(为了更好的可视化,我将把这些值放在一起):

                     a                                       LENGTH(a)
FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD        55     
                     b                                       LENGTH(b)
fghjtoryo_de_yhjks_da_dghqcaa_de_asga_xcvbgl_easdeonasd        56

比较两个字符串的大小没有区别:

FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD
fghjtoryo_de_yhjks_da_dghqcaa_de_asga_xcvbgl_easdeonasd

我已经在 Toad、PLSQL Developer 和 SQLPLUSW 上测试了这个查询,结果都一样。所以我的问题是这个 LENGTH(b)=56 来自哪里?我知道它可能与字符集有关,但我不知道为什么。我什至用trim命令测试过,结果是一样的。

我做的另一个测试

  • substr(b, 1,55)结果与上面的文本相同
  • lenght(trim(b))结果是 56
  • substr(b,56)结果是空的(没有空,没有空格,只是空的)

@Sebas 建议:

  • LENGTHB(b)结果是 56
  • ASCII(substr(b,56))

所以,再一次:这个 LENGTH(b)=56 是从哪里来的

很抱歉发了这么长的帖子,并感谢那些来到这里的人(阅读所有内容)。感谢那些无论如何不读的人:)

此致

4

3 回答 3

3

“nlssort”函数的文档没有说明输出字符串将是输入字符串的规范化,或者它们将具有相同的长度。该函数的目的是返回可用于对输入字符串进行排序的数据。

请参阅http://docs.oracle.com/cd/E11882_01/server.112/e26088/functions113.htm#SQLRF51561

很想用它来规范你的字符串,因为它显然有效,但你在这里赌博......

哎呀,它甚至可以产生一个LENGTH(b)=200并且仍然在做它应该做的事情:)

于 2013-11-07T20:47:42.040 回答
1

1) Oracle 区分字节长度和字符长度:varchar2(55)表示 55 个字节,所以 55 个 UTF-8 字符只有在幸运的情况下才适合:你应该将你的字段声明为varchar2 (55 char).

2)扭曲像

replace(utl_raw.cast_to_varchar2(nlssort(
'FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD', 
'nls_sort=binary_ai')),' ','_') b

是胡说八道,您只是用一些相似的字符串替换字符串。您的数据库有一个编码,所有字符串都用该编码表示,这决定了它们的字节长度;mcalmeida 解释的任意变化引入了随机数据相关噪声,如果您进行比较,这绝不是一件好事。

3)关于去除重音的规定任务,您应该自己使用REPLACE,TRANSLATE等,因为只有您知道您的要求;它不是 Unicode 规范化或任何“标准”,没有捷径。您可以定义一个函数并从任何查询和任何 PL/SQL 程序中调用它,而无需难看的复制和粘贴。

于 2013-12-09T10:37:02.577 回答
0

函数“nlssort()”在字符串的原始二进制末尾返回带有额外 00 的二进制。

测试:

select NLSSORT('abc') from dual

输出:

61626300

这个问题可以通过从 NLSSORT 的返回中删除最后 2 位来解决。

解决方案:

  select a, length(a), b, length(b)   
    from ( select 'FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD' a,
                  replace(
                      utl_raw.cast_to_varchar2( 
                                 substr(nlssort('FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD', 'nls_sort=binary_ai'),1, 
                                                  length(nlssort('FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD', 'nls_sort=binary_ai'))-2 
                                                 )
                                          )

                         ,' ','_') b
             from dual
     )

  )
于 2013-12-10T09:41:24.560 回答