2013-07-06 2 views
2

다음 요구 사항이 있습니다.Oracle sql 쿼리를 최적화해야합니다.

특정 날짜 (당일) 및 특정 상점 (예 : 12)에 대한 스탭의 채용 일정을 가져오고 싶습니다. 또, 다른 가게 (13) 직원이 와서 가게 (12)에 가게되고, 가게 12 직원이 다른 가게에서 일할 수있는 시나리오가 있습니다.

직원이 출석을 표시하는 경우 상태 테이블에 현재/부재 세부 정보가 저장됩니다.

나는 을 가져오고 싶습니다. 1) staffstatus에 상관없이 staffschedules에서 상점 12에 대한 스태프의 일정은 특정 날짜에 항목이 있거나 없는지 확인하십시오. 항목이 상태 테이블에있는 경우

2) 다른 모든 상점에서이고 staffstatus를 참조하여 다른 매장에 가서 직원의 일정을 거부 staffschedules에서 스케줄 (, 나는

), 검색 할 아무것도 필요하지 않습니다

2 테이블 : 나는 아래 쓴

  1. 일정
  2. 상태 ----> 직원/현재 결석 세부 사항 질문. 평균 30 초가 걸립니다. 최악의 경우, 93 초.

    STAFFSCHEUDLES TABLE SCHEMA: 
    
    CREATE TABLE "STAFFSCHEDULES" 
    (
    "STORE_ID" VARCHAR2(75 BYTE) NOT NULL ENABLE, 
    "START_DATE" DATE NOT NULL ENABLE, 
    "JOB_ID"    VARCHAR2(22 BYTE) NOT NULL ENABLE, 
    "START_TIME" VARCHAR2(5 BYTE) NOT NULL ENABLE, 
    "END_DATE" DATE NOT NULL ENABLE, 
    "END_TIME" VARCHAR2(5 BYTE) NOT NULL ENABLE, 
    "JOBNAME"  VARCHAR2(1500 BYTE) 
    CONSTRAINT "PK_STAFFSCHEDULES" PRIMARY KEY ("STORE_ID", "START_DATE", "JOB_ID", "START_TIME") 
    
    CREATE UNIQUE INDEX "PK_STAFFSCHEDULES" ON "STAFFSCHEDULES" 
    (
    "STORE_ID", "START_DATE", "JOB_ID", "START_TIME" 
    ) 
    
    CREATE INDEX "IDX1_STAFFSCHEDULES_STORSTDT" ON "STAFFSCHEDULES" 
    (
        "STORE_ID", 
        "START_DATE" 
        ) 
    
    CREATE INDEX "STAFFSCHEDULES_IDX" ON "STAFFSCHEDULES" 
        (
        "JOB_ID" 
    ) 
    
    
    STAFFSTATUS TABLE SCHEMA: 
    
    
    CREATE TABLE "STAFFSTATUS" 
    (
    "JOB_SEQ_ID" NUMBER(10,0) NOT NULL ENABLE, 
    "JOB_ID"  VARCHAR2(15 BYTE) NOT NULL ENABLE, 
    "STORE_ID" VARCHAR2(4 BYTE) NOT NULL ENABLE, 
    "JOB_DATE" DATE NOT NULL ENABLE, 
    "STATUS"  VARCHAR2(1 BYTE) DEFAULT 'N' , 
    "SERVER_DATE" DATE 
    CONSTRAINT "PK_STAFFSTATUS" PRIMARY KEY ("JOB_SEQ_ID") 
    CONSTRAINT "UK_STAFFSTATUS" UNIQUE ("JOB_ID", "STORE_ID", "JOB_DATE") 
    ) 
    
    CREATE INDEX "INDEX_STAFFSTATUS" ON "STAFFSTATUS" 
    (
    "STORE_ID", 
    "STATUS" 
    ) 
    
    CREATE INDEX "INDEX_STAFFSTATUS_JOB_DT" ON "STAFFSTATUS" 
    (
    "STORE_ID", 
    "JOB_DATE", 
    "STATUS" 
    ) 
    
    CREATE UNIQUE INDEX "PK_STAFFSTATUS" ON "STAFFSTATUS" 
    (
    "JOB_SEQ_ID" 
    ) 
    
    CREATE UNIQUE INDEX "UK_STAFFSTATUS" ON "STAFFSTATUS" 
    (
    "JOB_ID", "STORE_ID", "JOB_DATE" 
    ) 
    
    CREATE INDEX "INDEX_STAFFSTATUS_UPDT" ON "STAFFSTATUS" 
        (
        "STORE_ID", 
        "SERVER_DATE" 
        ) 
    

    쿼리는 일정을 검색하려면 다음

      SELECT *      
           From StaffSchedules 
           WHERE store_id='15'     
           AND ((start_date BETWEEN TRUNC(to_date('07/08/2013','MM/DD/YYYY')) AND TRUNC(to_date('07/08/2013','MM/DD/YYYY'))+(1-1/24/60/60) 
            AND TO_CHAR(start_date,'HH24:MI') <= start_time) 
            OR((end_date BETWEEN TRUNC(to_date('07/08/2013','MM/DD/YYYY'))+1/24/60/60 AND TRUNC(to_date('07/08/2013','MM/DD/YYYY'))+1) 
            AND TO_CHAR(end_date,'HH24:MI') >= end_time)) 
    
           AND job_id NOT IN 
           (SELECT distinct s2.job_id 
           FROM staffschedules s2 
           WHERE s2.store_id=store_id     
           AND ((s2.start_date BETWEEN TRUNC(to_date('07/08/2013','MM/DD/YYYY')) AND TRUNC(to_date('07/08/2013','MM/DD/YYYY'))+(1-1/24/60/60) 
            AND TO_CHAR(s2.start_date,'HH24:MI') <= s2.start_time) 
            OR((s2.end_date BETWEEN TRUNC(to_date('07/08/2013','MM/DD/YYYY'))+1/24/60/60 AND TRUNC(to_date('07/08/2013','MM/DD/YYYY'))+1) 
            AND TO_CHAR(s2.end_date,'HH24:MI') >= s2.end_time)) 
           AND SUBSTR(ses2.org_path,INSTR(ses2.org_path,'/',1,7)+1,8) = 'NOTALLOCATED') 
    
           AND job_id NOT IN (
    
             Select job_Id From staffStatus Where Trunc(job_Date)=Trunc(to_date('07/08/2013','MM/DD/YYYY')) And Store_Id!='15')   
    
    
         UNION ALL 
    
           SELECT *      
            From StaffSchedules ss 
            Right Outer Join staffStatus status On Es.job_Id=status.job_Id And status.Store_Id<>ss.Store_Id And 
             (Trunc(status.job_Date)=Trunc(ss.Start_Date) Or Trunc(status.job_Date)=Trunc(ss.End_Date)) 
    
            AND ((start_date BETWEEN TRUNC(to_date('07/08/2013','MM/DD/YYYY')) AND TRUNC(to_date('07/08/2013','MM/DD/YYYY'))+(1-1/24/60/60) 
            AND TO_CHAR(start_date,'HH24:MI') <= start_time) 
            OR((end_date BETWEEN TRUNC(to_date('07/08/2013','MM/DD/YYYY'))+1/24/60/60 AND TRUNC(to_date('07/08/2013','MM/DD/YYYY'))+1) 
            AND TO_CHAR(end_date,'HH24:MI') >= end_time)) 
    
           AND job_id NOT IN 
           (SELECT distinct s2.job_id 
            FROM staffschedules s2 
            WHERE s2.store_id=store_id     
            AND ((s2.start_date BETWEEN TRUNC(to_date('07/08/2013','MM/DD/YYYY')) AND TRUNC(to_date('07/08/2013','MM/DD/YYYY'))+(1-1/24/60/60) 
            AND TO_CHAR(s2.start_date,'HH24:MI') <= s2.start_time) 
            OR((s2.end_date BETWEEN TRUNC(to_date('07/08/2013','MM/DD/YYYY'))+1/24/60/60 AND TRUNC(to_date('07/08/2013','MM/DD/YYYY'))+1) 
            AND TO_CHAR(s2.end_date,'HH24:MI') >= s2.end_time)) 
           AND SUBSTR(ses2.org_path,INSTR(ses2.org_path,'/',1,7)+1,8) = 'NOTALLOCATED') 
    
    
           AND status.store_id='15' AND TRUNC(status.job_date)=TRUNC(to_date('07/08/2013','MM/DD/YYYY'))    
           ORDER BY job_id,start_date,start_time;     
    

    실행 계획을 :

    ----------------------------------------------------------------------------------------------------- ------------- 
    | Id | Operation      | Name       | Rows | Bytes | Cost (%CPU)| Time  | 
    ----------------------------------------------------------------------------------------------------- ------------- 
    | 0 | SELECT STATEMENT    |        | 41 | 10865 |  37990 (1)| 00:07:36 | 
    | 1 | SORT ORDER BY     |        | 41 | 10865 | 37989 (67)| 00:07:36 | 
    | 2 | UNION-ALL      |        |  |   |   |   | 
    | 3 | NESTED LOOPS ANTI   |        |  1 | 265 | 12649 (1)| 00:02:32 | 
    | 4 |  NESTED LOOPS ANTI   |        |  1 | 146 | 12620 (1)| 00:02:32 | 
    |* 5 |  TABLE ACCESS BY INDEX ROWID| STAFFSCHEDULES     | 109 | 13734 | 12401 (1)| 00:02:29 | 
    |* 6 |  INDEX RANGE SCAN   | IDX1_STAFFSCHEDULES_STORSTDT | 65068 |  | 410 (1)| 00:00:05 | 
    |* 7 |  INDEX RANGE SCAN   | UK_STAFFSTATUS     | 137K| 2694K|  2 (0)| 00:00:01 | 
    |* 8 |  TABLE ACCESS BY INDEX ROWID | STAFFSCHEDULES     |  1 | 119 | 29 (0)| 00:00:01 | 
    |* 9 |  INDEX RANGE SCAN   | STAFFSCHEDULES_IDX    | 83 |  |  2 (0)| 00:00:01 | 
    | 10 | NESTED LOOPS ANTI   |        | 40 | 10600 | 25340 (1)| 00:05:05 | 
    | 11 |  NESTED LOOPS    |        | 40 | 5840 | 24820 (1)| 00:04:58 | 
    | 12 |  TABLE ACCESS BY INDEX ROWID| STAFFSTATUS     | 2208 | 44160 | 2931 (1)| 00:00:36 | 
    |* 13 |  INDEX RANGE SCAN   | INDEX_STAFFSTATUS_SCHD_DT  | 2208 |  | 1525 (1)| 00:00:19 | 
    |* 14 |  TABLE ACCESS BY INDEX ROWID| STAFFSCHEDULES     |  1 | 126 | 29 (0)| 00:00:01 | 
    |* 15 |  INDEX RANGE SCAN   | STAFFSCHEDULES_IDX    | 83 |  |  2 (0)| 00:00:01 | 
    |* 16 |  TABLE ACCESS BY INDEX ROWID | STAFFSCHEDULES     |  1 | 119 | 13 (0)| 00:00:01 | 
    |* 17 |  INDEX RANGE SCAN   | PK_STAFFSCHEDULES    |  1 |  | 12 (0)| 00:00:01 | 
    ----------------------------------------------------------------------------------------------------- ------------- 
    
    Predicate Information (identified by operation id): 
    --------------------------------------------------- 
    
        5 - filter(("START_DATE"<=TO_DATE(' 2013-07-08 23:59:59', 'syyyy-mm-dd hh24:mi:ss') AND 
          "START_DATE">=TO_DATE(' 2013-07-08 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND 
          "START_TIME">=TO_CHAR(INTERNAL_FUNCTION(START_DATE"),'HH24:MI') OR 
          "END_DATE"<=TO_DATE(' 2013-07-09 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND 
          "END_DATE">=TO_DATE(' 2013-07-08 00:00:01', 'syyyy-mm-dd hh24:mi:ss') AND 
          "END_TIME"<=TO_CHAR(INTERNAL_FUNCTION("END_DATE"),'HH24:MI'))) 
    
        6 - access("STORE_ID"='15') 
        7 - access("JOB_ID"="JOB_ID") 
         filter(TRUNC(INTERNAL_FUNCTION("JOB_DATE"))=TO_DATE(' 2013-07-08 00:00:00', 'syyyy-mm-dd 
          hh24:mi:ss') AND "STORE_ID"<>'15') 
        8 - filter("S2"."STORE_ID"='15' AND ("S2"."START_DATE"<=TO_DATE(' 2013-07-08 23:59:59', 
          'syyyy-mm-dd hh24:mi:ss') AND "S2"."START_DATE">=TO_DATE(' 2013-07-08 00:00:00',  'syyyy-mm-dd 
          hh24:mi:ss') AND "S2"."START_TIME">=TO_CHAR(INTERNAL_FUNCTION("S2"."START_DATE"),'HH24:MI' 
         ) OR "S2"."END_DATE"<=TO_DATE(' 2013-07-09 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND 
          "S2"."END_DATE">=TO_DATE(' 2013-07-08 00:00:01', 'syyyy-mm-dd hh24:mi:ss') AND 
          "S2"."END_TIME"<=TO_CHAR(INTERNAL_FUNCTION("S2"."END_DATE"),'HH24:MI')) AND 
          SUBSTR("S2"."JOBNAME",INSTR("S2"."JOBNAME",'/',1,7)+1,8)='NOTALLOCATED') 
        9 - access("JOB_ID"="S2"."JOB_ID") 
        13 - access("STATUS"."STORE_ID"='15') 
        filter(TRUNC(INTERNAL_FUNCTION("STATUS"."JOB_DATE"))=TO_DATE(' 2013-07-08 00:00:00', 'syyyy-mm-dd 
          hh24:mi:ss')) 
        14 - filter(("START_DATE"<=TO_DATE(' 2013-07-08 23:59:59', 'syyyy-mm-dd hh24:mi:ss') AND 
          "START_DATE">=TO_DATE(' 2013-07-08 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND 
          "START_TIME">=TO_CHAR(INTERNAL_FUNCTION("START_DATE"),'HH24:MI') OR 
          "END_DATE"<=TO_DATE(' 2013-07-09 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND 
          "END_DATE">=TO_DATE(' 2013-07-08 00:00:01', 'syyyy-mm-dd hh24:mi:ss') AND 
          "END_TIME"<=TO_CHAR(INTERNAL_FUNCTION("END_DATE"),'HH24:MI')) AND 
          "STATUS"."STORE_ID"<>"STORE_ID" AND (TRUNC(INTERNAL_FUNCTION("STATUS"."JOB_DATE"))=TRUNC(INTERNAL_FUNCT 
          ION("START_DATE")) OR TRUNC(INTERNAL_FUNCTION("STATUS"."JOB_DATE"))=TRUNC(INTERNAL_FUNCTION(" 
          END_DATE"))) AND "STORE_ID"<>'15') 
        15 - access("STATUS"."JOB_ID"="JOB_ID") 
        16 - filter(("S2"."STORE_ID"='15' AND ("S2"."START_DATE"<=TO_DATE(' 2013-07-08 23:59:59', 
          'syyyy-mm-dd hh24:mi:ss') AND "S2"."START_DATE">=TO_DATE(' 2013-07-08 00:00:00', 'syyyy-mm-dd 
          hh24:mi:ss') AND "S2"."START_TIME">=TO_CHAR(INTERNAL_FUNCTION("S2"."START_DATE"),'HH24:MI' 
         ) OR "S2"."END_DATE"<=TO_DATE(' 2013-07-09 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND 
          "S2"."END_DATE">=TO_DATE(' 2013-07-08 00:00:01', 'syyyy-mm-dd hh24:mi:ss') AND 
          "S2"."END_TIME"<=TO_CHAR(INTERNAL_FUNCTION("S2"."END_DATE"),'HH24:MI')) AND 
          SUBSTR("S2"."JOBNAME",INSTR("S2"."JOBNAME",'/',1,7)+1,8)='NOTALLOCATED') 
        17 - access("S2"."STORE_ID"="STORE_ID" AND "JOB_ID"="S2"."JOB_ID") 
         filter("JOB_ID"="S2"."JOB_ID" AND "S2"."STORE_ID"<>'15') 
    

    샘플 Staffschedule 데이터 :

    storeid job_id  startdate   starttime   enddate   endtime jobname 
    12   1  2013-07-11 09:00:00  09:00   2013-07-11 18:00:00  10:00  class A 
    12   1  2013-07-11 09:00:00  10:00   2013-07-11 18:00:00  11:00  class B 
    12   1  2013-07-11 09:00:00  11:00   2013-07-11 18:00:00  18:00  class C 
    

    이 문제에서 저를 도와주세요. 사전에

    덕분에

+0

두 테이블의 테이블 구조는 무엇입니까? 왜 두 테이블 모두에 '저장'해야합니까? 원래 '매장 12'에 있던 직원도 직원 상태에 있습니까? – RedBaron

+2

설명 계획 출력과 어떤 색인이 존재하는지 등을 표시하십시오. 우리는 독자를 신경 쓰지 않습니다. – OldProgrammer

+0

Explain plan이 업데이트되었습니다. 보세요, 당신의 요점을 공유하십시오. – user2556379

답변

0

메인 필터는 date에 있습니다. 그래서 당신은 날짜별로 색인하기를 원할 것입니다. 그러나 이것은 날짜의 레코드 수가 테이블의 레코드 수보다 현저히 작은 경우에만 의미가 있습니다.5 %라고 말하십시오. 따라서 귀하의 가장 좋은 가격은 staffschedules.date (또는 다른 여러 부분으로 구성된 인덱스 (날짜로 시작)) 일 수 있습니다.

sqlplus의 예제 날짜와 함께 두 개의 쿼리를 개별적으로 실행하고 시간이 소비되는 곳을 확인해야합니다. 시간이 첫 번째 검색어에 지출 된 경우 date 색인 또는 date-store 색인이됩니다. 이 부분은 느려서는 안됩니다. 연합의 두 번째 부분은 느린 경우

두 부분으로 그 나누고 각 조사 :

SELECT * 
    FROM staffschedules 
    WHERE date = v_date 
    AND store <> v_store 

SELECT staffid 
    FROM staffstatus 
    WHERE store=v_store 
    AND jobdate=v_date 

첫 부분이 느린 경우

다시 날짜 인덱스 . 두 번째 것이 느린 경우 staffstatus.jobdate에 색인이 필요할 수 있습니다.각각이 빠르게 실행되면 staffid이 결합 된 방법을 살펴야하지만 대부분 jobdate에 대한 인덱스의 경우입니다.

+0

UNION의 두 번째 부분이 시간이 걸립니다. Explain 계획 및 테이블 스키마로 업데이트했습니다. 한번 봐주세요. 내 이해에 따라 두 번째 부분은 직원 일정의 선택, staffstatus의 선택 및 테이블 결합과 같이 실행됩니다. 그래서 시간이 걸립니다. 먼저 참여한 다음 선택하는 방법이 있습니까? 행을 결합한 다음 staffschedules에서 선택한다는 의미입니까? – user2556379

1

좋아, 질문 데이터에는 큰 변화가 있으므로 처음부터 살펴 보겠습니다. 쿼리 텍스트 존재하는 하나의 지정되지 않은 것은 모두의

첫째, :

AND SUBSTR(ses2.org_path,INSTR(ses2.org_path,'/',1,7)+1,8) = 'NOTALLOCATED') 

쿼리의 별칭 ses2 아무 테이블이 없습니다, 그래서 나는 그 일에 수행 할 작업에 대한 아무런 생각이 없습니다. 단지 true으로 평가되지 않는다는 것을 유의하십시오. 왜냐하면 length('NOTALLOCATED')이 8 이상이기 때문입니다.

쿼리 성능에 영향을주는 주요 요소는 NOT IN 조건을 과도하게 사용하는 것입니다. 대부분의 경우 이러한 조건은 기본 쿼리의 필드에 대한 간단한 테스트로 변경해야합니다. 질문에서 질의에
우리는 상황이 그렇게 가지고

select 
    t.* 
from 
    my_table t 
where 
    <big-basket-of-conditions-here> 
    and 
    not (<one-more-test>) 

일예로 변경해야

select 
    t.* 
from 
    my_table t 
where 
    <big-basket-of-conditions-here> 
    and 
    t.field_1 not in (select t.field_1 
        from my_table t 
        where 
         <same-big-basket-of-conditions>  
         and 
         <one_more_test> 
        ) 

당신이 :

  AND job_id NOT IN 
      (SELECT distinct s2.job_id 
      FROM staffschedules s2 
      WHERE s2.store_id=store_id     
      AND ((s2.start_date BETWEEN TRUNC(to_date('07/08/2013','MM/DD/YYYY')) AND TRUNC(to_date('07/08/2013','MM/DD/YYYY'))+(1-1/24/60/60) 
       AND TO_CHAR(s2.start_date,'HH24:MI') <= s2.start_time) 
       OR((s2.end_date BETWEEN TRUNC(to_date('07/08/2013','MM/DD/YYYY'))+1/24/60/60 AND TRUNC(to_date('07/08/2013','MM/DD/YYYY'))+1) 
       AND TO_CHAR(s2.end_date,'HH24:MI') >= s2.end_time)) 
      AND SUBSTR(ses2.org_path,INSTR(ses2.org_path,'/',1,7)+1,8) = 'NOTALLOCATED') 

을 변경할 수있는 단지

  AND SUBSTR(ses2.org_path,INSTR(ses2.org_path,'/',1,7)+1,8) <> 'NOTALLOCATED') 

하위 쿼리 및 NOT IN없이. 이렇게 외부 후 가입 및 테스트 분야 : 당신이 테스트를 수행하기 위해 다른 테이블을 조인해야하는 경우
더 나은 주요 선택에 그것을 할 수

:

select * from (
    select 
    ss.*, 
    nvl(SUBSTR(ses2.org_path,INSTR(ses2.org_path,'/',1,7)+1,12),'X') alloc_flag 
    from 
    staffschedules ss, 
    some_table  ses2 
    where 
    ses2.some_field (+) = ss.some_field and ... 
) 
where alloc_flag <> 'NOTALLOCATED' 

이러한 상황의 또 다른 좋은 예는이 조건입니다

AND job_id NOT IN (
    Select job_Id 
    From staffStatus 
    Where Trunc(job_Date)=Trunc(to_date('07/08/2013','MM/DD/YYYY')) And Store_Id!='15' 
)   

두 가지 나쁜 점이 있습니다. 그 중 하나는 위에서 논의한 것이며 다음은 조건으로 !=의 사용법입니다. 이러한 조건은 효과적인 인덱스 사용 가능성을 제거합니다. 이 경우 최악의 경우는 모든 인덱스가 첫 번째 위치에 store_id을 갖기 때문에 데이터베이스 엔진은 전체 인덱스를 반복하여 모든 조건을 찾아야하며 job_date이라는 첫 번째 위치의 다른 인덱스로 폴백 할 수 없습니다.
그래서, 조건이 같은 기술

select * from (
    select 
    ss.*, 
    nvl(statuses.store_id,'X') status_store_id 
    from 
    staffschedules ss, 
    staffstatus statuses 
    where 
    statuses.job_id (+) = ss.some_field 
    Trunc(job_Date (+)) = Trunc(to_date('07/08/2013','MM/DD/YYYY')) 
    and ... 
) 
where status_store_id = '15' 

그리고 마지막으로 rewrited 할 수는 필드에 대한 기능 사용에 합류하면서이다. Trunc(job_date)은 직관적으로 보일 수 있지만 데이터베이스가 job_date에 색인을 사용할 수 없으므로 성능이 좋지 않을 수 있습니다. 따라서 경계 조건을 더 잘 사용하는 그러한 상황에서 :

job_Date >= to_date('07/08/2013','MM/DD/YYYY') 
    and 
    job_Date < to_date('07/08/2013','MM/DD/YYYY') + 1 

그리고 마지막으로 전체 일정 목록을 얻는 예제 쿼리를 작성했습니다. this SQLFiddle에서 테스트하거나 아래를보십시오. 지정된 저장소 및 날짜에 대한 전체 일정 및 상태 집합을 반환합니다.
스케줄링 로직을 이해하지 못하기 때문에 직접 쿼리 결과를 필터링 할 수 있습니다.

with params as (
    -- Just to write parameters only once. 
    -- Note that parameter date truncated. 
    select 
    trunc(to_date('07/24/2013 07:20','MM/DD/YYYY HH24:MI')) as date_val, 
    '15'             as store_id 
    from dual 
), 
store_jobs_from_schedule as ( 
    -- Select all valid schedules on desired date for specified store with corresponding statuses if any. 
    select       
    scheduled_jobs.job_id   job_id, 
    scheduled_jobs.store_id   schedule_store_id, 
    scheduled_jobs.start_date  schedule_start_date, 
    scheduled_jobs.start_date_time schedule_start_time, 
    scheduled_jobs.end_date   schedule_end_date, 
    scheduled_jobs.end_date_time  schedule_end_time, 
    scheduled_jobs.jobname   schedule_job_name, 
    status_list.job_seq_id   status_id,  
    status_list.store_id    status_store_id, 
    status_list.job_date    status_job_date, 
    status_list.status    status 
    from 
    (       
     select -- get all schedules for specified date and store. 
     p.date_val, 
     schedules.job_id, 
     schedules.store_id, 
     schedules.start_date, 
     (-- Calculate exact start time as date and time value 
      trunc(schedules.start_date) + 
      (to_date(schedules.start_time,'hh24:mi') - to_date('00:00','hh24:mi')) 
     ) start_date_time, 
     schedules.end_date, 
     (-- Calculate exact end time as date and time value 
      trunc(schedules.end_date) + 
      (to_date(schedules.end_time,'hh24:mi') - to_date('00:00','hh24:mi')) 
     ) end_date_time, 
     schedules.jobname 
     from 
     params   p, 
     staffschedules schedules 
     where         
     -- scheduled to specified store 
     schedules.store_id = p.store_id 
     and          
     -- start before the end of desired date 
     schedules.start_date < p.date_val + 1 
     and        
     -- end after beginning of desired date 
     schedules.end_date >= p.date_val 
    ) 
       scheduled_jobs, 
    staffstatus status_list 
    where               
    -- Check if schedule start time are valid 
    (scheduled_jobs.start_date <= scheduled_jobs.start_date_time) 
    and 
    -- Check if schedule end time are valid 
    (scheduled_jobs.end_date >= scheduled_jobs.end_date_time) 
    and       
    -- Link status by staff and date if any - only on desired date, 
    -- not for full schedule length 
    status_list.job_id (+) = scheduled_jobs.job_id 
    and 
    status_list.job_date (+) >= scheduled_jobs.date_val 
    and 
    status_list.job_date (+) < scheduled_jobs.date_val + 1 
), 
store_stuff_jobs as (
    -- Select all statuses for specified date and store and link it to corresponding schedules if any 
    select -- clear data in invalid schedules 
    job_id                 job_id,  
    decode(is_valid_schedule,'N', null,    schedule_store_id) schedule_store_id, 
    decode(is_valid_schedule,'N', cast(null as date), schedule_start_date) schedule_start_date, 
    decode(is_valid_schedule,'N', cast(null as date), schedule_start_time) schedule_start_time, 
    decode(is_valid_schedule,'N', cast(null as date), schedule_end_date) schedule_end_date, 
    decode(is_valid_schedule,'N', cast(null as date), schedule_end_time) schedule_end_time, 
    decode(is_valid_schedule,'N', null,    schedule_job_name) schedule_job_name, 
    status_id                status_id,  
    status_store_id               status_store_id, 
    status_job_date               status_job_date, 
    status                 status 
    from ( 
    select -- Calculate if selected schedule are valid    
     job_id, 
     schedule_store_id, 
     schedule_start_date, 
     schedule_start_time, 
     schedule_end_date, 
     schedule_end_time, 
     schedule_job_name, 
     status_id,  
     status_store_id, 
     status_job_date, 
     status, 
     (-- Calculate flag to check if times of schedules are valid 
     case 
      when 
      (schedule_start_date > schedule_start_time) 
      or 
      (schedule_end_date < schedule_end_time) 
      then 'N' 
      else 'Y' 
     end   
    ) is_valid_schedule 
    from (
     select 
     status_list.job_id   job_id, 
     schedules.store_id   schedule_store_id, 
     schedules.start_date  schedule_start_date, 
     (-- Calculate exact start time as date and time value 
      trunc(schedules.start_date) + 
      (to_date(schedules.start_time,'hh24:mi') - to_date('00:00','hh24:mi')) 
     )       schedule_start_time, 
     schedules.end_date   schedule_end_date, 
     (-- Calculate exact end time as date and time value 
      trunc(schedules.end_date) + 
      (to_date(schedules.end_time,'hh24:mi') - to_date('00:00','hh24:mi')) 
     )       schedule_end_time, 
     schedules.jobname   schedule_job_name, 
     status_list.job_seq_id  status_id,  
     status_list.store_id  status_store_id, 
     status_list.job_date  status_job_date, 
     status_list.status   status 
     from 
     params   p, 
     staffstatus status_list, 
     staffschedules schedules 
     where 
     status_list.job_date >= p.date_val  
     and 
     status_list.job_date < p.date_val + 1  
     and 
     status_list.store_id = p.store_id 
     and          
     -- Link schedules for same staff on same date if any. 
     -- Even schedules to same store, because we need exactly same 
     -- record as in first query to eliminate duplicates on last step. 
     schedules.job_id (+) = status_list.job_id 
     and 
     schedules.start_date (+) < trunc(status_list.job_date) + 1 -- start before the end of desired date 
     and 
     schedules.end_date (+) >= trunc(status_list.job_date) -- end after beginning of desired date 
    ) 
) 
) 
    select 
    -- records comes from schedules 
    job_id, 
    schedule_store_id store_id, 
    schedule_store_id, 
    schedule_start_date, 
    schedule_start_time, 
    schedule_end_date, 
    schedule_end_time, 
    schedule_job_name, 
    status_id,  
    status_store_id, 
    status_job_date, 
    status 
    from 
    store_jobs_from_schedule 
union -- duplicates eliminated 
    select 
    -- records comes from status 
    job_id, 
    status_store_id store_id, 
    schedule_store_id, 
    schedule_start_date, 
    schedule_start_time, 
    schedule_end_date, 
    schedule_end_time, 
    schedule_job_name, 
    status_id,  
    status_store_id, 
    status_job_date, 
    status 
    from 
    store_stuff_jobs 
order by 
    job_id, schedule_start_date, schedule_start_time 
+0

ThinkJet에 감사드립니다. 이것은 잘 작동합니다. 그러나이 시나리오에 대한 일정은 반환되지 않습니다. 직원 xyz의 집 저장소는 13입니다. 오늘 그는 매장 12에서 일하고 있습니다. staffstatus 테이블에서 xyz 저장소는 12 (호스트 저장소이므로 출장은 매장 12로 표시됩니다)이고 staffschedules 테이블 xyz 저장소는 13입니다 (항상 홈 스토어 ID가있는 일정) . 저장소 12 일정을 가져 오려면 외부 ID가 13 인 일정을 조건에서 거부합니다. 이 쿼리를 사용하여 다른 상점 일정을 가져올 수 있습니까? – user2556379

+0

@ user2556379 질문의 첫 번째 버전과 비교하여 스키마를 크게 변경했습니다. 이전 질문의 'staffid'에 해당하는 필드는 무엇입니까? 왜 날짜와 시간을 2 개의 분리 된 열로 나눕니까? 시간이 문자 형식으로 저장되는 이유는 무엇입니까? 같은 행의'TO_CHAR (start_date, 'HH24 : MI') <= start_time)'과 같은 비교 대상의 목적은 무엇입니까? – ThinkJet

+0

죄송합니다. Staffid는 Job_Id로 바뀝니다. staffschedules의 샘플 데이터. – user2556379

관련 문제