Oracle中的LONG类型有两种:
LONG文本类型,能存储2GB的文本。与VARCHAR2或CHAR类型一样,存储在LONG类型中的文本要进行字符集转换。
LONG RAW类型,能存储2GB的原始二进制数据(不用进行字符集转换的数据)。
在此并不解释如何使用LONG类型,而是会解释为什么你不希望在应用中使用LONG(或LONG RAW)类型。首先要注意的是,Oracle文档在如何处理LONG类型方面描述得很明确。Oracle SQL Reference手册指出:
不要创建带LONG列的表,而应该使用LOB列(CLOB、NCLOB、BLOB)。支持LONG列只是为了保证向后兼容性。

1 LONG和LONG RAW类型的限制

 

LONG/LONG RAW类型                                         CLOB/BLOB类型
每个表中只能有一个LONG或LONG RAW列         每个表可以有最多12.000个CLOB或BLOB类型的列
定义用户定义的类型时,不能有LONG/LONG     用户定义的类型完成可以使用CLOB和BLOB类型
RAW类型的属性
不能在WHERE子句中引用LONG类型                 WHERE子句中可以引用LOB类型,而且DBMS_LOB包
                                                                        中提供了大量函数来处理LOB类型
除了NOT NULL之外,完整性约束中不能引用     完整性约束中可以引用LOB类型
LONG类型
LONG类型不支持分布式事务                                 LOB确实支持分布式事务
LONG类型不能使用基本或高级复制技术来复制     LOB完全支持复制
LONG列不能在GROUP BY、ORDER BY或             只要对LOB应用一个函数,将其转换为一个标量SQL类型,

CONNECT BY子句中引用,也不能在使用了         如VARCHAR2、NUMBER或DATE,LOB就可以出现在
DISTINCT、UNIQUE、INTERSECT、MINUS     这些子句中
或UNION的查询中使用
PL/SQL函数/过程不能接受LONG类型的输入         PL/SQL可以充分处理LOB类型
SQL内置函数不能应用于LONG列(如SUBSTR)     SQL函数可以应用于LOB类型
CREATE TABLE AS SELECT语句中不能使用             LOB支持CREATE TABLE AS SELECT
LONG类型
在包含LONG类型的表上不能使用ALTER TABLE MOVE         可以移动包含LOB的表

如果表中有一个LONG列,那么很多事情都不能做。对于所有新的应用,甚至根本不该考虑使用LONG类型。相反,应该使用适当的LOB类型。对于现有的应用,如果受到表12.-2所列的某个限制,就应该认真地考虑将LONG类型转换为相应的LOB类型。由于已经做了充分考虑来提供向后兼容性,所以编写为使用LONG类型的应用也能透明地使用LOB类型。

 

2 处理遗留的LONG类型

“那如何考虑Oracle中的数据字典呢?“数据字典中散布着LONG列,这就使得字典列的使用很成问题。例如,不能使用SQL搜索ALL_VIEWS字典视图来找出包含文本HELLO的所有视图:

scott@ORCL>select *
  2  from all_views
  3  where text like '%HELLO%';
where text like '%HELLO%'
      *
第 3 行出现错误:
ORA-00932: 数据类型不一致: 应为 NUMBER, 但却获得 LONG

这个问题并不只是ALL_VIEWS视图才有,许多视图都存在同样的问题:

scott@ORCL>select table_name, column_name
  2  from dba_tab_columns
  3  where data_type in ( 'LONG', 'LONG RAW' )
  4  and owner = 'SYS'
  5  and table_name like 'DBA%';

