2008-08-21 6 views
4

Я написал код PL/SQL, чтобы денормализовать таблицу в форме с большей легкостью и запросом. Код использует временную таблицу для выполнения некоторой части своей работы, объединяя несколько строк из исходной таблицы.Лучший способ инкапсулировать сложную логику курсора Oracle PL/SQL в виде представления?

Логика написана как pipelined table function, следуя шаблону из связанной статьи. Функция table использует объявление PRAGMA AUTONOMOUS_TRANSACTION, чтобы разрешить временную манипуляцию таблицами, а также принимает входной параметр курсора, чтобы ограничить денормализацию определенными значениями ID.

Затем я создал представление для запроса функции таблицы, передавая все возможные значения ID в качестве курсора (другие функции использования будут более ограничительными).

Мой вопрос: это все действительно необходимо? Я полностью пропустил гораздо более простой способ сделать то же самое?

Каждый раз, когда я касаюсь PL/SQL, создается впечатление, что я слишком много печатаю.

Обновление: Я добавлю эскиз таблицы, с которой я имею дело, чтобы дать всем представление о денормализации, о которой я говорю. В таблице хранится история работ сотрудника, каждая из которых имеет строку активации и (возможно) строку завершения. Сотрудник может иметь несколько одновременных заданий, а также одну и ту же работу снова и снова в нескончаемых диапазонах дат. Например:

| EMP_ID | JOB_ID | STATUS | EFF_DATE | other columns... 
|  1 |  10 | A  | 10-JAN-2008 | 
|  2 |  11 | A  | 13-JAN-2008 | 
|  1 |  12 | A  | 20-JAN-2008 | 
|  2 |  11 | T  | 01-FEB-2008 | 
|  1 |  10 | T  | 02-FEB-2008 | 
|  2 |  11 | A  | 20-FEB-2008 | 

Выполнение запроса, чтобы выяснить, кто работает, когда в какой работе нетривиально. Таким образом, моя функция денормализации заполняет временную таблицу только диапазонами дат для каждого задания, для любого EMP_ID s, прошедшего через курсор. Переходя в EMP_ID s 1 и 2 будет производить следующее:

| EMP_ID | JOB_ID | START_DATE | END_DATE | 
|  1 |  10 | 10-JAN-2008 | 02-FEB-2008 | 
|  2 |  11 | 13-JAN-2008 | 01-FEB-2008 | 
|  1 |  12 | 20-JAN-2008 |    | 
|  2 |  11 | 20-FEB-2008 |    | 

(END_DATE позволяет NULL с для рабочих мест, которые не имеют заданную дату окончания.)

Как вы можете себе представить, это Денормализованная форма гораздо проще, но создавать его - насколько я могу судить, требуется временная таблица для хранения промежуточных результатов (например, записи о работе, для которых найдена строка активации, но не завершение ... все же). Используя функцию конвейерной таблицы, чтобы заполнить временную таблицу, а затем вернуть ее строки, это единственный способ выяснить, как это сделать.

ответ

4

Я думаю, что способ решения этой проблемы является использование аналитических функций ...

Я создал тестовый случай с помощью:

create table employee_job (
    emp_id integer, 
    job_id integer, 
    status varchar2(1 char), 
    eff_date date 
    ); 

insert into employee_job values (1,10,'A',to_date('10-JAN-2008','DD-MON-YYYY')); 
insert into employee_job values (2,11,'A',to_date('13-JAN-2008','DD-MON-YYYY')); 
insert into employee_job values (1,12,'A',to_date('20-JAN-2008','DD-MON-YYYY')); 
insert into employee_job values (2,11,'T',to_date('01-FEB-2008','DD-MON-YYYY')); 
insert into employee_job values (1,10,'T',to_date('02-FEB-2008','DD-MON-YYYY')); 
insert into employee_job values (2,11,'A',to_date('20-FEB-2008','DD-MON-YYYY')); 

commit; 

Я использовал привести функцию, чтобы получить следующую дату, а затем завернуть все это как подзапрос, чтобы получить записи «А» и добавить дату окончания, если таковая есть.

select 
    emp_id, 
    job_id, 
    eff_date start_date, 
    decode(next_status,'T',next_eff_date,null) end_date 
from 
    (
    select 
     emp_id, 
     job_id, 
     eff_date, 
     status, 
     lead(eff_date,1,null) over (partition by emp_id, job_id order by eff_date, status) next_eff_date, 
     lead(status,1,null) over (partition by emp_id, job_id order by eff_date, status) next_status 
    from 
     employee_job 
    ) 
where 
    status = 'A' 
order by 
    start_date, 
    emp_id, 
    job_id 

Я уверен, что есть некоторые варианты использования, которые я пропустил, но вы поняли эту идею.Аналитические функции ваш друг :)

