Сведения о вопросе

Gaukhar

17:33, 23rd August, 2020

Теги

sql   oracle   plsql    

Лучший способ инкапсулировать сложную логику курсора Oracle PL/SQL в виде представления?

Просмотров: 578   Ответов: 6

Я написал код PL/SQL для денормализации таблицы в форму much-easer-to-query. Код использует временную таблицу для выполнения некоторой части своей работы, объединяя некоторые строки из исходной таблицы вместе.

Логика записывается как конвейерная табличная функция, следуя шаблону из связанной статьи. Табличная функция использует объявление 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 с 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 s для заданий,которые не имеют заранее определенной даты окончания.)

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



  Сведения об ответе

FAriza

05:02, 17th August, 2020

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

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

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;

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

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                              


  Сведения об ответе

park

11:01, 5th August, 2020

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

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


  Сведения об ответе

#hash

16:53, 28th August, 2020

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

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

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

Номер 2 даст вам меньше движущихся частей и решит вашу проблему согласованности.

Мэтью Батлер


  Сведения об ответе

DAAA

12:57, 1st August, 2020

Настоящая проблема здесь - это дизайн таблицы "write-only", под которым я подразумеваю, что в нее легко вставить данные, но сложно и неэффективно получить полезную информацию из нее! Ваша таблица "temporary" имеет структуру, которую должна была иметь таблица "permanent" в первую очередь.

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

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

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


  Сведения об ответе

COOL

22:40, 3rd August, 2020

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

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


  Сведения об ответе

crush

02:44, 13th August, 2020

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

  1. Создайте глобальную временную таблицу , содержащую только IDs, который вам нужен:

    CREATE GLOBAL TEMPORARY TABLE tab_ids (id INTEGER)  
    ON COMMIT DELETE ROWS;
    
  2. Заполните временную таблицу нужным вам параметром IDs.

  3. Используйте операцию EXISTS в своей процедуре для выбора строк, которые находятся только в таблице IDs:

      SELECT yt.col1, yt.col2 FROM your\_table yt  
       WHERE EXISTS (  
          SELECT 'X' FROM tab_ids ti  
           WHERE ti.id = yt.id  
       )
    

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


Ответить на вопрос

Чтобы ответить на вопрос вам нужно войти в систему или зарегистрироваться