TABLE_NAME                                                   COLUMN_NAME
------------------------------------------------------------ -------------------
-----------------------------------------
DBA_ADVISOR_SQLPLANS                                         OTHER
DBA_ARGUMENTS                                                DEFAULT_VALUE
DBA_CLUSTER_HASH_EXPRESSIONS                                 HASH_EXPRESSION
DBA_CONSTRAINTS                                              SEARCH_CONDITION
DBA_IND_EXPRESSIONS                                          COLUMN_EXPRESSION
DBA_IND_PARTITIONS                                           HIGH_VALUE
DBA_IND_SUBPARTITIONS                                        HIGH_VALUE
DBA_MVIEWS                                                   QUERY
DBA_MVIEW_AGGREGATES                                         MEASURE
DBA_MVIEW_ANALYSIS                                           QUERY
DBA_NESTED_TABLE_COLS                                        DATA_DEFAULT
DBA_OUTLINES                                                 SQL_TEXT
DBA_REGISTERED_MVIEWS                                        QUERY_TXT
DBA_REGISTERED_SNAPSHOTS                                     QUERY_TXT
DBA_SNAPSHOTS                                                QUERY
DBA_SQLSET_PLANS                                             OTHER
DBA_SQLTUNE_PLANS                                            OTHER
DBA_SUBPARTITION_TEMPLATES                                   HIGH_BOUND
DBA_SUMMARIES                                                QUERY
DBA_SUMMARY_AGGREGATES                                       MEASURE
DBA_TAB_COLS                                                 DATA_DEFAULT
DBA_TAB_COLUMNS                                              DATA_DEFAULT
DBA_TAB_PARTITIONS                                           HIGH_VALUE
DBA_TAB_SUBPARTITIONS                                        HIGH_VALUE
DBA_TRIGGERS                                                 TRIGGER_BODY
DBA_VIEWS                                                    TEXT
DBA_VIEWS_AE                                                 TEXT

已选择27行。

如果你想在SQL中使用这些列,就需要将它们转换为一种对SQL友好的类型。可以使用一个用户定义的函数来做到这一点。以下例子展示了如何使用一个LONG SUBSTR函数来达到这个目的,这个函数允许将任何4,000字节的LONG类型转换为一个VARCHAR2,以便用于SQL。完成后,就能执行以下查询。首先 如何得到一个LONG类型的子串:

scott@ORCL>create or replace package long_help
  2  authid current_user
  3  as
  4  function substr_of
  5  ( p_query in varchar2,
  6  p_from in number,
  7  p_for in number,
  8  p_name1 in varchar2 default NULL,
  9  p_bind1 in varchar2 default NULL,
 10  p_name2 in varchar2 default NULL,
 11  p_bind2 in varchar2 default NULL,
 12  p_name3 in varchar2 default NULL,
 13  p_bind3 in varchar2 default NULL,
 14  p_name4 in varchar2 default NULL,
 15  p_bind4 in varchar2 default NULL )
 16  return varchar2;
 17  end;
 18  /

程序包已创建。

在第2行上 指定了AUTHID CURRENT_USER。 这使得这个包会作为调用者运行,拥有所有角色和权限。这一点很重要,原因有两个。首先,我们希望数据库安全性不要受到破坏,这个包只返回允许我们(调用 者)看到的列子串。其次,我们希望只在数据库中将这个包安装一次,就能一直使用它的功能;使用调用者权限可以保证这一点。如果我们使用PL/SQL的默认安全模型(定义者权限,define right),这个包会以所有者的权限来运行,这样一来,它就只能看到包所有者能看到的数据,这可能不包括允许调用者看到的数据集。

函数SUBSTR_OF的基本思想是取一个查询,这个查询最多只选择一行和一列:即我们感兴趣的LONG值。如果需要,SUBSTR_OF会解析这个查询,为之绑定输入,并通过查询获取结果,返回LONG值中必要的部分。
包体(实现)最前面声明了两个全局变量。G_CURSOR变量保证一个持久游标在会话期间一直打开。这是为了避免反复打开和关闭游标,并避免不必要地过多解析SQL。第二个全局变量G_QUERY用于记住这个包中已解析的上一个SQL查询的文本。只要查询保持不变,就只需将其解析一次。因此,即使一个查询中查询了5,000行,只要我们传入这个函数的SQL查询不变,就只会有一个解析调用:

