1

在支持 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,没有其他选择可用。

这适用于甲骨文:

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

但是,性能非常糟糕。直接返回 LongText 的查询需要 70-80 毫秒处理大约 9k 行,但上述构造需要 30 到 60,不可接受。

所以:

  1. 是否有任何其他 Oracle 类型我可以强制这两个列可以容纳与 CLOB 一样多的数据?理想情况下是更面向文本的东西,比如 MySQL 的 LONGTEXT 或 SQL Server 的 NTEXT(或者更好的是 NVARCHAR(MAX))?
  2. 我应该考虑其他任何方法吗?

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

Oracle版本:Oracle Database 11g 11.2.0.1.0 64bit Production
不确定我是否是唯一的用户,但相对时间仍然惊人。

我看到我之前发布的性能的小桌子的统计数据:
  行数:9,237
  varchar 列总长度:148,516
  clob 列总长度:227,020
4

1 回答 1

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 个字节”)。祝你好运!

于 2013-11-25T07:26:59.467 回答