将子字符串与多列进行比较
Comparing Substring Against Multiple Columns
我有一个 table,它有 20 个相似的文本属性列,text1..text20。这些列的类型为 CLOB。我正在查找这些文本属性列之一包含特定短语(例如“%unemployed%”)的行。我需要知道两件事,哪些行匹配,哪些列匹配。我以为我可以使用 ANY 作为起点,但我遇到了问题。
似乎 ANY 语句不适用于“%”。例如,
select * from emp where 'BLAKE' = ANY(ename, job); -- Returns Data
但是
select * from emp where '%BLAKE%' = ANY(ename, job) -- No Data Found
执行此操作的正确方法是什么?伪代码将是...
Select name, addr,
which_column_matched(%unemployed%, text1..text20),
text1..text20
from table
where %unemployed% = ANY(text1..text20);
您可以使用子查询来识别匹配的第一列,然后 return 那:
select t.*
from (select t.*,
(case when text1 like '%unemployed%' then 'text1'
when text2 like '%unemployed%' then 'text2'
. . .
when text20 like '%unemployed%' then 'text20'
end) as col_match
from t
) t
where col_match is not null;
在 Oracle 中,您可以为此使用 unpivot
。它仍然需要您枚举所有列,但语法非常简洁。
如果您希望每个匹配的列都有一条记录:
select *
from emp unpivot (col for src in (text1, text2, text3))
where col like '%unemployed%'
如果您想要一个包含匹配列列表的附加列,您可以聚合结果集:
select ename, listagg(src, ', ')
from emp unpivot (col for src in (text1, text2, text3))
where col like '%unemployed%'
group by ename
我一直担心 Oracle 如何处理 CLOB
数据,所以这里的测试表明 Pivot 解决方案应该可以解决问题。
drop table emptest;
-- Assuming we are using the venerable EMP table
create table emptest as select * from emp;
alter table emptest add(
text1 CLOB,
text2 CLOB,
text3 CLOB
)
/
declare
v_text clob;
begin
-- set one column to a length well beyond 16k but below 32k, max VARCHAR2 for PL/SQL
v_text := lpad('X', 16000, 'X')||' unemployed ' || lpad('X', 10000, 'X');
update emptest set text2 = v_text where ename = 'SMITH';
-- set others to short values
v_text := 'an unemployed salesman in text 1';
update emptest set text1 = v_text where ename = 'TURNER';
v_text := 'an unemployed manager in text 3';
update emptest set text3 = v_text where ename = 'JONES';
commit;
end;
/
declare
v_clob clob;
begin
-- Set a field to an absurdly long value, with the match value way beyond 32k.
update emptest set text1 = empty_clob() where ename = 'SMITH' returning text1 into v_clob;
for i in 1..10000 loop
dbms_lob.writeappend(v_clob, 36, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890');
end loop;
dbms_lob.writeappend(v_clob, 18, 'unemployed manager');
commit;
end;
/
select empno, ename, clob_name, clob_value, length(clob_value) clob_length
from emptest unpivot (clob_value for clob_name in (text1, text2, text3))
where clob_value like '%unemployed%'
/
结果将是:
EMPNO ENAME CLOB_NAME CLOB_VALUE CLOB_LENGTH
----- ------- --------- ----------- -----------
7566 JONES TEXT3 <excluded> 31
7369 SMITH TEXT1 <excluded> 360018
7369 SMITH TEXT2 <excluded> 26012
7844 TURNER TEXT1 <excluded> 32
重要的是 Oracle 在为 SMITH
处理 TEXT1
时如何处理 LIKE
关键字:请注意该列的长度超过 360k 个字符。
我们尝试使用 CLOB
数据类型的许多标准语法之所以有效,只是因为 Oracle 将 CLOB
强制转换为 VARCHAR2
,但它具有固有的长度限制。
如该测试所示,LIKE
比较确实适用于胖 CLOB
值——至少在我测试它的 Oracle 12c 中是这样。
如果您尝试显示匹配的实际内容,情况会有所不同:您需要熟悉 DBMS_LOB
包及其子程序,例如 DBMS_LOB.INSTR
和 DBMS_LOB.SUBSTR
如果你正在处理长 CLOB
值。
我有一个 table,它有 20 个相似的文本属性列,text1..text20。这些列的类型为 CLOB。我正在查找这些文本属性列之一包含特定短语(例如“%unemployed%”)的行。我需要知道两件事,哪些行匹配,哪些列匹配。我以为我可以使用 ANY 作为起点,但我遇到了问题。
似乎 ANY 语句不适用于“%”。例如,
select * from emp where 'BLAKE' = ANY(ename, job); -- Returns Data
但是
select * from emp where '%BLAKE%' = ANY(ename, job) -- No Data Found
执行此操作的正确方法是什么?伪代码将是...
Select name, addr,
which_column_matched(%unemployed%, text1..text20),
text1..text20
from table
where %unemployed% = ANY(text1..text20);
您可以使用子查询来识别匹配的第一列,然后 return 那:
select t.*
from (select t.*,
(case when text1 like '%unemployed%' then 'text1'
when text2 like '%unemployed%' then 'text2'
. . .
when text20 like '%unemployed%' then 'text20'
end) as col_match
from t
) t
where col_match is not null;
在 Oracle 中,您可以为此使用 unpivot
。它仍然需要您枚举所有列,但语法非常简洁。
如果您希望每个匹配的列都有一条记录:
select *
from emp unpivot (col for src in (text1, text2, text3))
where col like '%unemployed%'
如果您想要一个包含匹配列列表的附加列,您可以聚合结果集:
select ename, listagg(src, ', ')
from emp unpivot (col for src in (text1, text2, text3))
where col like '%unemployed%'
group by ename
我一直担心 Oracle 如何处理 CLOB
数据,所以这里的测试表明 Pivot 解决方案应该可以解决问题。
drop table emptest;
-- Assuming we are using the venerable EMP table
create table emptest as select * from emp;
alter table emptest add(
text1 CLOB,
text2 CLOB,
text3 CLOB
)
/
declare
v_text clob;
begin
-- set one column to a length well beyond 16k but below 32k, max VARCHAR2 for PL/SQL
v_text := lpad('X', 16000, 'X')||' unemployed ' || lpad('X', 10000, 'X');
update emptest set text2 = v_text where ename = 'SMITH';
-- set others to short values
v_text := 'an unemployed salesman in text 1';
update emptest set text1 = v_text where ename = 'TURNER';
v_text := 'an unemployed manager in text 3';
update emptest set text3 = v_text where ename = 'JONES';
commit;
end;
/
declare
v_clob clob;
begin
-- Set a field to an absurdly long value, with the match value way beyond 32k.
update emptest set text1 = empty_clob() where ename = 'SMITH' returning text1 into v_clob;
for i in 1..10000 loop
dbms_lob.writeappend(v_clob, 36, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890');
end loop;
dbms_lob.writeappend(v_clob, 18, 'unemployed manager');
commit;
end;
/
select empno, ename, clob_name, clob_value, length(clob_value) clob_length
from emptest unpivot (clob_value for clob_name in (text1, text2, text3))
where clob_value like '%unemployed%'
/
结果将是:
EMPNO ENAME CLOB_NAME CLOB_VALUE CLOB_LENGTH
----- ------- --------- ----------- -----------
7566 JONES TEXT3 <excluded> 31
7369 SMITH TEXT1 <excluded> 360018
7369 SMITH TEXT2 <excluded> 26012
7844 TURNER TEXT1 <excluded> 32
重要的是 Oracle 在为 SMITH
处理 TEXT1
时如何处理 LIKE
关键字:请注意该列的长度超过 360k 个字符。
我们尝试使用 CLOB
数据类型的许多标准语法之所以有效,只是因为 Oracle 将 CLOB
强制转换为 VARCHAR2
,但它具有固有的长度限制。
如该测试所示,LIKE
比较确实适用于胖 CLOB
值——至少在我测试它的 Oracle 12c 中是这样。
如果您尝试显示匹配的实际内容,情况会有所不同:您需要熟悉 DBMS_LOB
包及其子程序,例如 DBMS_LOB.INSTR
和 DBMS_LOB.SUBSTR
如果你正在处理长 CLOB
值。