EMP_ID JOB_ID  START_DATE  END_DATE    
    1  10  10-JAN-2008 02-FEB-2008   
    2  11  13-JAN-2008 01-FEB-2008   
    2  11  20-FEB-2008        
    1  12  20-JAN-2008        
+0

Очень круто. Я никогда не слышал об аналитических функциях, но я обязательно их проверю. Это выглядит намного проще, чем то, что я пробовал. – yukondude

+0

Оказалось, что все будет хорошо. Было осложнение того, что можно иметь несколько активаций для одного завершения, но, изменив строку декодирования на: decode (next_status, 'A', next_eff_date - 1, next_eff_date), проблема была решена красиво. Огромное спасибо. – yukondude

1

Вместо того, чтобы иметь входной параметр в качестве курсора, у меня бы была переменная таблицы (не знаю, имеет ли Oracle такую ​​вещь, как я - парень TSQL) или заполняю другую таблицу темп с идентификационными значениями и присоединяюсь к это в представлении/функции или где вам нужно.

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

0

Я не мог согласиться с вами больше, HollyStyles. Я также был парнем TSQL и нашел некоторые особенности Oracle более чем озадачивающими. К сожалению, временные таблицы в Oracle не так удобны, и в этом случае другая существующая логика SQL ожидает прямого запроса к таблице, поэтому я даю ей это представление. В этой системе нет никакой логики приложения, которая существует за пределами базы данных.

Разработчики Oracle, похоже, используют курсоры гораздо более охотно, чем я мог бы подумать.Учитывая рабство & дисциплина природы PL/SQL, это тем более удивительно.

0

Самое простое решение:

  1. Создать global temporary table содержащий только идентификаторы вам нужно:

    CREATE GLOBAL TEMPORARY TABLE tab_ids (ID INTEGER)
    ON COMMIT DELETE ROWS;

  2. Заполните временную таблицу идентификаторами, которые вам нужны.

  3. Использование EXISTS операции в вашей процедуре, чтобы выбрать строки, которые только в таблице идентификаторов:

    ВЫБРАТЬ yt.col1, yt.col2 ОТ your_table уг
    WHERE EXISTS (
    SELECT 'X' FROM tab_ids ти
    ГДЕ ti.id = yt.id
    )

Кроме того, можно передать через запятую строку идентификаторов в качестве параметра функции и разобрать его в таблицу. Это выполняется одним SELECT. Хотите узнать больше - спросите меня, как :-) Но это должен быть отдельный вопрос.

+0

Обратите внимание: вам нужно создать временную таблицу только один раз. –

+0

Временная таблица, о которой я упоминал, в основном состоит из идентификаторов (четыре столбца, составляющих первичный ключ исходной таблицы). Проблема в том, что некоторые из исходных строк должны быть объединены вместе, и поэтому это не так просто, как вы предлагаете. – yukondude

1

Похоже, что вы даете некоторую согласованность чтения здесь, то есть: будет возможно, чтобы содержимое вашей временной таблицы не синхронизировалось с исходными данными, если у вас есть одновременная модификация данных модификации.

Не зная требований или сложностей, которые вы хотите достичь. Я бы поставил

  1. , чтобы определить представление, содержащее (возможно, сложную) логику в SQL, иначе я бы добавил PL/SQL в микс;
  2. Конвейерная функция таблицы, но с использованием типа коллекции SQL (вместо временной таблицы). Простой пример: http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:4447489221109

Номер 2 предоставит вам более легкие детали и решит проблему согласованности.

Мэтью Батлер

+0

Спасибо за подсказку. Я не буду слишком беспокоиться о согласованности чтения, так как представление и его базовая функция таблицы предназначены только для отчетов, и совпадение между ними не является обязательным требованием. – yukondude

1

Реальная проблема здесь дизайн «только для записи» стол - я имею в виду, это просто вставить в нее данные, но сложно и неэффективно, чтобы получить полезную информацию из из этого! Ваша «временная» таблица имеет структуру, которую должна была иметь «постоянная» таблица.

Не могли бы вы, возможно, сделать это:

  • Создать постоянную таблицу с лучшей структурой
  • занесения его в соответствии с данными в первой таблице
  • Определение триггера базы данных исходной таблицы, чтобы сохранить новая таблица в настоящий момент

Затем вы можете просто выбрать из новой таблицы для выполнения своих отчетов.

+0

К сожалению, исходная таблица лежит в основе поставляемой поставщиком системы. Я сам подумал о триггерной идее, хотя ей пришлось бы ссылаться на исходную таблицу, и это вызывает другие проблемы. Вся система имеет «хрупкое» чувство к ней, поэтому я подумал, что лучше пойти с представлением – yukondude

 Смежные вопросы

  • Нет связанных вопросов^_^