Oracle:将VARCHAR2和CLOB强制转换为相同类型而不进行截断

时间:2013-11-23 14:15:18

标签: performance oracle casting clob coerce

在支持MS SQL Server,MySQL和Oracle的应用程序中,有一个包含以下相关列的表(此处显示的类型适用于Oracle):

ShortText VARCHAR2(1700) indexed
LongText CLOB

应用程序在ShortText中存储850个字符或更少的字符,在LongText中存储较长的值。我需要创建一个视图来返回该数据,无论它在哪个列中。这适用于SQL Server和MySQL:

SELECT
  CASE
    WHEN ShortText IS NOT NULL THEN ShortText
    ELSE LongText
  END AS TheValue
FROM MyTable

但是,在Oracle上,它会生成此错误:

ORA-00932: inconsistent datatypes: expected CHAR got CLOB 

...意味着Oracle不会将两列隐式转换为相同的类型,因此查询必须明确地执行此操作。不希望数据被截断,因此使用的类型必须能够容纳与CLOB一样多的数据,据我所知(不是Oracle专家)仅指CLOB,没有其他选择可用。

这适用于Oracle:

SELECT
  CASE
    WHEN ShortText IS NOT NULL THEN TO_CLOB(ShortText)
    ELSE LongText
  END AS TheValue
FROM MyTable

然而,表现非常糟糕。对于大约9k行,直接返回LongText的查询需要70-80 ms,但上面的构造需要30到60 ,这是不可接受的。

所以:

  1. 我是否可以将两个列强制转换为其他任何Oracle类型 可以保存与CLOB一样多的数据?理想情况下更多 面向文本,如MySQL的LONGTEXT,或SQL Server的NTEXT(甚至是 更好,NVARCHAR(MAX))?
  2. 我应该看看其他任何方法吗?

  3. 一些细节,特别是@Guido Leenders要求的细节:

    Oracle version: Oracle Database 11g 11.2.0.1.0 64bit Production
    Not certain if I was the only user, but the relative times are still striking.
    
    Stats for the small table where I saw the performance I posted earlier:
      rowcount: 9,237
      varchar column total length: 148,516
      clob column total length: 227,020

1 个答案:

答案 0 :(得分:1)

to_clob非常昂贵,所以尽量避免使用它。但我认为它应该对9K行表现合理。根据我们开发的具有类似数据模型行为的应用程序之一的测试用例:

create table bubs_projecten_sample
( id number
, toelichting varchar2(1700)
, toelichting_l clob
)

begin
  for i in 1..10000
  loop
    insert into bubs_projecten_sample
    ( id
    , toelichting
    , toelichting_l
    )
    values
    ( i
    , case when mod(i, 2) = 0 then 'short' else null end
    , case when mod(i, 2) = 0 then rpad('long', i, '*') else null end
    )
    ;
  end loop;
  commit;
end;

现在确保写入缓存和脏块中的所有内容:

select *
from   bubs_projecten_sample

测试表现:

create table bubs_projecten_flat
as
select id
,      to_clob(toelichting) toelichting_any
from   bubs_projecten_sample
where  toelichting is not null
union all
select id
,      toelichting_l
from   bubs_projecten_sample
where  toelichting_l is not null

创建表在普通入门级服务器上花费不到1秒,包括写出数据,17K一致性获取,4K物理读取。存储在磁盘上(注意rpad)对于toelichting为25K,对于toelichting_l为16M。

您能进一步详细说明问题吗?

请检查大型CLOB是否未内联存储。通常,大型CLOB存储在单独的系统维护表中。在表中存储大型CLOB可能会使表格中的全表扫描变得昂贵。

另外,我可以想象总是填充两列。您仍然可以为第一个这么多字符编制索引。您只需要使用指示符在表中记忆CLOB或shortText列是否正在领先。

作为旁注;我看到850和1700之间的区别。我建议让它们相等,但记得检查你是否使用字符语义创建表。这可以通过使用:“varchar2(850 char)”在语句级别完成。请注意,Oracle实际上会创建一个适合850 * 4字节的列(至少在AL32UTF8中,“32”代表“每个字符最多4个字节”)。祝你好运!