복잡한 Oracle PL/SQL 커서 논리를 뷰로 캡슐화하는 가장 좋은 방법은 무엇입니까?

StackOverflow https://stackoverflow.com/questions/20081

  •  09-06-2019
  •  | 
  •  

문제

나는 테이블을 쿼리하기 훨씬 쉬운 형식으로 비정규화하기 위해 PL/SQL 코드를 작성했습니다.코드는 임시 테이블을 사용하여 일부 작업을 수행하고 원본 테이블의 일부 행을 병합합니다.

논리는 다음과 같이 작성됩니다. 파이프라인 테이블 함수, 링크된 기사의 패턴을 따릅니다.테이블 함수는 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_IDs는 커서를 통해 전달되었습니다.통과 EMP_IDs 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미리 정해진 종료 날짜가 없는 작업의 경우입니다.)

상상할 수 있듯이 이 비정규화된 형식은 쿼리하기가 훨씬 쉽지만, 내가 아는 한 이를 생성하려면 중간 결과(예: 활성화 행이 지정된 작업 레코드)를 저장하기 위한 임시 테이블이 필요합니다. 찾았지만 종료되지는 않았습니다...아직).파이프라인 테이블 함수를 사용하여 임시 테이블을 채운 다음 해당 행을 반환하는 것이 제가 알아낸 유일한 방법입니다.

도움이 되었습니까?

해결책

이에 접근하는 방법은 분석 기능을 사용하는 것이라고 생각합니다.

다음을 사용하여 테스트 사례를 설정했습니다.

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;

나는 선두 다음 날짜를 가져온 다음 "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                              

다른 팁

입력 매개변수를 커서로 사용하는 대신 테이블 변수를 사용하거나(Oracle에 그런 것이 있는지는 모르겠습니다. 저는 TSQL 전문가입니다) ID 값으로 다른 임시 테이블을 채우고 뷰에 조인합니다. /function 또는 필요한 곳 ​​어디에서나 사용할 수 있습니다.

내 솔직한 의견으로는 커서가 필요한 유일한 시간은 가지다 루프.그리고 루프를 수행해야 하는 경우 항상 애플리케이션 로직의 데이터베이스 외부에서 루프를 수행하는 것이 좋습니다.

여기서 읽기 일관성을 포기하는 것 같습니다.동시 수정 데이터 수정이 있는 경우 임시 테이블의 내용이 원본 데이터와 동기화되지 않을 수 있습니다.

요구 사항이나 달성하려는 목표의 복잡성을 알지 못한 채.나는 시도 할 것이다

  1. SQL에 (아마도 복잡한) 논리를 포함하는 뷰를 정의하려면 다음과 같이 혼합하여 일부 PL/SQL을 추가합니다.
  2. 파이프라인 테이블 함수이지만 SQL 컬렉션 유형을 사용합니다(임시 테이블 대신).간단한 예는 다음과 같습니다. http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:4447489221109

2번은 움직이는 부분을 줄이고 일관성 문제를 해결합니다.

매튜 버틀러

여기서 진짜 문제는 "쓰기 전용" 테이블 디자인입니다. 즉, 테이블에 데이터를 삽입하기는 쉽지만 유용한 정보를 얻는 것은 까다롭고 비효율적입니다!"임시" 테이블은 "영구" 테이블이 처음에 가졌어야 했던 구조를 가지고 있습니다.

아마도 이렇게 할 수 있습니까?

  • 더 나은 구조로 영구 테이블 만들기
  • 첫 번째 테이블의 데이터와 일치하도록 채웁니다.
  • 지금부터 새 테이블의 동기화를 유지하려면 원본 테이블에 데이터베이스 트리거를 정의하세요.

그런 다음 새 테이블에서 선택하여 보고를 수행할 수 있습니다.

나는 당신의 의견에 더 이상 동의할 수 없습니다, HollyStyles.나는 또한 TSQL 전문가였으며 Oracle의 특이한 점 중 일부가 약간 당혹 스럽다는 것을 알았습니다.불행히도 임시 테이블은 Oracle에서 그다지 편리하지 않으며 이 경우 기존의 다른 SQL 논리가 테이블을 직접 쿼리할 것으로 예상하므로 대신 이 뷰를 제공합니다.이 시스템에는 데이터베이스 외부에 존재하는 애플리케이션 로직이 실제로 없습니다.

Oracle 개발자들은 제가 생각했던 것보다 훨씬 더 적극적으로 커서를 사용하는 것 같습니다.PL/SQL의 속박 및 규율 특성을 고려하면 이는 더욱 놀라운 일입니다.

가장 간단한 해결책은 다음과 같습니다.

  1. 만들기 전역 임시 테이블 필요한 ID만 포함되어 있습니다.

    CREATE GLOBAL TEMPORARY TABLE tab_ids (id INTEGER)  
    ON COMMIT DELETE ROWS;
    
  2. 필요한 ID로 임시 테이블을 채웁니다.

  3. 프로시저에서 EXISTS 작업을 사용하여 ID 테이블에만 있는 행을 선택합니다.

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

쉼표로 구분된 ID 문자열을 함수 매개변수로 전달하고 이를 테이블로 구문 분석할 수도 있습니다.이는 단일 SELECT에 의해 수행됩니다.더 알고 싶으시면 - 방법을 물어보세요 :-) 그러나 그것은 별개의 질문이 되어야 합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top