Сравнение подстроки с несколькими столбцами - PullRequest
1 голос
/ 30 марта 2020

У меня есть таблица с 20 аналогичными столбцами текстовых атрибутов, text1..text20. Эти столбцы имеют тип CLOB. Я ищу, чтобы найти строки, где один из этих столбцов текстовых атрибутов содержит конкретную фразу c, например, "% безработных%". Мне нужно знать 2 вещи, какие строки соответствуют, и какой столбец был сопоставлен. Я думал, что могу использовать ЛЮБОЙ в качестве отправной точки, но у меня есть проблемы.

Похоже, что ЛЮБОЙ оператор не работает с "%". Например,

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);

Ответы [ 3 ]

1 голос
/ 30 марта 2020

В 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
0 голосов
/ 30 марта 2020

Я всегда беспокоюсь о том, как 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 обрабатывает ключевое слово LIKE при работе с TEXT1 для SMITH: обратите внимание, что длина столбца> 360 тыс. Символов. Большая часть стандартного синтаксиса, который мы пытаемся использовать с CLOB типами данных, работает только потому, что Oracle приводит CLOB к VARCHAR2, но имеет внутренние ограничения длины.

Как показывает этот тест, LIKE Сравнение работает для жирных CLOB значений - по крайней мере, в Oracle 12 c, где я его тестировал.

Все будет несколько иначе, если вы попытаетесь отобразить фактический контент, который соответствует: вам необходимо ознакомиться с пакетом DBMS_LOB и его подпрограммами, такими как DBMS_LOB.INSTR и DBMS_LOB.SUBSTR, если вы имеете дело с длинными значениями CLOB.

0 голосов
/ 30 марта 2020

Вы можете использовать подзапрос, чтобы определить первый соответствующий столбец, а затем вернуть его:

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;
...