我有一个大约有100.000行的表,过去看起来或多或少是这样的:
id varchar(20),
omg varchar(10),
ponies varchar(3000)
添加对国际字符的支持时,我们必须将ponies
列重新定义为nclob,因为3000(多字节)字符对于nvarchar来说太大了
id varchar(20),
omg varchar(10),
ponies nclob
我们使用java中的预准备语句从表中读取:
select omg, ponies from tbl where id = ?
在'ponies'列更改为NCLOB和其他一些更改为使用nchar列的表之后,Oracle 11g决定执行全表扫描,而不是使用id
列的索引,这会导致我们的申请要停止。
在查询中添加提示时,会使用索引,一切都很“正常”,或者说比列为varchar时要慢一点。
我们已经定义了以下连接属性:
oracle.jdbc.convertNcharLiterals="true"
defaultNChar=true
顺便说一句,数据库统计信息已更新。
我没有时间查看所有查询,因此我不知道是否忽略了其他索引,但是我不得不担心defaultNChar设置会以某种方式混淆优化器,因为id不是nchar吗?无论是在几乎所有查询上撒上提示还是重新定义所有密钥都会很尴尬。
或者,全表扫描被认为是无意义的,因为“大”nclob将被加载 - 这个假设似乎已经减少3个数量级,我想相信Oracle比这更聪明。
或者只是运气不好?或者是其他东西?是否可以在没有提示的情况下修复?
答案 0 :(得分:3)
问题原来是jdbc-flag defaultNChar = true。
如果参数作为nchar / nvarchar发送,则Oracles优化器不会使用在char / varchar2列上创建的索引。这几乎是有意义的,因为我想你可能会得到幻影结果。
我们主要使用存储过程,参数定义为char / varchar2 - 在执行查询之前强制转换 - 所以我们没有注意到这种效果,除了在使用动态sql的一些地方。
解决方案是将数据库转换为AL32UTF8并删除nchar列。
答案 1 :(得分:2)
当您重新统计统计信息时,您估算或使用dbms_stats.gather_table_stats
并使用estimate_percentage> 50%?如果没有,则使用dbms_stats,其估计值为100%。
如果您的表只有3列,并且这些是您要返回的列,则无论您提示什么,即使id
索引是唯一的,最佳索引也是3列。现在,您的解释计划应该通过唯一的索引扫描,然后是rowid的表访问。如果您索引所有3列,则这将成为唯一扫描,因为您返回的所有信息都已在索引中,并且无需重新访问该表即可获取它。订单将id, omg, ponies
在where子句中使用它。这将有效地使您的表格成为index organized table
,这比拥有单独的索引更容易。显然,在之后收集统计数据。
说出我实际上并不确定的所有内容你可以索引一个nclob,无论你做什么,列的大小都会产生影响,因为你需要做的磁盘读取时间越长。
答案 2 :(得分:0)
很抱歉,但我不明白你为什么要把你的专栏小马从varchar改为clob。如果此列中的最大长度为3000个字符,为什么不使用NVARCHAR2列呢?据我所知,nvarchar2最多可以容纳4000个字符。
但是你是对的,当国家字符集是AL16UTF16时,允许的最大列大小是2000个字符,当它是UTF8时,允许的最大列大小是4000个字符。