Sql LENGTH命令的奇怪行为-ORACLE

Sql LENGTH命令的奇怪行为-ORACLE,sql,oracle,Sql,Oracle,我在这里遇到了一个我无法理解的尴尬局面。我将要写的函数的文档中也没有什么东西可以说明这一点 我有一个带有字段的表titulo varchar2(55)。我在巴西,这个字段中的一些字符有重音,我的目标是创建一个没有重音的类似字段(由原始字符替换,因为这个a变成a等等) 我可以使用一系列函数来实现这一点,如replace,translate和其他函数,但我在互联网上发现,它们更优雅,然后我就使用它。这就是问题所在 我的更新代码如下: update myTable set TITULO_URL

我在这里遇到了一个我无法理解的尴尬局面。我将要写的函数的文档中也没有什么东西可以说明这一点

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

我可以使用一系列函数来实现这一点,如
replace
translate
和其他函数,但我在互联网上发现,它们更优雅,然后我就使用它。这就是问题所在

我的更新代码如下:

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)
首先,我认为这些函数可能在添加字符,让我检查一下。我执行了一个select命令来获取一行,其中
titulo
有55个字符

select titulo from myTable where length(titulo) = 55
然后我选择一行进行一些测试,我选择的行有以下值:
'FGHJTÓRYO DE yhks DA DGHQÃa DE ASGA XCVBGL EASDEÔNASD'
(我确实更改了它以保留数据,但结果相同)

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

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的结果是(为了更好地可视化,我将一个接一个地放置这些值):

将两个字符串一个接一个地进行比较,大小没有差异:

FGHJTÓRYO DE YHJKS DA DGHQÇÃA DE ASGA XCVBGL EASDEÔNASD
fghjtoryo_de_yhjks_da_dghqcaa_de_asga_xcvbgl_easdeonasd
我在Toad、plsqldeveloper和SQLPLUSW上测试了这个查询,结果都是一样的。所以我的问题是,这个长度(b)=56来自哪里?我知道这可能与角色集有关,但我不明白为什么。我甚至用
trim
命令进行了测试,结果是一样的

我做的另一个测试

  • substr(b,1,55)
    结果与上述文本相同
  • lenght(trim(b))
    结果是56
  • substr(b,56)
    结果为空(没有null,没有空格,只有空)
@Sebas建议:

  • LENGTHB(b)
    结果是56
  • ASCII(substr(b,56))
那么,同样:这个长度(b)=56来自哪里

很抱歉发了这么长的帖子,感谢大家来到这里(阅读所有内容)。 感谢那些不读书的人:)


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

很容易使用它来规范化字符串,因为它显然是有效的,但你在这里赌博

见鬼,它甚至可以产生一个长度(b)=200并且仍然在做它应该做的事情:)

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) 关于消除口音的任务,你应该自己做替换、翻译等,因为只有你知道你的要求;它不是Unicode规范化或任何“标准”,没有快捷方式。 您可以定义一个函数并从任何查询和任何PL/SQL程序调用它,而无需进行难看的复制和粘贴。

函数“nlssort()”返回二进制,并在字符串的原始二进制末尾加上00

测试:

select NLSSORT('abc') from dual
输出:

61626300

这个问题可以通过删除NLSSORT返回的最后两位数字来解决

解决方案:

  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
     )

  )

LENGTHB(b)
说了什么?可能这是NLSSORT的常见行为——用chr(0)终止字符串。正如@EgorSkriptunoff所说,NLSSORT添加了NULL,这就是为什么长度不同(并且很容易修剪)的答案。如果你问为什么nlssort首先要添加空终止符,我不确定,但它可能是C代码的遗留,其中空终止字符串很常见(同样,纯粹是猜测)@tbone是的,我熟悉C语言编程,知道空终止符,当EgorSkriptunoff从NLSSORT中指出这种行为时,我只是猜到了和你一样的结果(一些C语言的遗产)。奇怪的是Oracle把它留在了那里。终止null似乎是错误13386193的原因,它影响了基于
nlssort
的基于函数的索引的导出/导入。这在12c和11.2.0.4中是固定的,但我想他们在数据泵中解决了这一问题,而不是改变
nlssort
的工作方式,因为这可能会导致现有索引和其他代码出现问题。因此没有帮助,但我认为这很有趣…
varchar2(55)
也可能意味着“55个字符”-这取决于
NLS_LENGTH_语义的设置
如果您花一些时间阅读关于这个问题的评论,您将看到这个行为是一个在版本12c和11.2.0.4上纠正的错误。主要问题是nlssort(由于该错误)在转换后在字符串(\0)的末尾留下了一个空终止符。对于
胡说八道
来说,你完全错了,
你只是在用一些相似的字符串替换字符串
这正是我想要做的。使用我研究过的这个演员阵容
  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
     )

  )