Search code examples
sqloracle-databasequery-optimizationoracle-ebs

A long query tuning


I have the following query, in this query, I am selecting the ebs tables, with a custom table which has header_id and populating the the data in a custom table XXREPORT_L1_TBL.

I want to tune this query.

[update] made changes to the query as bellow:

  1. splited the query in 3 different insert statements
  2. removed the columns which do in line queries for values
  3. added an update statement at the end for these columns.

    insert into XX.XXREPORT_L1_TBL ( ORDER_NUMBER 
                                            , LINE_NUMBER
                                            , UOM
                                            , CUSTOMER_LENGTH
                                            , THEORETICAL_WEIGHT
                                            , FINISH
                                            , ORDER_QTY_PCS
                                            , ORDER_QTY_KGS
                                            , SALES_VALUE
                                            , TOTAL_VALUE
                                            , ORDERED_QUANTITY 
                                            , WIP_ENTITY_ID 
                                            , JOB_NAME
                                            , JOB_TYPE
                                            , JOB_STATUS 
                                            , JOB_RELEASED_DATE 
                                            , DATE_COMPLETED
                                            , DATE_CLOSED
                                            , JOB_CARD_QTY 
                                            , ALLOY 
                                            , PROFILE
                                            , PROD_QTY_KGS 
                                            , COST_KGS_THEORY 
                                            , COST_KGS_ACTUAL
        )
    
    SELECT 
     ---- Sales Order
       xx.order_number
       ,xx.line_number 
       ,xx.UOM,
       xx.customer_length,
       xx.theoretical_weight,
       xx.finish,
       xx.order_qty_pcs, 
       xx.order_qty_kgs,      
       xx.sales_value, -- total value / total kgs
       xx.total_value, -- line total
       xx.ordered_quantity,
     -- Production
       xx.wip_entity_id,
       xx.job_name,
       ( select case when a.inventory_item_id = 5716770 and a.job_type='NOT CHILD' then 'PARENT' 
                when a.job_type='CHILD' and a.inventory_item_id is null then 'CHILD' 
                when a.job_type='NOT CHILD' and a.inventory_item_id is NOT null then 'NOT CHILD' END JOB_TYPE 
         from ( select  disc2.wip_entity_id as wip_entity_id,      decode      (      nvl(disc2.attribute9,-1) , -1,'NOT CHILD', 'CHILD') job_type,      oel.inventory_item_id
                      from APPS.wip_discrete_jobs disc2,      APPS.oe_order_lines_all oel
                      where oel.line_id(+) = disc2.source_line_id
               )a
         where a.wip_entity_id = xx.wip_entity_id
       ) job_type, 
       ( select decode ( xx.status_type, 6, 'Open',
                                       3, 'Open',
                                       4,      'Completed',                                          
                                        LU1.MEANING )
                from APPS.FND_LOOKUP_VALUES LU1 
                where  LU1.LOOKUP_TYPE = 'WIP_JOB_STATUS'
                AND LU1.LOOKUP_CODE = xx.STATUS_TYPE
       ) job_status,      
       xx.job_released_date,
       xx.date_completed, 
       xx.date_closed
       ,xx.net_quantity as job_card_qty
       ,xx.alloy
       ,xx.profile
       ,xx.prod_qty_kgs      
         -- Theoretical Order cost  
       ,xx.cost_kgs_theory
         -- Actual Order cost      
       ,xx.cost_kgs_actual
         from (      
          select a.*
        -- Theoretical Order cost
                , DECODE (a.qty_completed * a.customer_length * a.theoretical_weight,0,0,
                    a.TOT_THEORY_COST_RELIEVED/(a.qty_completed * a.customer_length * a.theoretical_weight) ) as cost_kgs_theory 
        -- Actual Order cost
                , DECODE ( a.qty_completed * a.customer_length * a.theoretical_weight, 0, 0,
                    a.TOT_ACTUAL_COST_INCURRED/(a.qty_completed * a.customer_length * a.theoretical_weight )) as cost_kgs_actual
                from ( 
    
      select 
     -- Normal orders, INTERNAL Orders, Crimped Profile (parent jobs)     
       -- Sales Order      
            oeh.order_number as order_number
            ,oel.line_number
            ,oel.pricing_quantity_uom as UOM
            ,oel.attribute1 as customer_length
            ,oel.attribute6 as theoretical_weight
            ,oel.attribute5 as finish
            ,oel.attribute18 as order_qty_pcs
            ,oel.attribute7 as order_qty_kgs
            ,xx_om.GetLineUnitSellingPrice(oel.line_id) sales_value
            ,xx_om.GetHeaderUnitSellingPrice(oeh.header_id) total_value
            ,oel.ordered_quantity ordered_quantity
     -- Production                
            , tbl0.qty_completed as qty_completed
            ,disc.wip_entity_id as wip_entity_id
            ,( select wip_entity_name from APPS.wip_entities ent
                where ent.wip_entity_id = disc.wip_entity_id) job_name
            ,disc.status_type
            ,disc.date_released as job_released_date
            , DECODE ( disc.date_completed, NULL, disc.date_completed,
            -- my day Definition
            to_date(to_char(to_date(TO_CHAR(disc.date_completed-     interval      '7' hour,'DD-MON-YYYY')||'00:00:00','DD-MON-YYYY HH24:MI:SS'),      'DD-MON-YYYY HH24:MI:SS'), 'DD-MON-YYYY      HH24:MI:SS'))      as      date_completed
            , DECODE ( disc.date_closed, NULL, disc.date_closed,
            to_date(to_char(to_date(TO_CHAR(disc.date_closed-     interval      '7' hour,'DD-MON-YYYY')||'00:00:00','DD-MON-YYYY HH24:MI:SS'),      'DD-MON-YYYY HH24:MI:SS'), 'DD-MON-YYYY HH24:MI:SS'))      as      date_closed
            , disc.net_quantity
       , ( select opr2.quantity_completed
             from APPS.wip_operations opr2
        where opr2.wip_entity_id = disc.wip_entity_id
        and opr2.operation_seq_num =       (select max(opr.operation_seq_num)
                                          from APPS.wip_operations opr, APPS.wip_discrete_jobs disc2
                                          where opr.wip_entity_id = disc2.wip_entity_id 
                                          and disc2.wip_entity_id =  disc.wip_entity_id))* oel.attribute1 * oel.attribute6 as prod_qty_kgs
            ,oel.attribute4 as alloy
            ,oel.attribute2 as profile
    
     -- Theoretical Order cost
              ,tbl0.TOT_THEORY_COST_RELIEVED
     -- Actual Order cost 
              ,tbl0.TOT_ACTUAL_COST_INCURRED
     from XX.XXREPORT_Lzero_TBL tbl0
     join APPS.oe_order_headers_all oeh on oeh.header_id = tbl0.header_id 
     join APPS.oe_order_lines_all oel on   oeh.org_id = oel.org_id and     oeh.header_id = oel.header_id
     join APPS.xx_assemblies asm on oel.line_id = asm.line_id
     join APPS.wip_discrete_jobs disc on disc.primary_item_id = asm.inventory_item_id
       where  oel.link_to_line_id is null
    
     union
     -- Crimped Child Jobs
      select 
     -- Sales Order      
            oeh.order_number as order_number
            ,oel.line_number
            ,oel.pricing_quantity_uom as UOM
            ,oel.attribute1 as customer_length
            ,oel.attribute6 as theoretical_weight
            ,oel.attribute5 as finish
            ,oel.attribute18 as order_qty_pcs
            ,oel.attribute7 as order_qty_kgs
            ,xx_om.GetLineUnitSellingPrice(oel.line_id) sales_value
            ,xx_om.GetHeaderUnitSellingPrice(oeh.header_id) total_value
            ,oel.ordered_quantity ordered_quantity
     -- Production
            , tbl0.qty_completed as qty_completed
            ,child_jobs.wip_entity_id as wip_entity_id
            ,( select wip_entity_name from APPS.wip_entities ent
                where ent.wip_entity_id = child_jobs.wip_entity_id)          job_name
            ,disc.status_type
            ,disc.date_released as job_released_date
            , DECODE ( disc.date_completed, NULL, disc.date_completed,
            to_date(to_char(to_date(TO_CHAR(disc.date_completed-interval      '7' hour,'DD-MON-YYYY')||'00:00:00','DD-MON-YYYY HH24:MI:SS'),      'DD-MON-YYYY HH24:MI:SS'), 'DD-MON-YYYY HH24:MI:SS')) as      date_completed
            , DECODE ( disc.date_closed, NULL, disc.date_closed,
            to_date(to_char(to_date(TO_CHAR(disc.date_closed-interval      '7' hour,'DD-MON-YYYY')||'00:00:00','DD-MON-YYYY HH24:MI:SS'),      'DD-MON-YYYY HH24:MI:SS'), 'DD-MON-YYYY HH24:MI:SS')) as      date_closed
            , disc.net_quantity
            , ( select opr2.quantity_completed
                  from APPS.wip_operations opr2
                  where opr2.wip_entity_id = disc.wip_entity_id
                  and opr2.operation_seq_num =       (select          max(opr.operation_seq_num)
                                                    from     APPS.wip_operations opr, APPS.wip_discrete_jobs disc2
                                                    where opr.wip_entity_id = disc2.wip_entity_id 
                                                    and disc.wip_entity_id =  disc.wip_entity_id))* oel.attribute1 * oel.attribute6 as prod_qty_kgs                
            ,oel.attribute4 as alloy
            ,oel.attribute2 as profile
     -- Theoretical Order cost
              ,tbl0.TOT_THEORY_COST_RELIEVED
     -- Actual Order cost 
              ,tbl0.TOT_ACTUAL_COST_INCURRED     
    from XX.XXREPORT_Lzero_TBL tbl0
     join APPS.oe_order_headers_all oeh on oeh.header_id = tbl0.header_id
     join APPS.oe_order_lines_all oel on oeh.org_id = oel.org_id and     oeh.header_id = oel.header_id
     join APPS.xx_assemblies asm on oel.line_id = asm.line_id
     join APPS.wip_discrete_jobs disc on disc.primary_item_id =     asm.inventory_item_id 
     join ( select wdj2.source_line_id, wdj2.attribute9 child_wip, wdj2.wip_entity_id, wdj2.status_type status_type
        from APPS.wip_discrete_jobs wdj2
        where attribute9 IS NOT NULL ) child_jobs on      child_jobs.child_wip = to_char(disc.wip_entity_id)
     where oel.link_to_line_id is null
    
      union
      -- Orders with star (*) items need to pick profile and customer      length etc from ego_configured_pr_agv view 
      select 
     -- Sales Order      
            oeh.order_number as order_number
            ,oel.line_number
            ,oel.pricing_quantity_uom as UOM
            ,to_char(agv.gx_cp_length) as customer_length
            ,to_char(agv.gx_cp_th_weight) as theoretical_weight
            ,agv.gx_cp_surfacetreatment as finish
            ,oel.attribute18 as order_qty_pcs
            , to_char(agv.gx_cp_th_weight * agv.gx_cp_length *          oel.ordered_quantity) as order_qty_kgs
            ,XX.xx_om.GetLineUnitSellingPrice(oel.line_id) sales_value
            ,XX.xx_om.GetHeaderUnitSellingPrice(oeh.header_id)      total_value
            ,oel.ordered_quantity ordered_quantity
     -- Production                
            , tbl0.qty_completed as qty_completed
            ,disc.wip_entity_id as wip_entity_id
            ,( select wip_entity_name from APPS.wip_entities ent
                where ent.wip_entity_id = disc.wip_entity_id) job_name
            ,disc.status_type
            ,disc.date_released as job_released_date
            , DECODE ( disc.date_completed, NULL, disc.date_completed,
            to_date(to_char(to_date(TO_CHAR(disc.date_completed-interval      '7' hour,'DD-MON-YYYY')||'00:00:00','DD-MON-YYYY HH24:MI:SS'),      'DD-MON-YYYY HH24:MI:SS'), 'DD-MON-YYYY HH24:MI:SS')) as      date_completed
            , DECODE ( disc.date_closed, NULL, disc.date_closed,
            to_date(to_char(to_date(TO_CHAR(disc.date_closed-interval      '7' hour,'DD-MON-YYYY')||'00:00:00','DD-MON-YYYY HH24:MI:SS'),      'DD-MON-YYYY HH24:MI:SS'), 'DD-MON-YYYY HH24:MI:SS')) as      date_closed
            , disc.net_quantity
       , ( select opr2.quantity_completed
        from APPS.wip_operations opr2
        where opr2.wip_entity_id = disc.wip_entity_id
        and opr2.operation_seq_num =       (select      max(opr.operation_seq_num)
                                          from APPS.wip_operations opr,      APPS.wip_discrete_jobs disc2
                                          where opr.wip_entity_id =      disc2.wip_entity_id 
                                          and disc2.wip_entity_id =       disc.wip_entity_id))* agv.gx_cp_length * agv.gx_cp_th_weight as      prod_qty_kgs
            ,gx_cp_alloy as alloy
            ,gx_cp_profile_id as profile
     -- Theoretical Order cost
              ,tbl0.TOT_THEORY_COST_RELIEVED
     -- Actual Order cost 
              ,tbl0.TOT_ACTUAL_COST_INCURRED
    
     from XX.XXREPORT_Lzero_TBL tbl0
     join APPS.oe_order_headers_all oeh on oeh.header_id = tbl0.header_id
     join APPS.oe_order_lines_all oel on oeh.org_id = oel.org_id and          oeh.header_id = oel.header_id
     join APPS.wip_discrete_jobs disc on oel.line_id =      disc.source_line_id
     join APPS.ego_gx_configured_pr_agv agv on agv.inventory_item_id=      oel.inventory_item_id
       where oel.link_to_line_id is null
            )a 
            ) xx;
    

Solution

  • There's almost certainly no short and simple solution to tuning this query. The problem here is it's size and complexity. Lack of performance is merely a consequence.

    As a first step I would consider taking a break from the keyboard. Grab a pen and paper and in plain English (or whichever "human" language you prefer) write the questions you want answered from your database via this query. Then ask yourself what columns/variables/attributes do you absolutely need to answer those questions? Write them down as well.

    Now, do you really need all of those columns, nested joins, selects, and so forth to produce those variables? Maybe, but probably not. The key point here is to focus on only the data/information you need (YAGNI) and from there map out a picture the bare relationships you need to produce the information that answer your question. In other words, work from the outside in, not the other way around.

    I realize that this perhaps sounds a bit abstract and vague, but the whole point is that maintaining clear and simple code is always an ongoing struggle. Keeping your eye on the objective at hand will help keep your head of the weeds.

    Finally, a few more specific thoughts at a glance:

    • Do you really need that union? Try to do without it if you can.
    • Nesting sucks. Nested nesting especially sucks. Keep things flat whenever possible and practical.
    • Is it possible or practical to split this into independent, smaller queries?
    • Use more descriptive names for your variables, add comments judiciously.
    • Learn and master the SQL EXPLAIN command.