Tuesday, April 10, 2012

Record Print Status Too slow, Sql#4

Poor Performing Sql#4


(This sql was formatted for readability)

SELECT SYSDATE
, :B4
, IBY_AMOUNT_PAID
, IBY_DISCOUNT_AMOUNT_TAKEN
, AP_INVOICES_UTILITY_PKG.GET_PAYMENT_STATUS(INV.INVOICE_ID)
, INV.INVOICE_ID
FROM AP_INVOICES_ALL INV
, AP_SELECTED_INVOICES_ALL SI
, (SELECT
    SUM(IBYDOCS.PAYMENT_AMOUNT) IBY_AMOUNT_PAID
  , NVL(SUM(IBYDOCS.PAYMENT_CURR_DISCOUNT_TAKEN),0) IBY_DISCOUNT_AMOUNT_TAKEN
  , IBYDOCS.CALLING_APP_DOC_UNIQUE_REF1 REF1
  , IBYDOCS.CALLING_APP_DOC_UNIQUE_REF2 REF2
  , IBYDOCS.CALLING_APP_DOC_UNIQUE_REF3 REF3
  FROM IBY_FD_DOCS_PAYABLE_V IBYDOCS
  , IBY_FD_PAYMENTS_V IBYPMTS
  WHERE IBYPMTS.ORG_TYPE              = 'OPERATING_UNIT'
  AND IBYPMTS.PAYMENT_ID              = IBYDOCS.PAYMENT_ID
  AND IBYPMTS.COMPLETED_PMTS_GROUP_ID = :B2
  AND IBYPMTS.ORG_ID                  = :B1
  GROUP BY IBYDOCS.CALLING_APP_DOC_UNIQUE_REF1
  , IBYDOCS.CALLING_APP_DOC_UNIQUE_REF2
  , IBYDOCS.CALLING_APP_DOC_UNIQUE_REF3
  ) IBYDPM
WHERE INV.INVOICE_ID              = SI.INVOICE_ID
AND SI.CHECKRUN_NAME              = :B3
AND INV.INVOICE_TYPE_LOOKUP_CODE <> 'INTEREST'
AND IBYDPM.REF2                   = TO_CHAR(INV.INVOICE_ID)
AND IBYDPM.REF1                   = TO_CHAR(SI.CHECKRUN_ID)
AND IBYDPM.REF2                   = TO_CHAR(SI.INVOICE_ID)
AND IBYDPM.REF3                   = TO_CHAR(SI.PAYMENT_NUM);
This sql had following plan at our site

PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------------
SQL handle: SQL_b4d6909efd6427e2
SQL text: SELECT SYSDATE, :B4 , IBY_AMOUNT_PAID, IBY_DISCOUNT_AMOUNT_TAKEN, 
          AP_INVOICES_UTILITY_PKG.GET_PAYMENT_STATUS(INV.INVOICE_ID), 
          INV.INVOICE_ID FROM AP_INVOICES_ALL INV, AP_SELECTED_INVOICES_ALL SI, 
          (SELECT SUM(IBYDOCS.PAYMENT_AMOUNT) IBY_AMOUNT_PAID, 
          NVL(SUM(IBYDOCS.PAYMENT_CURR_DISCOUNT_TAKEN),0) 
          IBY_DISCOUNT_AMOUNT_TAKEN, IBYDOCS.CALLING_APP_DOC_UNIQUE_REF1 REF1, 
          IBYDOCS.CALLING_APP_DOC_UNIQUE_REF2 REF2, 
          IBYDOCS.CALLING_APP_DOC_UNIQUE_REF3 REF3 FROM IBY_FD_DOCS_PAYABLE_V 
          IBYDOCS, IBY_FD_PAYMENTS_V IBYPMTS WHERE IBYPMTS.ORG_TYPE = 
          'OPERATING_UNIT' AND IBYPMTS.PAYMENT_ID = IBYDOCS.PAYMENT_ID AND 
          IBYPMTS.COMPLETED_PMTS_GROUP_ID = :B2 AND IBYPMTS.ORG_ID = :B1 GROUP BY 
          IBYDOCS.CALLING_APP_DOC_UNIQUE_REF1, 
          IBYDOCS.CALLING_APP_DOC_UNIQUE_REF2, 
          IBYDOCS.CALLING_APP_DOC_UNIQUE_REF3) IBYDPM WHERE INV.INVOICE_ID = 
          SI.INVOICE_ID AND SI.CHECKRUN_NAME = :B3 AND 
          INV.INVOICE_TYPE_LOOKUP_CODE <> 'INTEREST' AND IBYDPM.REF2 = 
          TO_CHAR(INV.INVOICE_ID) AND IBYDPM.REF1 = TO_CHAR(SI.CHECKRUN_ID) AND 
          IBYDPM.REF2 = TO_CHAR(SI.INVOICE_ID) AND IBYDPM.REF3 = 
          TO_CHAR(SI.PAYMENT_NUM)
