Home » RDBMS Server » Performance Tuning » How to reduce the consistent gets of this sql (Oracle 10.2.0.4.0 64bit , AIX 5.3)
How to reduce the consistent gets of this sql [message #513370] |
Mon, 27 June 2011 02:21 |
snowball
Messages: 229 Registered: April 2006 Location: China
|
Senior Member |
|
|
Hi, guys
How can I reduce the consistents gets of the sql:
select b.OFFER_ID,
b.OFFER_CODE,
b.OFFER_NAME,
b.OFFER_COMMENTS,
b.BAND_ID,
b.CAN_BE_BUY_ALONE,
b.PRICING_PLAN_ID,
b.PRIORITY,
b.STATE,
to_char(b.EFF_DATE, 'yyyymmdd hh24:mi:ss'),
to_char(b.EXP_DATE, 'yyyymmdd hh24:mi:ss'),
to_char(b.BEGIN_DATE, 'yyyymmdd hh24:mi:ss'),
to_char(b.END_DATE, 'yyyymmdd hh24:mi:ss'),
b.REDOFLAG,
b.MIN_NUM,
b.MAX_NUM,
b.EXP_DATE_OFFSET,
b.EXP_DATE_OFFSET_UNIT,
b.USER_RANGE,
b.OFFER_ATTR_TYPE,
b.OFFER_TYPE,
to_char(b.STATE_CHG_DATE, 'yyyymmdd hh24:mi:ss'),
to_char(b.OFFER_CRT_DATE, 'yyyymmdd hh24:mi:ss'),
b.GROUP_TYPE_ID,
nvl(b.GROUP_LIMIT_RULE, 0),
b.PROD_OFFER_PUBLISHER,
b.CUSTOM_REQUEST,
b.OPERATION_MODLE,
a.GROUP_ID,
a.REGION_NAME,
c.pricing_desc,
d.band_name,
b.eff_type,
b.exp_type,
b.MIN_ORDER_NUM,
b.MAX_ORDER_NUM,
b.IF_CUST_PRICE
from REGION a, PRODUCT_OFFER b, PRICING_PLAN c, BAND d
where a.offer_id = b.Offer_Id
and b.pricing_plan_id = c.pricing_plan_id
and b.band_id = d.band_id
and b.state = 'A'
and b.Eff_Date < sysdate and
b.exp_date > sysdate
and b.begin_date < sysdate
and b.end_date > sysdate
and '#10030#10044#200109#218265#' like '%#' || a.group_id || '#%'
and to_number(a.Right_Limit) <= 0
--and (a.Chn_Class_Limit like '%#' || || '#%' or
--trim(:channel_segment) is null)
and 0 = Offer_Login_Limit_Check(b.offer_id,'nf0S01');
Offer_Login_Limit_Check is a function of check the valid user login.
The function definition :
CREATE OR REPLACE FUNCTION OFFER_LOGIN_LIMIT_CHECK
(
IN_OFFER_ID PRODUCT_OFFER.OFFER_ID%TYPE,
IS_LOGIN_NO DLOGINMSG.LOGIN_NO%TYPE
) RETURN NUMBER IS
RETNUM NUMBER;
VN_COUNTS NUMBER;
BEGIN
RETNUM := 0;
VN_COUNTS := 0;
IF is_login_no is NULL then
RETURN 0;
END if;
SELECT MAX (cnum) INTO VN_COUNTS
FROM (SELECT COUNT (0) cnum
FROM SBANDOFFER
WHERE ELEMENT_CODE = IS_LOGIN_NO
AND ELEMENT_TYPE = 'gh'
AND OFFER_ID = IN_OFFER_ID
AND BAND_TYPE = 80
AND STATE = '1'
AND EFFDATE <= SYSDATE
AND EXPDATE >= SYSDATE
UNION
SELECT COUNT (0) cnum
FROM dloginrolerel a, sbandoffer b
WHERE a.login_no = IS_LOGIN_NO
AND a.power_code = b.element_code
AND element_type = 'js'
AND BAND_TYPE = 80
AND OFFER_ID = IN_OFFER_ID
AND STATE = '1'
AND EFFDATE <= SYSDATE
AND EXPDATE >= SYSDATE);
IF VN_COUNTS > 0 THEN
RETNUM := 1;
END IF;
RETURN RETNUM;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN - SQLCODE;
WHEN OTHERS THEN
RETURN SQLCODE;
END OFFER_LOGIN_LIMIT_CHECK;
The execution plan of the sql:
Execution Plan
----------------------------------------------------------
Plan hash value: 894788602
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 134 | 36314 | 150 (7)| 00:00:02 |
|* 1 | HASH JOIN | | 134 | 36314 | 150 (7)| 00:00:02 |
| 2 | TABLE ACCESS FULL | BAND | 55 | 660 | 3 (0)| 00:00:01 |
|* 3 | HASH JOIN | | 134 | 34706 | 146 (7)| 00:00:02 |
| 4 | MERGE JOIN | | 157 | 34540 | 111 (8)| 00:00:02 |
|* 5 | TABLE ACCESS BY INDEX ROWID | PRODUCT_OFFER | 156 | 29016 | 96 (8)| 00:00:02 |
|* 6 | INDEX FULL SCAN | PRODUCT_OFFER_IDX | 224 | | 80 (9)| 00:00:01 |
|* 7 | SORT JOIN | | 226 | 7684 | 15 (7)| 00:00:01 |
|* 8 | TABLE ACCESS BY INDEX ROWID| REGION | 226 | 7684 | 14 (0)| 00:00:01 |
|* 9 | INDEX RANGE SCAN | IDX_REGION_FUN | 819 | | 3 (0)| 00:00:01 |
| 10 | INDEX FAST FULL SCAN | IDX_REGION_COM_1 | 18191 | 692K| 34 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("B"."BAND_ID"="D"."BAND_ID")
3 - access("B"."PRICING_PLAN_ID"="C"."PRICING_PLAN_ID")
5 - filter("B"."STATE"='A' AND "B"."END_DATE">SYSDATE@! AND "B"."EXP_DATE">SYSDATE@! AND
"B"."EFF_DATE"<SYSDATE@! AND "B"."BEGIN_DATE"<SYSDATE@!)
6 - filter("OFFER_LOGIN_LIMIT_CHECK"("B"."OFFER_ID",'nf0S01')=0)
7 - access("A"."OFFER_ID"="B"."OFFER_ID")
filter("A"."OFFER_ID"="B"."OFFER_ID")
8 - filter('#10030#10044#200109#218265#' LIKE '%#'||"A"."GROUP_ID"||'#%')
9 - access(TO_NUMBER("RIGHT_LIMIT")<=0)
Statistics
----------------------------------------------------------
22524 recursive calls
0 db block gets
138064 consistent gets
0 physical reads
0 redo size
264416 bytes sent via SQL*Net to client
2109 bytes received via SQL*Net from client
149 SQL*Net roundtrips to/from client
22525 sorts (memory)
0 sorts (disk)
2206 rows processed
The sql trace of this sql:
********************************************************************************
select b.OFFER_ID,
b.OFFER_CODE,
b.OFFER_NAME,
b.OFFER_COMMENTS,
b.BAND_ID,
b.CAN_BE_BUY_ALONE,
b.PRICING_PLAN_ID,
b.PRIORITY,
b.STATE,
to_char(b.EFF_DATE, 'yyyymmdd hh24:mi:ss'),
to_char(b.EXP_DATE, 'yyyymmdd hh24:mi:ss'),
to_char(b.BEGIN_DATE, 'yyyymmdd hh24:mi:ss'),
to_char(b.END_DATE, 'yyyymmdd hh24:mi:ss'),
b.REDOFLAG,
b.MIN_NUM,
b.MAX_NUM,
b.EXP_DATE_OFFSET,
b.EXP_DATE_OFFSET_UNIT,
b.USER_RANGE,
b.OFFER_ATTR_TYPE,
b.OFFER_TYPE,
to_char(b.STATE_CHG_DATE, 'yyyymmdd hh24:mi:ss'),
to_char(b.OFFER_CRT_DATE, 'yyyymmdd hh24:mi:ss'),
b.GROUP_TYPE_ID,
nvl(b.GROUP_LIMIT_RULE, 0),
b.PROD_OFFER_PUBLISHER,
b.CUSTOM_REQUEST,
b.OPERATION_MODLE,
a.GROUP_ID,
a.REGION_NAME,
c.pricing_desc,
d.band_name,
b.eff_type,
b.exp_type,
b.MIN_ORDER_NUM,
b.MAX_ORDER_NUM,
b.IF_CUST_PRICE
from REGION a, PRODUCT_OFFER b, PRICING_PLAN c, BAND d
where a.offer_id = b.Offer_Id
and b.pricing_plan_id = c.pricing_plan_id
and b.band_id = d.band_id
and b.state = 'A'
and b.Eff_Date < sysdate and
b.exp_date > sysdate
and b.begin_date < sysdate
and b.end_date > sysdate
and '#10030#10044#200109#218265#' like '%#' || a.group_id || '#%'
and to_number(a.Right_Limit) <= 0
--and (a.Chn_Class_Limit like '%#' || || '#%' or
--trim(:channel_segment) is null)
and 0 = Offer_Login_Limit_Check(b.offer_id,'nf0S01')
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.04 0.05 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 149 5.01 5.70 0 2920 0 2206
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 151 5.05 5.75 0 2920 0 2206
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 77
Rows Row Source Operation
------- ---------------------------------------------------
2206 HASH JOIN (cr=138064 pr=0 pw=0 time=21087739 us)
56 TABLE ACCESS FULL BAND (cr=7 pr=0 pw=0 time=226 us)
2243 HASH JOIN (cr=138057 pr=0 pw=0 time=21180773 us)
2243 MERGE JOIN (cr=137764 pr=0 pw=0 time=5030939 us)
16318 TABLE ACCESS BY INDEX ROWID PRODUCT_OFFER (cr=136792 pr=0 pw=0 time=16775468 us)
22524 INDEX FULL SCAN PRODUCT_OFFER_IDX (cr=135218 pr=0 pw=0 time=19303965 us)(object id 176928)
2243 SORT JOIN (cr=972 pr=0 pw=0 time=1322567 us)
5294 TABLE ACCESS BY INDEX ROWID REGION (cr=972 pr=0 pw=0 time=1166350 us)
82473 INDEX RANGE SCAN IDX_REGION_FUN (cr=151 pr=0 pw=0 time=164992 us)(object id 241945)
20696 INDEX FAST FULL SCAN IDX_REGION_COM_1 (cr=293 pr=0 pw=0 time=41547 us)(object id 241946)
Elapsed times include waiting on following events:
Event waited on Times Max. Wait Total Waited
---------------------------------------- Waited ---------- ------------
SQL*Net message to client 149 0.00 0.00
SQL*Net more data to client 36 0.00 0.00
SQL*Net message from client 149 0.00 0.65
********************************************************************************
The execution plan of this function( OFFER_LOGIN_LIMIT_CHECK):
SQL> select OFFER_LOGIN_LIMIT_CHECK(20097,'nf0S01') from dual;
Execution Plan
----------------------------------------------------------
Plan hash value: 1546270724
-----------------------------------------------------------------
| Id | Operation | Name | Rows | Cost (%CPU)| Time |
-----------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 2 (0)| 00:00:01 |
| 1 | FAST DUAL | | 1 | 2 (0)| 00:00:01 |
-----------------------------------------------------------------
Statistics
----------------------------------------------------------
2 recursive calls
0 db block gets
6 consistent gets
0 physical reads
0 redo size
545 bytes sent via SQL*Net to client
492 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
1 rows processed
The sql trace of function( OFFER_LOGIN_LIMIT_CHECK) :
SELECT MAX (CNUM)
FROM
(SELECT COUNT (0) CNUM FROM SBANDOFFER WHERE ELEMENT_CODE = :B2 AND
ELEMENT_TYPE = 'gh' AND OFFER_ID = :B1 AND BAND_TYPE = 80 AND STATE = '1'
AND EFFDATE <= SYSDATE AND EXPDATE >= SYSDATE UNION SELECT COUNT (0) CNUM
FROM DLOGINROLEREL A, SBANDOFFER B WHERE A.LOGIN_NO = :B2 AND A.POWER_CODE =
B.ELEMENT_CODE AND ELEMENT_TYPE = 'js' AND BAND_TYPE = 80 AND OFFER_ID =
:B1 AND STATE = '1' AND EFFDATE <= SYSDATE AND EXPDATE >= SYSDATE)
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 0 0.00 0.00 0 0 0 0
Execute 22524 13.68 13.00 0 0 0 0
Fetch 22524 2.80 2.50 0 135144 0 22524
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 45048 16.48 15.50 0 135144 0 22524
Misses in library cache during parse: 0
Optimizer mode: ALL_ROWS
Parsing user id: 77 (recursive depth: 1)
Here are some tables and their records:
region has 91,007 records
product_offer has 22,523 records
pricing_plan has 20,695 records
brand has 56 records
sbandoffer has 2 records
dloginrolerel(synonyms) has 28,417 records
I have already added the indexes for the outer sql to reduce the cost from 374 to 150.
But the the consistent gets seems not reduce that much.
I notice that the function will cost 6 consistents gets per execution. It seems it not very high.
But if this function goes with the sql, it will cost a very high consistent gets, if I remove this function, the sql only cost 1903 consistents gets.
So what I am thinking there should be two ways to reduce the consistent gets:
The first one is reduce the recursive call of the sql.
The second is reduce the consistent gets of the function. (but it seems that the consistent gets in this function is very low, only 6.)
Any ideas or suggestions, thanks very much.
BR,
Milo
|
|
|
Re: How to reduce the consistent gets of this sql [message #513377 is a reply to message #513370] |
Mon, 27 June 2011 02:57 |
hkchital
Messages: 128 Registered: September 2008 Location: Singapore
|
Senior Member |
|
|
PRODUCT_OFFER returns 22,524 rows causing the function to be executed 22,524 times.
That is why the consistent gets for a single execution of the SQL query is high
You need to merge the function's sql code into the query itself.
Hemant K Chitale
|
|
|
|
Re: How to reduce the consistent gets of this sql [message #513384 is a reply to message #513381] |
Mon, 27 June 2011 03:47 |
cookiemonster
Messages: 13958 Registered: September 2008 Location: Rainy Manchester
|
Senior Member |
|
|
Since pretty much all your consistent gets are coming from the function call what other way do you think there may be?
You can't noticebly reduce the gets of the main query or the query in the function. So that leaves the number of times the function is called.
Hemant's suggestion is the only viable one.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Re: How to reduce the consistent gets of this sql [message #513453 is a reply to message #513381] |
Mon, 27 June 2011 10:13 |
hkchital
Messages: 128 Registered: September 2008 Location: Singapore
|
Senior Member |
|
|
> Because this function will called by many applications, and many applications has used this sql.
>so maybe it can not be removed or replace in this sql directly in current scenario.
I am not asking you to drop the function. The function may well make sense for use in multiple other points in the application where it may be used against only a few rows.
It is in *this* query that you shouldn't be using the function but merging it into the query.
Hemant K Chitale
[Updated on: Mon, 27 June 2011 10:14] Report message to a moderator
|
|
|
Re: How to reduce the consistent gets of this sql [message #513501 is a reply to message #513453] |
Mon, 27 June 2011 21:44 |
snowball
Messages: 229 Registered: April 2006 Location: China
|
Senior Member |
|
|
I have create the composite index on pricing_plan_id, band_id,state,Eff_Date,exp_date,begin_date,end_date, but the original sql will not use this index.
So I use hint to see, but the cost goes to very high. "TABLE ACCESS BY INDEX ROWID" after the index full scan raise the cost. And the recursive calls and consistent gets goes to lower.
SQL> r
1 select /*+ index(b idx_product_offer_com2) */ b.OFFER_ID,
2 b.OFFER_CODE,
3 b.OFFER_NAME,
4 b.OFFER_COMMENTS,
5 b.BAND_ID,
6 b.CAN_BE_BUY_ALONE,
7 b.PRICING_PLAN_ID,
8 b.PRIORITY,
9 b.STATE,
10 to_char(b.EFF_DATE, 'yyyymmdd hh24:mi:ss'),
11 to_char(b.EXP_DATE, 'yyyymmdd hh24:mi:ss'),
12 to_char(b.BEGIN_DATE, 'yyyymmdd hh24:mi:ss'),
13 to_char(b.END_DATE, 'yyyymmdd hh24:mi:ss'),
14 b.REDOFLAG,
15 b.MIN_NUM,
16 b.MAX_NUM,
17 b.EXP_DATE_OFFSET,
18 b.EXP_DATE_OFFSET_UNIT,
19 b.USER_RANGE,
20 b.OFFER_ATTR_TYPE,
21 b.OFFER_TYPE,
22 to_char(b.STATE_CHG_DATE, 'yyyymmdd hh24:mi:ss'),
23 to_char(b.OFFER_CRT_DATE, 'yyyymmdd hh24:mi:ss'),
24 b.GROUP_TYPE_ID,
25 nvl(b.GROUP_LIMIT_RULE, 0),
26 b.PROD_OFFER_PUBLISHER,
27 b.CUSTOM_REQUEST,
28 b.OPERATION_MODLE,
29 a.GROUP_ID,
30 a.REGION_NAME,
31 c.pricing_desc,
32 d.band_name,
33 b.eff_type,
34 b.exp_type,
35 b.MIN_ORDER_NUM,
36 b.MAX_ORDER_NUM,
37 b.IF_CUST_PRICE
38 from REGION a, PRODUCT_OFFER b, PRICING_PLAN c, BAND d
39 where a.offer_id = b.Offer_Id
40 and b.pricing_plan_id = c.pricing_plan_id
41 and b.band_id = d.band_id
42 and b.state = 'A'
43 and b.Eff_Date < sysdate and
44 b.exp_date > sysdate
45 and b.begin_date < sysdate
46 and b.end_date > sysdate
47 and '#10030#10044#200109#218265#' like '%#' || a.group_id || '#%'
48 and to_number(a.Right_Limit) <= 0
49 --and (a.Chn_Class_Limit like '%#' || || '#%' or
50 --trim(:channel_segment) is null)
51* and 0 = Offer_Login_Limit_Check_TEST1(b.offer_id,'nf0S01')
2206 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3806189357
---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 134 | 36314 | 5981 (1)| 00:01:12 |
|* 1 | HASH JOIN | | 134 | 36314 | 5981 (1)| 00:01:12 |
|* 2 | TABLE ACCESS BY INDEX ROWID | REGION | 228 | 7752 | 14 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_REGION_FUN | 819 | | 3 (0)| 00:00:01 |
|* 4 | HASH JOIN | | 134 | 31758 | 5966 (1)| 00:01:12 |
| 5 | TABLE ACCESS FULL | BAND | 55 | 660 | 3 (0)| 00:00:01 |
|* 6 | HASH JOIN | | 134 | 30150 | 5962 (1)| 00:01:12 |
|* 7 | TABLE ACCESS BY INDEX ROWID| PRODUCT_OFFER | 156 | 29016 | 5927 (1)| 00:01:12 |
|* 8 | INDEX FULL SCAN | IDX_PRODUCT_OFFER_COM2 | 15643 | | 178 (2)| 00:00:03 |
| 9 | INDEX FAST FULL SCAN | IDX_REGION_COM_1 | 18191 | 692K| 34 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("A"."OFFER_ID"="B"."OFFER_ID")
2 - filter('#10030#10044#200109#218265#' LIKE '%#'||"A"."GROUP_ID"||'#%')
3 - access(TO_NUMBER("RIGHT_LIMIT")<=0)
4 - access("B"."BAND_ID"="D"."BAND_ID")
6 - access("B"."PRICING_PLAN_ID"="C"."PRICING_PLAN_ID")
7 - filter("OFFER_LOGIN_LIMIT_CHECK_TEST1"("B"."OFFER_ID",'nf0S01')=0)
8 - access("B"."STATE"='A' AND "B"."EXP_DATE">SYSDATE@! AND "B"."END_DATE">SYSDATE@! AND
"B"."EFF_DATE"<SYSDATE@! AND "B"."BEGIN_DATE"<SYSDATE@!)
filter("B"."STATE"='A' AND "B"."END_DATE">SYSDATE@! AND "B"."EXP_DATE">SYSDATE@! AND
"B"."EFF_DATE"<SYSDATE@! AND "B"."BEGIN_DATE"<SYSDATE@!)
Statistics
----------------------------------------------------------
16410 recursive calls
0 db block gets
105928 consistent gets
174 physical reads
0 redo size
264659 bytes sent via SQL*Net to client
2109 bytes received via SQL*Net from client
149 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
2206 rows processed
so I try to add offer_id in the composite index, it goes much better, but still it's not best base on the cost value, but the consistent gets and recursive calls lower about 20% compare with the original one.
SQL> r
1 select /*+ index(b idx_product_offer_com2) */ b.OFFER_ID,
2 b.OFFER_CODE,
3 b.OFFER_NAME,
4 b.OFFER_COMMENTS,
5 b.BAND_ID,
6 b.CAN_BE_BUY_ALONE,
7 b.PRICING_PLAN_ID,
8 b.PRIORITY,
9 b.STATE,
10 to_char(b.EFF_DATE, 'yyyymmdd hh24:mi:ss'),
11 to_char(b.EXP_DATE, 'yyyymmdd hh24:mi:ss'),
12 to_char(b.BEGIN_DATE, 'yyyymmdd hh24:mi:ss'),
13 to_char(b.END_DATE, 'yyyymmdd hh24:mi:ss'),
14 b.REDOFLAG,
15 b.MIN_NUM,
16 b.MAX_NUM,
17 b.EXP_DATE_OFFSET,
18 b.EXP_DATE_OFFSET_UNIT,
19 b.USER_RANGE,
20 b.OFFER_ATTR_TYPE,
21 b.OFFER_TYPE,
22 to_char(b.STATE_CHG_DATE, 'yyyymmdd hh24:mi:ss'),
23 to_char(b.OFFER_CRT_DATE, 'yyyymmdd hh24:mi:ss'),
24 b.GROUP_TYPE_ID,
25 nvl(b.GROUP_LIMIT_RULE, 0),
26 b.PROD_OFFER_PUBLISHER,
27 b.CUSTOM_REQUEST,
28 b.OPERATION_MODLE,
29 a.GROUP_ID,
30 a.REGION_NAME,
31 c.pricing_desc,
32 d.band_name,
33 b.eff_type,
34 b.exp_type,
35 b.MIN_ORDER_NUM,
36 b.MAX_ORDER_NUM,
37 b.IF_CUST_PRICE
38 from REGION a, PRODUCT_OFFER b, PRICING_PLAN c, BAND d
39 where a.offer_id = b.Offer_Id
40 and b.pricing_plan_id = c.pricing_plan_id
41 and b.band_id = d.band_id
42 and b.state = 'A'
43 and b.Eff_Date < sysdate and
44 b.exp_date > sysdate
45 and b.begin_date < sysdate
46 and b.end_date > sysdate
47 and '#10030#10044#200109#218265#' like '%#' || a.group_id || '#%'
48 and to_number(a.Right_Limit) <= 0
49 --and (a.Chn_Class_Limit like '%#' || || '#%' or
50 --trim(:channel_segment) is null)
51* and 0 = Offer_Login_Limit_Check_TEST1(b.offer_id,'nf0S01')
2206 rows selected.
Execution Plan
----------------------------------------------------------
Plan hash value: 3806189357
---------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 134 | 36314 | 262 (4)| 00:00:04 |
|* 1 | HASH JOIN | | 134 | 36314 | 262 (4)| 00:00:04 |
|* 2 | TABLE ACCESS BY INDEX ROWID | REGION | 228 | 7752 | 14 (0)| 00:00:01 |
|* 3 | INDEX RANGE SCAN | IDX_REGION_FUN | 819 | | 3 (0)| 00:00:01 |
|* 4 | HASH JOIN | | 134 | 31758 | 247 (3)| 00:00:03 |
| 5 | TABLE ACCESS FULL | BAND | 55 | 660 | 3 (0)| 00:00:01 |
|* 6 | HASH JOIN | | 134 | 30150 | 244 (3)| 00:00:03 |
| 7 | TABLE ACCESS BY INDEX ROWID| PRODUCT_OFFER | 156 | 29016 | 209 (3)| 00:00:03 |
|* 8 | INDEX FULL SCAN | IDX_PRODUCT_OFFER_COM2 | 156 | | 197 (3)| 00:00:03 |
| 9 | INDEX FAST FULL SCAN | IDX_REGION_COM_1 | 18191 | 692K| 34 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("A"."OFFER_ID"="B"."OFFER_ID")
2 - filter('#10030#10044#200109#218265#' LIKE '%#'||"A"."GROUP_ID"||'#%')
3 - access(TO_NUMBER("RIGHT_LIMIT")<=0)
4 - access("B"."BAND_ID"="D"."BAND_ID")
6 - access("B"."PRICING_PLAN_ID"="C"."PRICING_PLAN_ID")
8 - access("B"."STATE"='A' AND "B"."EXP_DATE">SYSDATE@! AND "B"."END_DATE">SYSDATE@! AND
"B"."EFF_DATE"<SYSDATE@! AND "B"."BEGIN_DATE"<SYSDATE@!)
filter("B"."STATE"='A' AND "OFFER_LOGIN_LIMIT_CHECK_TEST1"("B"."OFFER_ID",'nf0S01')=0 AND
"B"."END_DATE">SYSDATE@! AND "B"."EXP_DATE">SYSDATE@! AND "B"."EFF_DATE"<SYSDATE@! AND
"B"."BEGIN_DATE"<SYSDATE@!)
Statistics
----------------------------------------------------------
16338 recursive calls
0 db block gets
100895 consistent gets
0 physical reads
0 redo size
264491 bytes sent via SQL*Net to client
2109 bytes received via SQL*Net from client
149 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
2206 rows processed
The composite index full scan cost more than the unique index on offer_id.
Could I do something to lower this cost so it will use this execution plan and without using hint?
Thanks very much,
Milo
|
|
|
|
|
|
|
Re: How to reduce the consistent gets of this sql [message #513560 is a reply to message #513548] |
Tue, 28 June 2011 04:15 |
snowball
Messages: 229 Registered: April 2006 Location: China
|
Senior Member |
|
|
Hi, guys
When I trying to do the equal logic. An error was showed, while it told me 'ORA-00904: "B"."OFFER_ID": invalid identifier', but the b.offer_id is used in main query.
The function definition:
CREATE OR REPLACE FUNCTION OFFER_LOGIN_LIMIT_CHECK
(
IN_OFFER_ID PRODUCT_OFFER.OFFER_ID%TYPE,
IS_LOGIN_NO DLOGINMSG.LOGIN_NO%TYPE
) RETURN NUMBER IS
RETNUM NUMBER;
VN_COUNTS NUMBER;
BEGIN
RETNUM := 0;
VN_COUNTS := 0;
IF is_login_no is NULL then
RETURN 0;
END if;
SELECT MAX (cnum) INTO VN_COUNTS
FROM (SELECT COUNT (0) cnum
FROM SBANDOFFER
WHERE ELEMENT_CODE = IS_LOGIN_NO
AND ELEMENT_TYPE = 'gh'
AND OFFER_ID = IN_OFFER_ID
AND BAND_TYPE = 80
AND STATE = '1'
AND EFFDATE <= SYSDATE
AND EXPDATE >= SYSDATE
UNION
SELECT COUNT (0) cnum
FROM dloginrolerel a, sbandoffer b
WHERE a.login_no = IS_LOGIN_NO
AND a.power_code = b.element_code
AND element_type = 'js'
AND BAND_TYPE = 80
AND OFFER_ID = IN_OFFER_ID
AND STATE = '1'
AND EFFDATE <= SYSDATE
AND EXPDATE >= SYSDATE);
IF VN_COUNTS > 0 THEN
RETNUM := 1;
END IF;
RETURN RETNUM;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN - SQLCODE;
WHEN OTHERS THEN
RETURN SQLCODE;
END OFFER_LOGIN_LIMIT_CHECK;
I do the equal logic like this:
( 0 =
(SELECT MAX (cnum)
FROM (SELECT COUNT (0) cnum
FROM SBANDOFFER
WHERE ELEMENT_CODE = 'nf0S01'
AND ELEMENT_TYPE = 'gh'
AND OFFER_ID = b.offer_id
AND BAND_TYPE = 80
AND STATE = '1'
AND EFFDATE <= SYSDATE
AND EXPDATE >= SYSDATE
UNION ALL
SELECT COUNT (0) cnum
FROM dloginrolerel e, sbandoffer f
WHERE e.login_no = 'nf0S01'
AND e.power_code = f.element_code
AND element_type = 'js'
AND BAND_TYPE = 80
AND OFFER_ID = b.offer_id
AND STATE = '1'
AND EFFDATE <= SYSDATE
AND EXPDATE >= SYSDATE ))
);
Thanks.
Milo
[Updated on: Tue, 28 June 2011 04:24] Report message to a moderator
|
|
|
|
|
|
|
|
Re: How to reduce the consistent gets of this sql [message #513705 is a reply to message #513560] |
Wed, 29 June 2011 01:44 |
hkchital
Messages: 128 Registered: September 2008 Location: Singapore
|
Senior Member |
|
|
You are obtaining two counts. If either count is non-zero, the Max is non-zero so the comparison to 0 fails.
Therefore, what you need to verify is that *both* counts are 0.
Which could be written as :
SELECT .... main query ...
WHERE ... main query ......
AND
(
0 =
(SELECT COUNT (0) cnum
FROM SBANDOFFER
WHERE ELEMENT_CODE = IS_LOGIN_NO
AND ELEMENT_TYPE = 'gh'
AND OFFER_ID = IN_OFFER_ID
AND BAND_TYPE = 80
AND STATE = '1'
AND EFFDATE <= SYSDATE
AND EXPDATE >= SYSDATE)
AND
0 =
(SELECT COUNT (0) cnum
FROM dloginrolerel a, sbandoffer b
WHERE a.login_no = IS_LOGIN_NO
AND a.power_code = b.element_code
AND element_type = 'js'
AND BAND_TYPE = 80
AND OFFER_ID = IN_OFFER_ID
AND STATE = '1'
AND EFFDATE <= SYSDATE
AND EXPDATE >= SYSDATE)
)
Hemant K Chitale
|
|
|
|
|
|
|
|
|
Re: How to reduce the consistent gets of this sql [message #513764 is a reply to message #513762] |
Wed, 29 June 2011 05:46 |
cookiemonster
Messages: 13958 Registered: September 2008 Location: Rainy Manchester
|
Senior Member |
|
|
The cost was always likely to go higher, Explains don't really factor function calls in, they'll be listed in the predicate section but they are ignored by the stats.
This is an object lesson in why you should be very wary of putting function calls into the where clause of a query.
|
|
|
|
Re: How to reduce the consistent gets of this sql [message #514117 is a reply to message #513825] |
Fri, 01 July 2011 12:10 |
|
Kevin Meade
Messages: 2103 Registered: December 1999 Location: Connecticut USA
|
Senior Member |
|
|
A long post. I flew through some of it so if I am repeating, my appologies.
There are two basic problems from a performance angle with this function.
1) maybe I am reading it wrong, but you seem to be counting the number of rows in each subquery but then you don't actually care how many you counted. This is a very basic mistake that all of us have made over the years. For example, suppose one time you run the count = 100 and a second time it = 1. How does this change the return value? If I read it right, you are only interested in seeing if there is at least one row so in that case, use the ROWNUM=1 construct.
Consider also the idea of splitting up your query inside the function into multiple steps. I don't see the significance of doing both queries if one of them returns a value. The union suggests you care about both but in fact you don't. A value > 0 from either seems to be enough in which case why do the second if the first gives a good result. So if you want to stay with the function, try splitting up the sql into two parts and doing a little more work in PL/SQL to manage the results. Be careful though. Adding more SQL to the function may result in increased recursive calls (see #2 next).
Someone mentioned using existential queries (EXISTS aka. CORRELATED SUBQUERIES). I believe they were working this same angle which is fetch only one row to get the count when you don't really care what the actual count is. The effect is that you will not read all the data when executing your function's queries and that will reduce your consistent gets. I suggest using ROWNUM=1 because you don't have to change too much.
Also, someone suggested creatung indexes specific to the query. If you are creating an index specifically for one query, put all columns referenced in the query (within reason) in the index (this includes columns in the select list if any) so that you can skip table access step. You goal here is two either a) achieve a fast index lookup straight to some row you need and skipping the associated TABLE ACCESS BY ROWID step at the same time, or b) use the index like a table so that you do a fast index scan rather than a full table scan. Both can reduce consistent gets but do so in different ways. Remember that every index you create means more work for Oracle during INSERT/UPDATE/DELETE and table maintenance ops so like most things, you are doing a trade off. Here you are trading off slightly slower performance on data changes for much better performance on specific queries. If you don't get the big bang that you need in your query to justify the tradeoff, rethink the idea of creating the index.
2) a bigger problem with PL/SQL functions called from SQL is that they are very expensive. Your query may show considerable recursive calls (not talking about consistent gets, these are two differnt things). You should see this somewhere (maybe AUTOTRACE?). There is no way to get rid of this aside from dropping the function and inlining its logic inside the main query or getting rid of SQL in the fuction. However, if your issue is recursive calls due to calling PL/SQL from SQL, then inlining the SQL and PL/SQL logic (using native oracle functions) in the main query will be a big benefit performance wise. Someone I believe mentioned this option. You responed with the typical "maintenance burdern". I respond to your reponse with, again it is all about tradeoffs. How badly do you want it to go faster? Try the re-write to see what the benefit is, then decide if you are willing to make the tradeoff to get the benefit. In this case you are trading off redudant code headaches for better performance. I have seen cases where 90% of long running queries was due to this kind of recursive call issue from a seemingly simple PL/SQL function just like this one. The app team I was assisting did not want to re-write it but in the end they had no choice. Their batch stream was hung on it. Try it out, you might have a similar situation.
Good luck, Kevin
vn_counts := null;
begin
select 1 cnum
into vn_counts
from sbandoffer
where ...
and rownum = 1
;
exception
when no_data_found then
null;
end;
if vn_counts is null then
begin
select 1 cnum
into vn_counts
from dloginrolerel a,sbandoffer b
where ...
and rownum = 1
;
exception
when no_data_found then
null;
end;
end if;
if vn_counts is null then vn_counts := 0; end if;
|
|
|
|
Goto Forum:
Current Time: Sun Nov 24 21:58:11 CST 2024
|