scott@ORCL>create or replace package body long_help
  2  as
  3
  4  g_cursor number := dbms_sql.open_cursor;
  5  g_query varchar2(32765);
  6
  7  procedure bind_variable( p_name in varchar2, p_value in varchar2 )
  8
  9  is
 10  begin
 11     if ( p_name is not null )
 12     then
 13             dbms_sql.bind_variable( g_cursor, p_name, p_value );
 14     end if;
 15  end;
 16
 17  function substr_of
 18  ( p_query in varchar2,
 19     p_from in number,
 20     p_for in number,
 21     p_name1 in varchar2 default NULL,
 22     p_bind1 in varchar2 default NULL,
 23     p_name2 in varchar2 default NULL,
 24     p_bind2 in varchar2 default NULL,
 25     p_name3 in varchar2 default NULL,
 26     p_bind3 in varchar2 default NULL,
 27     p_name4 in varchar2 default NULL,
 28     p_bind4 in varchar2 default NULL )
 29  return varchar2
 30
 31  as
 32     l_buffer varchar2(4000);
 33     l_buffer_len number;
 34
 35  begin
 36             if ( nvl(p_from,0) <= 0 )
 37             then
 38                     raise_application_error
 39                     (-20002, 'From must be >= 1 (positive numbers)' );
 40             end if;
 41
 42             if ( nvl(p_for,0) not between 12 and 4000 )
 43             then
 44                     raise_application_error
 45                     (-20003, 'For must be between 12 and 4000' );
 46             end if;
 47
 48             if ( p_query <> g_query or g_query is NULL )
 49             then
 50                     if ( upper(trim(nvl(p_query,'x'))) not like 'SELECT%')
 51                     then
 52                             raise_application_error
 53                             (-20001, 'This must be a select only' );
 54                     end if;
 55                     dbms_sql.parse( g_cursor, p_query, dbms_sql.native );
 56                     g_query := p_query;
 57             end if;
 58
 59             bind_variable( p_name1, p_bind1 );
 60             bind_variable( p_name2, p_bind2 );
 61             bind_variable( p_name3, p_bind3 );
 62             bind_variable( p_name4, p_bind4 );
 63
 64             dbms_sql.define_column_long(g_cursor, 1);
 65             if (dbms_sql.execute_and_fetch(g_cursor)>0)
 66             then
 67                     dbms_sql.column_value_long
 68                     (g_cursor, 1, p_for, p_from-1,
 69                     l_buffer, l_buffer_len );
 70             end if;
 71     return l_buffer;
 72  end substr_of;
 73
 74  end;
 75  /

程序包体已创建。
scott@ORCL>select *
  2  from (
  3     select owner, view_name,
  4             long_help.substr_of( 'select text
  5             from dba_views
  6             where owner = :owner
  7             and view_name = :view_name',
  8             1, 4000,
  9             'owner', owner,
 10             'view_name', view_name ) substr_of_view_text
 11     from dba_views
 12     where owner = user
 13  )
 14  where upper(substr_of_view_text) like '%INNER%'
 15  /

未选定行
scott@ORCL>select *
  2  from (
  3     select table_owner, table_name, partition_name,
  4             long_help.substr_of
  5                     ( 'select high_value from all_tab_partitions
  6                     where table_owner = :o and table_name = :n and partition
_name = :p',
  7             1, 4000,
  8             'o', table_owner,
  9             'n', table_name,
 10             'p', partition_name ) high_value
 11     from all_tab_partitions
 12     where table_name = 'T'
 13     and table_owner = user
 14  )
 15  where high_value like '%2003%'
 16  /

未选定行

还有一种方法,可以使用TO_LOB内置函数和一个全局临时表,将LONG或LONG RAW临时地转换为CLOB或BLOB。为此,PL/SQL过程可以如下:

Insert into global_temp_table ( blob_column )
select to_lob(long_raw_column) from t where...

这在偶尔需要处理单个LONG RAW值的应用中能很好地工作。不过,为此需要做的工作太多了。应该干脆将LONG RAW一次性转换为BLOB,然后处理BLOB。

转载于:https://my.oschina.net/u/1862478/blog/1933440

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