--------------------------------------------------------------------------------
 
--------------------------------------------------------------------------------
Plan name: SQL_PLAN_b9pnhmvyq89z252415c2f         Plan id: 1380015151
Enabled: YES     Fixed: NO      Accepted: YES     Origin: MANUAL-LOAD
--------------------------------------------------------------------------------

Plan hash value: 1132729352
 
--------------------------------------------------------------------------------------------------------------
| Id  | Operation                         | Name                     | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |                          |     1 |   148 |   390M  (1)|999:59:59 |
|   1 |  HASH GROUP BY                    |                          |     1 |   148 |   390M  (1)|999:59:59 |
|   2 |   NESTED LOOPS                    |                          |       |       |            |          |
|   3 |    NESTED LOOPS                   |                          |     1 |   148 |   390M  (1)|999:59:59 |
|   4 |     MERGE JOIN CARTESIAN          |                          |    86M|  9762M|    14M  (1)| 46:44:50 |
|   5 |      NESTED LOOPS                 |                          |       |       |            |          |
|   6 |       NESTED LOOPS                |                          | 58310 |  5238K|   124K  (1)| 00:24:50 |
|   7 |        TABLE ACCESS BY INDEX ROWID| AP_SELECTED_INVOICES_ALL | 58310 |  3188K|  7461   (1)| 00:01:30 |
|*  8 |         INDEX RANGE SCAN          | AP_SELECTED_INVOICES_N5  | 58310 |       |   473   (1)| 00:00:06 |
|*  9 |        INDEX UNIQUE SCAN          | AP_INVOICES_U1           |     1 |       |     1   (0)| 00:00:01 |
|* 10 |       TABLE ACCESS BY INDEX ROWID | AP_INVOICES_ALL          |     1 |    36 |     2   (0)| 00:00:01 |
|  11 |      BUFFER SORT                  |                          |  1488 | 38688 |    14M  (1)| 46:44:50 |
|  12 |       TABLE ACCESS BY INDEX ROWID | IBY_PAYMENTS_ALL         |  1488 | 38688 |   238   (0)| 00:00:03 |
|* 13 |        INDEX RANGE SCAN           | IBY_PAYMENTS_ALL_N10     |  1488 |       |    14   (0)| 00:00:01 |
|* 14 |     INDEX RANGE SCAN              | IBY_DOCS_PAYABLE_ALL_N6  |    50 |       |     2   (0)| 00:00:01 |
|* 15 |    TABLE ACCESS BY INDEX ROWID    | IBY_DOCS_PAYABLE_ALL     |     1 |    30 |    21   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   8 - access("SI"."CHECKRUN_NAME"=:B3)
   9 - access("INV"."INVOICE_ID"="SI"."INVOICE_ID")
  10 - filter("INV"."INVOICE_TYPE_LOOKUP_CODE"<>'INTEREST')
  13 - access("IBY_PAY"."COMPLETED_PMTS_GROUP_ID"=TO_NUMBER(:B2) AND 
              "IBY_PAY"."ORG_ID"=TO_NUMBER(:B1) AND "IBY_PAY"."ORG_TYPE"='OPERATING_UNIT')
  14 - access("IBY_PAY"."PAYMENT_ID"="IBY_DOCS"."PAYMENT_ID")
  15 - filter("IBY_DOCS"."CALLING_APP_DOC_UNIQUE_REF2"=TO_CHAR("INVOICE_ID") AND 
              "IBY_DOCS"."CALLING_APP_DOC_UNIQUE_REF1"=TO_CHAR("SI"."CHECKRUN_ID") AND 
              "IBY_DOCS"."CALLING_APP_DOC_UNIQUE_REF2"=TO_CHAR("INVOICE_ID") AND 
              "IBY_DOCS"."CALLING_APP_DOC_UNIQUE_REF3"=TO_CHAR("SI"."PAYMENT_NUM"))




