简体   繁体   中英

I want to optimize a stored procedure that uses IN clause and a regex_str function. I am not sure that how I should optimize it more?

The response time I am getting is around 200ms. I want to optimize it more. How can I achieve this?

CREATE OR REPLACE
PROCEDURE GETSTORES
(
LISTOFOFFERIDS IN VARCHAR2,
REF_OFFERS OUT TYPES.OFFER_RECORD_CURSOR
)
AS
BEGIN
  OPEN REF_OFFERS FOR
  SELECT
  /*+ PARALLEL(STORES 5) PARALLEL(MERCHANTOFFERS 5)*/
  MOFF.OFFERID,
  s.STOREID,
  S.LAT,
  s.LNG
  FROM
  MERCHANTOFFERS MOFF
  INNER JOIN STORES s ON MOFF.STOREID =S.STOREID
  WHERE
  MOFF.OFFERID IN
  (
    SELECT
      REGEXP_SUBSTR(LISTOFOFFERIDS,'[^,]+', 1, LEVEL)
    FROM
      DUAL CONNECT BY REGEXP_SUBSTR(LISTOFOFFERIDS, '[^,]+', 1, LEVEL) IS NOT NULL
  )
  ;
END
GETSTORES;

I am using the regex_substr to get a list of OfferIDs from the comma separated string that comes in LISTOFOFFERIDS. I have created the index on STOREID of the Stores table but to no avail. A new approach to achieve the same is also fine if its faster.

The types declaration for the same:

create or replace

    PACKAGE TYPES
    AS
    TYPE OFFER_RECORD
    IS
      RECORD(
      OFFER_ID MERCHANTOFFERS.OFFERID%TYPE,
      STORE_ID STORES.STOREID%TYPE,
      LAT STORES.LAT%TYPE,
      LNG STORES.LNG%TYPE
      );
    TYPE OFFER_RECORD_CURSOR
    IS
      REF
      CURSOR
        RETURN OFFER_RECORD;
      END
      TYPES;

The plan for the select reveals following information:

Plan hash value: 1501040938

-------------------------------------------------------------------------------------------------------------
| Id  | Operation                                  | Name           | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                           |                |   276 | 67620 |    17  (12)| 00:00:01 |
|*  1 |  HASH JOIN                                 |                |   276 | 67620 |    17  (12)| 00:00:01 |
|   2 |   NESTED LOOPS                             |                |       |       |            |          |
|   3 |    NESTED LOOPS                            |                |   276 | 61272 |     3  (34)| 00:00:01 |
|   4 |     VIEW                                   | VW_NSO_1       |     1 |   202 |     3  (34)| 00:00:01 |
|   5 |      HASH UNIQUE                           |                |     1 |       |     3  (34)| 00:00:01 |
|*  6 |       CONNECT BY WITHOUT FILTERING (UNIQUE)|                |       |       |            |          |
|   7 |        FAST DUAL                           |                |     1 |       |     2   (0)| 00:00:01 |
|*  8 |     INDEX RANGE SCAN                       | OFFERID_INDEX  |   276 |       |     0   (0)| 00:00:01 |
|   9 |    TABLE ACCESS BY INDEX ROWID             | MERCHANTOFFERS |   276 |  5520 |     0   (0)| 00:00:01 |
|  10 |   TABLE ACCESS FULL                        | STORES         |  9947 |   223K|    13   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------------

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

   1 - access("MERCHANTOFFERS"."STOREID"="STORES"."STOREID")
   6 - filter( REGEXP_SUBSTR ('M1-Off2,M2-Off5,M2-Off9,M5-Off4,M10-Off1,M1-Off3,M2-Off4,M3-Off2,M4-Of
              f6,M5-Off1,M6-Off1,M8-Off1,M7-Off3,M1-Off1,M2-Off1,M3-Off1,M3-Off4,M3-Off5,M3-Off6,M4-Off1,M4-Off7,M2
              -Off2,M3-Off3,M5-Off2,M7-Off1,M7-Off2,M1-Off7,M2-Off3,M3-Off7,M5-Off5,M4-Off2,M4-Off3,M4-Off5,M8-Off2
              ,M6-Off2,M1-Off5,M1-Off6,M1-Off9,M1-Off8,M2-Off6,M2-Off7,M4-Off4,M9-Off1,M6-Off4,M1-Off4,M1-Off10,M2-
              Off8,M3-Off8,M6-Off3,M5-Off3','[^,]+',1,LEVEL) IS NOT NULL)
   8 - access("MERCHANTOFFERS"."OFFERID"="$kkqu_col_1")
  1. If your server supports it (seems you want it), change the hints into /*+ PARALLEL(S 8) PARALLEL(MOFF 8)*/ . When you have aliases you must use the aliases in the hints.
  2. you should try the compound index suggested by APC( STORES(STOREID, LAT, LNG) )
  3. Please respond to the questions: For the example presented, how many distinct stores you get ( select count(distinct storeid) from (your_query) ) and how many stores are in the STORES table? ( Select count(*) from Stores )?
  4. Have you analysed the table with dbms_stats.gather_table_stats ?
  5. I believe the connect by query is NOT the problem. It runs in 0.02 seconds.

If you look at you explain plan the timings for each step are the same: there is no obvious candidate to focus on tuning.

The sample you posted has fifty tokens for OFFERID. Is that representative? They map to 276 STORES - is that a representative ratio? Do any offers hit more than one Store?

276 rows is about 2.7% of the rows which is a small-ish sliver: however, as STORES seems to be a very compact table it's marginal as to whether indexed reads would be faster than a full table scan.

The only obvious thing you could do to squeeze more juice out of the database would be to build a compound index on STORES(STOREID, LAT, LNG); presumably it's not a table which sees much DML so the overhead of an additional index wouldn't be much.

One last point: your query executes in 0.2s. So how much faster do you want it to go?

Consider dropping the regex on the join, so the join can happen fast.
If there are indexes on the join columns, chances are the join may move from nested loops to a hashed join of some sort.

Once you have that result set (with hopefully fewer rows), then filter it with your regex.

You may find that the WITH statement helpful in this scenerio.

Something on the order of this. ( untested example )

WITH
base AS
(
    SELECT /*+ PARALLEL(STORES 5) PARALLEL(MERCHANTOFFERS 5) */
           moff.OFFERID,
              s.STOREID,
              s.LAT,
              s.LNG
    FROM MERCHANTOFFERS moff
    INNER JOIN STORES s 
    ON MOFF.STOREID = S.STOREID
),
offers AS
(
    SELECT REGEXP_SUBSTR(LISTOFOFFERIDS,'[^,]+', 1, LEVEL) offerid
    FROM DUAL 
    CONNECT BY REGEXP_SUBSTR(LISTOFOFFERIDS, '[^,]+', 1, LEVEL) IS NOT NULL
)
SELECT base.*
FROM base,
     offers
WHERE base.offerid = offers.offerid

Oracle may execute the two views into in memory tables, then join.

No guarentees. Your milage may vary. You were looking for ideas. This is an idea. The very best of luck to you.

If I recall a hints chapter correctly, when you alias your table names, you need to use that alias in your hint. /*+ PARALLEL(s 5) PARALLEL(moff 5) */

I would be curious as to why you decided on the value 5 for your hints. I was under the impression that Oracle would chose a best value for it, depending on system load and other mysterious conditions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM