суббота, 19 марта 2011 г.

Поиск по всем таблицам в Oracle

Задача: полнотекстовый поиск в Oracle
То есть, мы знаем значение какой-то колонки какой-то таблицы. И ничего больше не знаем, ни названия таблицы, ни названия колонки. Такое может быть нужным, когда надо определить, в какую таблицу БД попадают определённые данные с веб-формы. Вызвано это, например, может быть тем, что у нас есть доступ к БД, но нет исходников приложения. А таблицы кто-то называл в стиле TABLE_1, TABLE_2, OT_1 и так далее.
К счастью, БД была Oracle, поэтому можно использовать тёмные стороны PL/SQL.
Думаю, смысл действа понятен, поэтому сразу к коду:

create or replace procedure whereIsValue(valueParam varchar2)
AS
  TYPE VALCUR IS REF CURSOR;
  cursor tabl is select table_name from user_tables;
  cursor col (tablename varchar2) is select column_name 
                                     from user_tab_columns
                                     where table_name like tableName;
  valueCursor VALCUR;
  tableName varchar2(50);
  columnName varchar2(50);
  columnValue varchar2(500);
  qq number(3);
begin
  open tabl;
  LOOP
    fetch tabl into tableName;
    EXIT WHEN tabl%NOTFOUND;
    OPEN col(tableName);
    LOOP     
      fetch col into columnName;
      EXIT WHEN col%NOTFOUND;
        OPEN valueCursor for 'select ' || columnName 
                         || ' from ' || tableName;
        LOOP
          BEGIN
            fetch valueCursor into columnValue;        
            EXIT WHEN valueCursor%NOTFOUND;
            if (columnValue like valueParam) then
              dbms_output.put_line(tableName);
              exit;
            end if;
          EXCEPTION
            WHEN OTHERS then
              qq := 4;
          END;
        END LOOP;
        CLOSE valueCursor;
    END LOOP;
    CLOSE col;
  END LOOP;

end;

Вкратце о том, что же происходит:

  • проходим по всем таблицам схемы;
  • для каждой таблицы проходим по всем её колонкам;
  • для каждой колонки забираем список значений и сравниваем с тем, что передано в параметре процедуры;
  • если совпало, то печатаем имя таблицы в DBMS_OUTPUT;

3 комментария:

  1. Primo. В языках, нечувствительных к регистру, к коим относится PL/SQL, camel-нотация не очень уместна. Более подходящим является стиль where_is_value и тд.

    Secundo. Использовать вложенный цикл там, где можно обойтись одним запросом, неграмотно с точки зрения взаимодействия декларативной и императивной парадигм - по сути ты навязываешь оптимизатору определенный план, лишая возможности сделать более осознанный выбор

    Tertio. Для цикла по курсору есть специальная конструкция
    for i in
    (select...)
    loop
    -- тело цикла
    end loop;
    Без всяких open и fetch.
    И проще написать, и выглядит нагляднее.

    ОтветитьУдалить
  2. Да, и еще придерусь - во фрагменте "where table_name like tableName" нет никакой нужды использовать конструкцию like. У нас нет литералов '%', поэтому like здесь работает в точности как простое сравнение "=".

    Разница лишь в том, что оператор сравнения более нагляден для разработчика.

    ОтветитьУдалить
  3. 1. К яве привык, поэтому так и написал. В целом согласен.
    2. Мне была нужна функция, при этом достаточно срочно. Написать решился, когда за 5 минут гугления не нашёл. Поэтому есть кривота, но я допускал это. Для того комменты и открыты, чтобы предлагать улучшения
    3. Опять же, моя привычка:)
    4. Индивидуально слишком. С точки зрения перфоманса это абсолютно одно и то же, но если я вижу like, то сразу понимаю, что сравниваются строки. так что в этом не соглашусь.

    ОтветитьУдалить