提高SQL查询的性能

时间:2018-08-08 15:06:01

标签: sql oracle oracle11g

我有一个tranctions表,我需要在其中获取5种不同牵引力的转换日期和时间(订单下降,订单选择,标有订单的地址,已加载并已发货)。

对于每个订单,我都尝试获取特定日期以及我们在该日期发货的订单的这些转换时间。我的查询几乎每个人都会运行以获取这些信息。

是否有更好的方法重写?非常感谢您的帮助。

explain plan

Index for Historymaster

SELECT
    "ORDER",
     MAX(CASE WHEN "ACTION" = 'DNLD' THEN TO_CHAR(datetimecreated,'yyyy-mm-dd 
     hh24:mi')  END) AS ORDER_Drop_time,
     MAX(CASE WHEN "ACTION" = 'REQUEST' THEN TO_CHAR(datetimecreated,'yyyy-mm-dd hh24:mi')  END) AS Label_request_time,
     MAX(CASE WHEN "ACTION" = 'PICK' THEN TO_CHAR(datetimecreated,'yyyy-mm-dd hh24:mi')  END) AS pick_time,
     MAX(CASE WHEN "ACTION" = 'LOAD' THEN TO_CHAR(datetimecreated,'yyyy-mm-dd hh24:mi')  END) AS Load_TIME,
     MAX(CASE WHEN "ACTION" = 'SHIP' THEN TO_CHAR(datetimecreated,'yyyy-mm-dd hh24:mi')  END) AS SHIP_COM_TIME
FROM
    historymaster hm
WHERE
    "ORDER" IN (
        SELECT
            "ORDER"
        FROM
            historymaster
        WHERE
            datetimecreated >=:usestartdate
            AND   datetimecreated <=:useenddate
            AND   "ACTION" = 'SHIP'
            AND   "OBJECT" = 'OBORDLINE'
    ) -- Looking up order ID for ship transations and using it 
    AND   (
        (-- Order drop 
            "ACTION" = 'DNLD'
            AND   "OBJECT" = 'OBORDLINE'
            AND   actionmodifier IS NULL
            AND   reasoncode = '00'
        )
        OR --Address label request
         (
            "ACTION" = 'REQUEST'
            AND   "OBJECT" = 'LABEL'
            AND   "CLASS" = 'ADDR'
        )
        OR -- pick 
         (
            "ACTION" = 'PICK'
            AND   "OBJECT" = 'OBO'
            AND   "CLASS" = 'INVE'
            AND   actualquantity != 0
            AND   substr(ordertype,1,1) = 'N'
        )
        OR   -- Trailer Load
         (
            "ACTION" = 'LOAD'
            AND   "OBJECT" = 'OBO'
            AND   "CLASS" = 'INVE'
        )
        OR --Ship Complete 
         (
            "OBJECT" = 'OBORDLINE'
            AND   hm.package = ' '
            AND   actionmodifier IS NULL
            AND   "ACTION" = 'SHIP'
        )
    )
GROUP BY
    "ORDER";

输出:

ORDER         ORDER_Drop_time   Label_request_time  PICK_TIME   Load_TIME   SHIP_COM_TIME
D2KJJKJLB-35689 8/2/2018 9:50   8/6/2018 9:50   8/6/2018 8:50   8/6/2018 10:50  8/7/2018 14:16

2 个答案:

答案 0 :(得分:0)

如果查询变慢,则应使用索引。

我能想到的最重要的索引是:

create index ix1 on historymaster ("OBJECT", "ACTION", datetimecreated);

此外,如果您确实希望此查询更快,则可以添加:

create index ix2 on historymaster ("OBJECT", "ACTION", reasoncode);
create index ix3 on historymaster ("OBJECT", "ACTION", "CLASS");

请注意不要向我们的表添加太多索引,因为它会使您的INSERTUPDATEDELETE变慢。您需要找到一个平衡点。

请检索查询的执行计划。为此:

  1. 准备检索:

    drop table plan_table;
    
  2. 创建执行计划:

    explain plan for
    select ...
    
  3. 获取执行计划:

    select plan_table_output 
      from table(dbms_xplan.display('plan_table',null,'typical'));
    

    获得后,将计划添加到您的问题中。

答案 1 :(得分:0)

您的查询基本上执行两次访问,因此(假设您仅选择少量订单)您需要两个索引来反映它。

第一个索引在给定的时间范围内选择ordersaction适当的object

 ("ACTION","OBJECT",datetimecreated)

第二个索引然后查询所有具有请求的ordersaction的{​​{1}}的记录

object

为演示主体,我创建了一个测试表,其中包含200万行和10万个订单(请参见下面的脚本)。

请注意,我不使用保留名称(例如,“ ORDER”为order_id),而对操作和对象使用“中性”名称。

 ("ORDER", "ACTION" ,"OBJECT")

您期望此执行计划-内部NESTED LOOP执行上述两个访问-请参阅 访问条件5和7。

select order_id,
max(case when action_id = 'A1' then datetimecreated end) as A1_date,
max(case when action_id = 'A2' then datetimecreated end) as A2_date,
max(case when action_id = 'A3' then datetimecreated end) as A3_date,
max(case when action_id = 'A4' then datetimecreated end) as A4_date,
max(case when action_id = 'A5' then datetimecreated end) as A5_date 
from test
where order_id in (
   select order_id
   from test
   where datetimecreated >= DATE'2018-02-05' and  datetimecreated <= DATE'2018-02-06' and
   action_id = 'A1' and object_id = 'O1')
AND
(action_id = 'A1' and object_id = 'O1' or
 action_id = 'A2' and object_id = 'O2' or
 action_id = 'A3' and object_id = 'O3' or
 action_id = 'A4' and object_id = 'O4' or
 action_id = 'A5' and object_id = 'O1')
group by order_id;


  ORDER_ID A1_DATE  A2_DATE  A3_DATE  A4_DATE  A5_DATE
---------- -------- -------- -------- -------- --------
       826 05.02.18 10.02.18 15.02.18 20.02.18 21.02.18
       833 05.02.18 10.02.18 15.02.18 20.02.18 21.02.18
.....
       823 05.02.18 10.02.18 15.02.18 20.02.18 21.02.18
       838 05.02.18 10.02.18 15.02.18 20.02.18 21.02.18

25 rows selected.

Elapsed: 00:00:00.10

这里是生成测试数据以供播放的脚本:

Plan hash value: 1930696803

--------------------------------------------------------------------------------------------
| Id  | Operation                      | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT               |           |   223 | 13380 |    12   (9)| 00:00:01 |
|   1 |  HASH GROUP BY                 |           |   223 | 13380 |    12   (9)| 00:00:01 |
|   2 |   NESTED LOOPS                 |           |       |       |            |          |
|   3 |    NESTED LOOPS                |           |   223 | 13380 |    11   (0)| 00:00:01 |
|   4 |     TABLE ACCESS BY INDEX ROWID| TEST      |     1 |    30 |     4   (0)| 00:00:01 |
|*  5 |      INDEX RANGE SCAN          | TEST_IDX1 |     1 |       |     3   (0)| 00:00:01 |
|   6 |     INLIST ITERATOR            |           |       |       |            |          |
|*  7 |      INDEX RANGE SCAN          | TEST_IDX2 |     1 |       |     6   (0)| 00:00:01 |
|   8 |    TABLE ACCESS BY INDEX ROWID | TEST      |   502 | 15060 |     7   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   5 - access("ACTION_ID"='A1' AND "OBJECT_ID"='O1' AND 
              "DATETIMECREATED">=TO_DATE(' 2018-02-05 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND 
              "DATETIMECREATED"<=TO_DATE(' 2018-02-06 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
   7 - access("ORDER_ID"="ORDER_ID" AND ("ACTION_ID"='A1' AND "OBJECT_ID"='O1' OR 
              "ACTION_ID"='A2' AND "OBJECT_ID"='O2' OR "ACTION_ID"='A3' AND "OBJECT_ID"='O3' OR 
              "ACTION_ID"='A4' AND "OBJECT_ID"='O4' OR "ACTION_ID"='A5' AND "OBJECT_ID"='O1'))