2016-12-21 5 views
3

PostgreSQL의 9.5CTE에서 JOIN 작업을 주문할 수 있습니까? CTE 아래 (PostgreSQL을)

는 DOCTOR_PROCEDURES에 테이블 CPT에서 기록과 CPT_INVOICE를 이동하고 DOCTORBILLING 그에 따라 UID 업데이트 할 올바르게 작동합니다. 그러나 CPT_INVOICE는 상위 CPT에 대한 외래 키를 가지므로이 외래 키 관계가 제거 될 때까지이 스크립트는 실패합니다.

PostgreSQL에서 CTE를 특정 순서로 실행하도록하는 방법이 있습니까? 즉, 먼저 planB보다 먼저 planC를 실행 하시겠습니까? 당신이 무거운 순서 종속성을 당겨 경우

WITH planA AS (
    select cpt_recid from doctorbilling 
), 
planC as (
    delete from cpt_invoice D 
    USING planA a 
    where D.recid = A.cpt_recid 
    returning D.cpt_recid, D.ninsurance, D.ncash, D.mustschedule, D.doneinoffice 
), 
planB as (
    delete from cpt C 
    USING planA A 
    where C.recid = A.cpt_recid 
    returning C.recid as cpt_recid, C.code, C.cdesc, C.procedure_type, C.sex 
), 
planD as (
    insert into doctor_procedures (code, cdesc, procedure_type, sex, ninsurance, ncash, mustschedule, doneinoffice, cpt_recid) 
    select distinct on (b.cdesc) b.code, b.cdesc, b.procedure_type, b.sex, c.ninsurance, c.ncash, c.mustschedule, c.doneinoffice, a.cpt_recid 
    from planA A 
    join planB B on B.cpt_recid = A.cpt_recid 
    left join planC C on C.cpt_recid = A.cpt_recid -- there may not be a cpt_invoice for the cpt_recid. 
    order by b.cdesc 
    returning cpt_recid, uid 
) 
update doctorbilling T 
set uid = D.uid 
from planD D 
where T.cpt_recid = D.cpt_recid 

답변

1

가장 쉬운 해결책은 (플랜 A가 제거로) 계획 C에 플랜 B 의존하게 될 것이다 : 당신이 주로 비정규 데이터 수정 문을 수행하기 때문에

WITH planC as (
    delete from cpt_invoice D 
    USING doctorbilling A 
    where D.recid = A.cpt_recid 
    returning D.recid, D.cpt_recid, D.ninsurance, D.ncash, D.mustschedule, D.doneinoffice 
), 
planB as (
    delete from cpt C 
    USING planC X 
    where C.recid = X.recid 
    returning C.recid as cpt_recid, C.code, C.cdesc, C.procedure_type, C.sex 
), 
planD as (
    insert into doctor_procedures (code, cdesc, procedure_type, sex, ninsurance, ncash, mustschedule, doneinoffice, cpt_recid) 
    select distinct on (b.cdesc) b.code, b.cdesc, b.procedure_type, b.sex, c.ninsurance, c.ncash, c.mustschedule, c.doneinoffice, b.cpt_recid 
    from planB B 
    left join planC C on C.cpt_recid = B.cpt_recid -- there may not be a cpt_invoice for the cpt_recid. 
    order by b.cdesc 
    returning cpt_recid, uid 
) 
update doctorbilling T 
set uid = D.uid 
from planD D 
where T.cpt_recid = D.cpt_recid; 

이 모두는하지만, 조금 이상한 보인다 표 doctorbilling의 모든 행 실제로, 당신은 확실히 좀 더 간단한 쿼리를 만들 것이다, 한 번에 cpt_recid 하나의 이동 가능성이 더 높습니다 :

WITH planC as (
    delete from cpt_invoice D 
    where D.recid = <<cpt_recid>> 
    returning D.recid, D.cpt_recid, D.ninsurance, D.ncash, D.mustschedule, D.doneinoffice 
), 
planB as (
    delete from cpt C 
    USING planC X 
    where C.recid = X.recid -- maintain dependency 
    returning C.code, C.cdesc, C.procedure_type, C.sex 
), 
planD as (
    insert into doctor_procedures (code, cdesc, procedure_type, sex, ninsurance, ncash, mustschedule, doneinoffice, cpt_recid) 
    select distinct on (b.cdesc) b.code, b.cdesc, b.procedure_type, b.sex, c.ninsurance, c.ncash, c.mustschedule, c.doneinoffice, b.cpt_recid 
    from planB B 
    left join planC C on true -- there may not be a cpt_invoice for the cpt_recid. 
    order by b.cdesc 
    returning uid 
) 
update doctorbilling T 
set uid = D.uid 
from planD D 
where T.cpt_recid = <<cpt_recid>>; 

