Oracle sql查询需要优化

时间:2013-07-06 14:42:27

标签: sql oracle oracle11g

我有以下要求。

我想获取特定日期(当天)和特定商店(例如12)的员工的工作时间表。此外,还有其他商店(13)员工将来商店(12),商店12名员工可以在其他商店工作。

如果员工标记他们的出勤状态,状态表将存储当前/缺席的详细信息。

我想要取 1)无论员工情况如何,员工时间表中店员12的员工时间表都在特定日期进入。

2)来自其他商店的员工安排的时间表,并通过参考staffstatus拒绝前往其他商店的员工的时间表(如果条目在状态表中,我需要检索,否则没有)

2表:

  1. 时间表
  2. 状态---->员工出席/缺席细节
  3. 我写了下面的查询。运行平均需要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
    

    请帮我解决这个问题。

    提前致谢

2 个答案:

答案 0 :(得分: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
  t.field_1 not in ( select t.field_1 
                     from my_table t
                     where 
                       <same-big-basket-of-conditions>     
                       and
                       <one_more_test>
                   )  

必须更改为

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

E.g。你有:

          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 如果您需要连接另一个表来执行测试,最好在主选择中执行:do outer join然后测试字段:

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'

最后一件事是加入时字段上的函数用法。 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

答案 1 :(得分:0)

您的主过滤器位于date。所以你想要按日期索引。但是,只有当日期的记录数远远少于表中的记录数时,这才有意义。说&lt; 5%。所以你的第一个最好的收获是staffschedules.date的索引(或者从日期开始的其他多部分索引)。

您应该在sqlplus中使用示例日期单独运行两个查询,并查看花费的时间。如果在第一个查询上花费了时间,那么这将是date索引甚至是date-store索引。这部分不应该很慢。

如果UNION的第二部分很慢,请将其分成两部分并进行调查:

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上的索引。