Q:Why this plan is bad?
A:This is using Merge Join Cartesian and hash group by. For some reason Hash group by do not perform well at my site. This query too is not written well. Following is completely unnecessary.
  1. Inner query has group by on IBYDOCS.CALLING_APP_DOC_UNIQUE_REF1, IBYDOCS.CALLING_APP_DOC_UNIQUE_REF2, IBYDOCS.CALLING_APP_DOC_UNIQUE_REF3. CALLING_APP_DOC_UNIQUE_REF2 and CALLING_APP_DOC_UNIQUE_REF3 are invoice id and payment number. This is unique key to ap_payment_schedules_all table and one checkrun can not select one invoice payment schedule more than once, so group by will not do anything except return single row.
  2. Why is the condition "AND IBYDPM.REF2 = TO_CHAR(INV.INVOICE_ID)" needed? Such unnecessary joins confuse Oracle Optimizer.

In order to get better plan i added following hint to inline view query
/*+ use_nl (ibypmt ibydocs)*/

I added following hint to main query
/*+ leading (ibydpm) use_nl (ibydpm si) index ( si ap_selected_invoices_n5) use_no_hash */

then we did the following

  1. Execute query with hint so that a better plan is available to create it as baseline plan
  2. Load bad plan the sql
  3. Disable bad plan for sql
  4. Load good plan for sql generated by sql with hints

You can see this post for how to perform above steps.

This is the plan we got
Plan hash value: 3309865005
 
-------------------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name                     | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |                          |     1 |   148 |   228K  (1)| 00:45:48 |
|   1 |  HASH GROUP BY                   |                          |     1 |   148 |   228K  (1)| 00:45:48 |
|   2 |   NESTED LOOPS                   |                          |       |       |            |          |
|   3 |    NESTED LOOPS                  |                          |     1 |   148 |   228K  (1)| 00:45:48 |
|   4 |     NESTED LOOPS                 |                          |   229 | 25648 |   228K  (1)| 00:45:43 |
|   5 |      NESTED LOOPS                |                          | 73175 |  4001K|  6699   (1)| 00:01:21 |
|   6 |       TABLE ACCESS BY INDEX ROWID| IBY_PAYMENTS_ALL         |  1488 | 38688 |   239   (0)| 00:00:03 |
|*  7 |        INDEX RANGE SCAN          | IBY_PAYMENTS_ALL_N10     |  1488 |       |    15   (0)| 00:00:01 |
|   8 |       TABLE ACCESS BY INDEX ROWID| IBY_DOCS_PAYABLE_ALL     |    49 |  1470 |    21   (0)| 00:00:01 |
|*  9 |        INDEX RANGE SCAN          | IBY_DOCS_PAYABLE_ALL_N6  |    50 |       |     2   (0)| 00:00:01 |
|* 10 |      TABLE ACCESS BY INDEX ROWID | AP_SELECTED_INVOICES_ALL |     1 |    56 |     4   (0)| 00:00:01 |
|* 11 |       INDEX RANGE SCAN           | AP_SELECTED_INVOICES_N5  |     1 |       |     3   (0)| 00:00:01 |
|* 12 |     INDEX UNIQUE SCAN            | AP_INVOICES_U1           |     1 |       |     1   (0)| 00:00:01 |
|* 13 |    TABLE ACCESS BY INDEX ROWID   | AP_INVOICES_ALL          |     1 |    36 |     2   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   7 - access("IBY_PAY"."COMPLETED_PMTS_GROUP_ID"=TO_NUMBER(:B2) AND 
              "IBY_PAY"."ORG_ID"=TO_NUMBER(:B1) AND "IBY_PAY"."ORG_TYPE"='OPERATING_UNIT')
   9 - access("IBY_PAY"."PAYMENT_ID"="IBY_DOCS"."PAYMENT_ID")
  10 - filter("IBY_DOCS"."CALLING_APP_DOC_UNIQUE_REF1"=TO_CHAR("SI"."CHECKRUN_ID") AND 
              "IBY_DOCS"."CALLING_APP_DOC_UNIQUE_REF3"=TO_CHAR("SI"."PAYMENT_NUM"))
  11 - access("SI"."CHECKRUN_NAME"=:B3 AND "IBY_DOCS"."CALLING_APP_DOC_UNIQUE_REF2"=TO_CHAR("INVOICE_
              ID"))
  12 - access("INV"."INVOICE_ID"="SI"."INVOICE_ID")
       filter("IBY_DOCS"."CALLING_APP_DOC_UNIQUE_REF2"=TO_CHAR("INVOICE_ID"))
  13 - filter("INV"."INVOICE_TYPE_LOOKUP_CODE"<>'INTEREST')
 

No comments:

Post a Comment