PL/pgSQL의 기능이 될 것입니다 더 나은 :

CREATE FUNCTION move_recid (id integer) RETURNS void AS $$ 
DECLARE 
    ... -- declare all variables 
BEGIN 
    delete from cpt_invoice 
    where recid = id 
    returning cpt_recid, ninsurance, ncash, mustschedule, doneinoffice 
     into inv_recid, inv_ins, inv_cash, inv_sch, inv_doi; 

    delete from cpt 
    where recid = id 
    returning code, cdesc, procedure_type, sex 
     into cpt_code, cpt_desc, cpt_proc, cpt_sex; 

    insert into doctor_procedures (code, cdesc, procedure_type, sex, ninsurance, ncash, 
            mustschedule, doneinoffice, cpt_recid) 
    values (cpt_code, cpt_desc, ...) 
    returning uid into dp_uid; 

    update doctorbilling 
    set uid = dp_uid 
    where cpt_recid = id; 
END; 
$$ LANGUAGE plpgsql STRICT; 

주문 보증. 동료 프로그래머가 쉽게 이해할 수 있으며 유지 보수가 쉽습니다.

+0

좋은 물건! 나는 plpgsql을 사용하지 않을 것이라고 생각했다. 감사. –

+0

그냥 참고로. 위의 CTE를 사용하여 하나의 레코드 만 마이그레이션하는 것이 아니라 전체 테이블을 마이그레이션합니다. –

1

먼저

TIA는 일반적으로 다른 방법으로이 문제를 해결하는 것이 더 낫다. SQL은 선언적 언어이며 주문에 대한 개념이 없으므로 여기서 수행하는 모든 작업은 표준 예상 동작이 아닌 구현 세부 사항에 의존합니다. 가장 확실한 방법은 로직을보다 명확하게 분해하여 사용자 정의 함수를 래핑하는 것입니다. 다른 방법으로 외래 키 제약 조건 DEFERRABLE을 표시하고이 쿼리를 실행하기 직전에 DEFERRED으로 설정 한 다음 쿼리 후 IMMEDIATE으로 설정할 수 있습니다. 그것들은 당신의 문제를 해결하는 최선의 선택과 올바른 방법이 될 것입니다.

원하는 특정 솔루션을 사용하십시오. 여기에서의 문제는 일반적으로 CTE를 주문할 필요가 없지만 일반적으로 가입 작업을 주문해야하는 필요성 때문입니다. I 다음 특정 상황에서는 안전 할 수 있지만 완전히 확신 할 수는 없습니다 (예 : 더 똑똑한 계획자가 나중에 문제를 일으킬 수 있음).

planD as (
    insert into doctor_procedures (code, cdesc, procedure_type, sex, ninsurance, ncash, mustschedule, doneinoffice, cpt_recid) 
    select distinct on (b.cdesc) b.code, b.cdesc, b.procedure_type, b.sex, c.ninsurance, c.ncash, c.mustschedule, c.doneinoffice, a.cpt_recid 
    from planA A 
    left join planC C on C.cpt_recid = A.cpt_recid -- there may not be a cpt_invoice for the cpt_recid. 
    join planB B on B.cpt_recid = A.cpt_recid OR B.cpt_recid = c.cpt_recid 
    order by b.cdesc 
    returning cpt_recid, uid 
) 

나는 이것이 장기적인 솔루션 인에서 완전히 확신하지 않다 이유는 스마트 가상 대패주의 할 수있을 것입니다 그 c.cpt_recid는 항상 a.cpt_recid 동일하므로 OR 절은 항상 중복됩니다.

+1

와우! 이 오랜 시간이 지난 후에도, 나는 "선언적"언어의 개념을 완전히 놓쳤습니다. 그것은 저에게 CTE 개념을 정말로 끌어들입니다. 감사. –