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。