在支持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 秒,这是不可接受的。
所以:
一些细节,特别是@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
答案 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个字节”)。祝你好运!