【问题标题】:Comparing Substring Against Multiple Columns将子字符串与多列进行比较
【发布时间】:2020-07-10 19:11:51
【问题描述】:

我有一个表,其中包含 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);

【问题讨论】:

  • 如果多列匹配怎么办?
  • LIKE 通配符仅与 LIKE 一起使用。

标签: sql oracle sql-like contains unpivot


【解决方案1】:

我一直担心 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

在处理TEXT1SMITH 时,Oracle 如何处理LIKE 关键字非常重要:请注意,该列的长度大于360k 个字符。 我们尝试与CLOB 数据类型一起使用的大部分标准语法之所以有效,是因为Oracle 将CLOB 强制转换为VARCHAR2,但这具有固有的长度限制。

正如该测试所示,LIKE 比较确实适用于胖 CLOB 值——至少在我测试过的 Oracle 12c 中。

如果您尝试显示匹配的实际内容,情况会有所不同:您需要熟悉 DBMS_LOB 包及其子程序,例如 DBMS_LOB.INSTRDBMS_LOB.SUBSTR 如果您正在处理 long @ 987654335@ 值。

【讨论】:

    【解决方案2】:

    在 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
    

    【讨论】:

      【解决方案3】:

      您可以使用子查询来识别匹配的第一列,然后返回:

      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;
      

      【讨论】:

        猜你喜欢
        • 2017-02-20
        • 1970-01-01
        • 2021-08-27
        • 1970-01-01
        • 1970-01-01
        • 2012-08-31
        • 2021-11-20
        • 2012-10-06
        • 2019-01-22
        相关资源
        最近更新 更多