Задача: полнотекстовый поиск в 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;
Primo. В языках, нечувствительных к регистру, к коим относится PL/SQL, camel-нотация не очень уместна. Более подходящим является стиль where_is_value и тд.
ОтветитьУдалитьSecundo. Использовать вложенный цикл там, где можно обойтись одним запросом, неграмотно с точки зрения взаимодействия декларативной и императивной парадигм - по сути ты навязываешь оптимизатору определенный план, лишая возможности сделать более осознанный выбор
Tertio. Для цикла по курсору есть специальная конструкция
for i in
(select...)
loop
-- тело цикла
end loop;
Без всяких open и fetch.
И проще написать, и выглядит нагляднее.
Да, и еще придерусь - во фрагменте "where table_name like tableName" нет никакой нужды использовать конструкцию like. У нас нет литералов '%', поэтому like здесь работает в точности как простое сравнение "=".
ОтветитьУдалитьРазница лишь в том, что оператор сравнения более нагляден для разработчика.
1. К яве привык, поэтому так и написал. В целом согласен.
ОтветитьУдалить2. Мне была нужна функция, при этом достаточно срочно. Написать решился, когда за 5 минут гугления не нашёл. Поэтому есть кривота, но я допускал это. Для того комменты и открыты, чтобы предлагать улучшения
3. Опять же, моя привычка:)
4. Индивидуально слишком. С точки зрения перфоманса это абсолютно одно и то же, но если я вижу like, то сразу понимаю, что сравниваются строки. так что в этом не соглашусь.