Skip to Main Content

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, jinwei.

Asked: September 04, 2018 - 1:21 am UTC

Last updated: September 04, 2018 - 6:44 am UTC

Version: 11.2.0.4.0

Viewed 1000+ times

You Asked

Hi Tom,I have met a SQL that with the latest statistics and the execution plan is in the wrong estimate-rows ,that's to say,it's a large difference between the estimate and the actual,the sql statement and execution is below:
select t1.card_no,
       t.card_active_date,
       t1.package_deadline,
       s.service_name,
       t1.channel_id,
       t1.interface_id,
       t1.id,
       t1.sms_package_id
  from (select iot_card_id, min(card_sync_date) card_active_date
          from (select cmt.*
                  from (select *
                          from t_iot_card_monitor_trancation
                         where iot_card_id in
                               (select id
                                  from t_iot_card
                                 where card_active_date = create_date
                                   and (card_state = '1' or
                                       (sysdate > Card_Test_Closing_Date and
                                       sysdate < card_silence_closing_date))
                                   and (flow_share = '00' or flow_share is null))
                           and package_used > 0) cmt
                  left join t_iot_card ic
                    on ic.id = cmt.iot_card_id
                 where to_char(cmt.card_sync_date, 'yyyy-mm') >
                       to_char(ic.card_test_closing_date, 'yyyy-mm')
                    or ic.card_test_closing_date is null)
         group by iot_card_id) t
  left join t_iot_card t1
    on t1.id = t.iot_card_id
  left join t_iot_interface_new t3
    on t3.id = t1.interface_id
  left join t_iot_interface_server s
    on t3.server_id = s.id
 where s.service_name != 'ydszdh';


execution plan:
----------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                           | Name                          | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
----------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |                               |      1 |        |      0 |00:00:01.51 |     957K|       |       |    |
|   1 |  HASH GROUP BY                      |                               |      1 |      5 |      0 |00:00:01.51 |     957K|   757K|   757K|    |
|*  2 |   FILTER                            |                               |      1 |        |      0 |00:00:01.51 |     957K|       |       |    |
|   3 |    NESTED LOOPS OUTER               |                               |      1 |      5 |   1860 |00:00:01.51 |     957K|       |       |    |
|*  4 |     HASH JOIN                       |                               |      1 |      5 |   1860 |00:00:01.51 |     955K|  1185K|  1185K| 1017K (0)|
|*  5 |      TABLE ACCESS FULL              | T_IOT_INTERFACE_SERVER        |      1 |      1 |     10 |00:00:00.01 |       6 |       |       |    |
|*  6 |      HASH JOIN                      |                               |      1 |    101 |   5030 |00:00:01.50 |     955K|  1263K|  1263K| 1255K (0)|
|   7 |       TABLE ACCESS FULL             | T_IOT_INTERFACE_NEW           |      1 |    125 |    125 |00:00:00.01 |       5 |       |       |    |
|   8 |       NESTED LOOPS                  |                               |      1 |     75 |   5030 |00:00:01.49 |     955K|       |       |    |
|   9 |        NESTED LOOPS                 |                               |      1 |     76 |   5030 |00:00:01.48 |     950K|       |       |    |
|  10 |         NESTED LOOPS                |                               |      1 |     76 |   5030 |00:00:01.47 |     949K|       |       |    |
|* 11 |          TABLE ACCESS FULL          | T_IOT_CARD                    |      1 |      1 |  15665 |00:00:00.20 |   14166 |       |       |    |
|* 12 |          TABLE ACCESS BY INDEX ROWID| T_IOT_CARD_MONITOR_TRANCATION |  15665 |    163 |   5030 |00:00:01.26 |     935K|       |       |    |
|* 13 |           INDEX RANGE SCAN          | IOT_CARD_MONITOR_TRAN_CARD_ID |  15665 |    165 |    909K|00:00:00.23 |   25712 |       |       |    |
|* 14 |         INDEX UNIQUE SCAN           | T_IOT_CARD                    |   5030 |      1 |   5030 |00:00:00.01 |     552 |       |       |    |
|  15 |        TABLE ACCESS BY INDEX ROWID  | T_IOT_CARD                    |   5030 |      1 |   5030 |00:00:00.01 |    5250 |       |       |    |
|  16 |     TABLE ACCESS BY INDEX ROWID     | T_IOT_CARD                    |   1860 |      1 |   1860 |00:00:00.01 |    2346 |       |       |    |
|* 17 |      INDEX UNIQUE SCAN              | T_IOT_CARD                    |   1860 |      1 |   1860 |00:00:00.01 |     370 |       |       |    |
----------------------------------------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter((TO_CHAR(INTERNAL_FUNCTION("CARD_SYNC_DATE"),'yyyy-mm')>TO_CHAR(INTERNAL_FUNCTION("IC"."CARD_TEST_CLOSING_DATE"),'yyyy-mm')
               OR "IC"."CARD_TEST_CLOSING_DATE" IS NULL))
   4 - access("T3"."SERVER_ID"="S"."ID")
   5 - filter("S"."SERVICE_NAME"<>'ydszdh')
   6 - access("T3"."ID"="T1"."INTERFACE_ID")
  11 - filter(("CARD_ACTIVE_DATE"="CREATE_DATE" AND ("FLOW_SHARE"='00' OR "FLOW_SHARE" IS NULL) AND ("CARD_STATE"='1' OR
              ("CARD_SILENCE_CLOSING_DATE">SYSDATE@! AND "CARD_TEST_CLOSING_DATE"0)
  13 - access("IOT_CARD_ID"="ID")
  14 - access("T1"."ID"="T_IOT_CARD_MONITOR_TRANCATION"."IOT_CARD_ID")
  17 - access("IC"."ID"="T_IOT_CARD_MONITOR_TRANCATION"."IOT_CARD_ID")                

table statistics:
Table Name Num Rows Sample Perc Last Analyzed Blocks Stale
                   Size            Stats
      
T_IOT_CARD 387382 387382 100 2018-09-03/16:56:54 14177 NO
T_IOT_CARD_MONITOR_TRANCATION 65201550 6520155 10 2018-09-03/05:32:57 659854 NO
T_IOT_INTERFACE_NEW 125 125 100 2018-09-03/05:34:26 7 NO
T_IOT_INTERFACE_SERVER 11 11 100 2018-09-03/05:34:26 5 NO


why the table access full of T_IOT_CARD is estimate 1 rows?
and anywhere to tune the sql?
Thanks!

and Connor said...

As you add predicates, the optimizer will generally assume that each predicate will *reduce* the number of rows in the sample set. For example, ask the optimizer how many people born in February it will think 1/12th. Ask how many of them are star sign Pisces, it will think 1/12th times 1/12th = 1/144th, or less than one percent, when in fact, most people born in Feb are Pisces.

You can see this with a simple demo

SQL> create table t
  2  as select * from dba_objects;

Table created.

SQL>
SQL> exec dbms_stats.gather_table_stats('','T');

PL/SQL procedure successfully completed.

SQL>
SQL> select /*+ gather_plan_statistics */ count(*) from t
  2  where owner = 'SYS';

  COUNT(*)
----------
     51766

1 row selected.

SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID  0dc3bp8v3ddbu, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(*) from t where owner = 'SYS'

Plan hash value: 2966233522

-------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      1 |00:00:00.01 |    1498 |
|   1 |  SORT AGGREGATE    |      |      1 |      1 |      1 |00:00:00.01 |    1498 |
|*  2 |   TABLE ACCESS FULL| T    |      1 |   2402 |  51766 |00:00:00.01 |    1498 |
-------------------------------------------------------------------------------------

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

   2 - filter("OWNER"='SYS')


19 rows selected.

SQL>
SQL> select /*+ gather_plan_statistics */ count(*) from t
  2  where owner = 'SYS'
  3  and   object_type = 'JAVA CLASS';

  COUNT(*)
----------
     34642

1 row selected.

SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID  418f63yfz4bv1, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(*) from t where owner =
'SYS' and   object_type = 'JAVA CLASS'

Plan hash value: 2966233522

-------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      1 |00:00:00.01 |    1498 |
|   1 |  SORT AGGREGATE    |      |      1 |      1 |      1 |00:00:00.01 |    1498 |
|*  2 |   TABLE ACCESS FULL| T    |      1 |     48 |  34642 |00:00:00.01 |    1498 |
-------------------------------------------------------------------------------------

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

   2 - filter(("OBJECT_TYPE"='JAVA CLASS' AND "OWNER"='SYS'))


20 rows selected.

SQL>
SQL> select /*+ gather_plan_statistics */ count(*) from t
  2  where owner = 'SYS'
  3  and   object_type = 'JAVA CLASS'
  4  and   status = 'VALID';

  COUNT(*)
----------
     34642

1 row selected.

SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID  77maurtkg4sfj, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(*) from t where owner =
'SYS' and   object_type = 'JAVA CLASS' and   status = 'VALID'

Plan hash value: 2966233522

-------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      1 |00:00:00.01 |    1498 |
|   1 |  SORT AGGREGATE    |      |      1 |      1 |      1 |00:00:00.01 |    1498 |
|*  2 |   TABLE ACCESS FULL| T    |      1 |     24 |  34642 |00:00:00.01 |    1498 |
-------------------------------------------------------------------------------------

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

   2 - filter(("OBJECT_TYPE"='JAVA CLASS' AND "OWNER"='SYS' AND
              "STATUS"='VALID'))


21 rows selected.

SQL>
SQL> select /*+ gather_plan_statistics */ count(*) from t
  2  where owner = 'SYS'
  3  and   object_type = 'JAVA CLASS'
  4  and   status = 'VALID'
  5  and   temporary = 'N';

  COUNT(*)
----------
     34642

1 row selected.

SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID  cnqr8f247z25z, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(*) from t where owner =
'SYS' and   object_type = 'JAVA CLASS' and   status = 'VALID' and
temporary = 'N'

Plan hash value: 2966233522

-------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      1 |00:00:00.01 |    1498 |
|   1 |  SORT AGGREGATE    |      |      1 |      1 |      1 |00:00:00.01 |    1498 |
|*  2 |   TABLE ACCESS FULL| T    |      1 |     12 |  34642 |00:00:00.01 |    1498 |
-------------------------------------------------------------------------------------

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

   2 - filter(("OBJECT_TYPE"='JAVA CLASS' AND "OWNER"='SYS' AND
              "STATUS"='VALID' AND "TEMPORARY"='N'))


22 rows selected.

SQL>
SQL> select /*+ gather_plan_statistics */ count(*) from t
  2  where owner = 'SYS'
  3  and   object_type = 'JAVA CLASS'
  4  and   status = 'VALID'
  5  and   temporary = 'N'
  6  and   generated = 'N';

  COUNT(*)
----------
     34642

1 row selected.

SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));

PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
SQL_ID  5nzjuw2z4w9ya, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(*) from t where owner =
'SYS' and   object_type = 'JAVA CLASS' and   status = 'VALID' and
temporary = 'N' and   generated = 'N'

Plan hash value: 2966233522

-------------------------------------------------------------------------------------
| Id  | Operation          | Name | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |      |      1 |        |      1 |00:00:00.01 |    1498 |
|   1 |  SORT AGGREGATE    |      |      1 |      1 |      1 |00:00:00.01 |    1498 |
|*  2 |   TABLE ACCESS FULL| T    |      1 |      6 |  34642 |00:00:00.01 |    1498 |
-------------------------------------------------------------------------------------

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

   2 - filter(("OBJECT_TYPE"='JAVA CLASS' AND "OWNER"='SYS' AND
              "STATUS"='VALID' AND "TEMPORARY"='N' AND "GENERATED"='N'))


22 rows selected.

SQL>
SQL>


Histograms can potentially help, as can extended statistics

https://blogs.oracle.com/optimizer/extended-statistics

If all else fails, you can hint a copy of the SQL to get the plan you want, capture a SQL plan baseline to lock in that plan, and the apply that baseline to the real SQL.

There is a full walkthrough of that process here

https://blogs.oracle.com/optimizer/using-sql-plan-management-to-control-sql-execution-plans

Rating

  (4 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

details

jinwei huang, September 04, 2018 - 6:49 am UTC

Thanks! But the SQL causes a lot of logical read with zero rows return,I need to reduce the logical read of the sql causes

10046 trace

jinwei huang, September 04, 2018 - 6:52 am UTC

from below the 10046 event shows that the logical read is so high:
call     count       cpu    elapsed       disk      query    current        rows
------- ------  -------- ---------- ---------- ---------- ----------  ----------
Parse        1      0.04       0.05          0         10          0           0
Execute      1      0.00       0.00          0          0          0           0
Fetch        1      1.45       1.46          0     966068          0           0
------- ------  -------- ---------- ---------- ---------- ----------  ----------
total        3      1.50       1.51          0     966078          0           0

Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: SYS
Number of plan statistics captured: 1

Rows (1st) Rows (avg) Rows (max)  Row Source Operation
---------- ---------- ----------  ---------------------------------------------------
         0          0          0  HASH GROUP BY (cr=966068 pr=0 pw=0 time=1463292 us cost=4189 size=796 card=4)
         0          0          0   FILTER  (cr=966068 pr=0 pw=0 time=1463082 us)
      1860       1860       1860    NESTED LOOPS OUTER (cr=966068 pr=0 pw=0 time=507938 us cost=4188 size=796 card=4)
      1860       1860       1860     HASH JOIN  (cr=963722 pr=0 pw=0 time=501724 us cost=4181 size=752 card=4)
        10         10         10      TABLE ACCESS FULL T_IOT_INTERFACE_SERVER (cr=6 pr=0 pw=0 time=226 us cost=3 size=39 card=1)
      5028       5028       5028      HASH JOIN  (cr=963716 pr=0 pw=0 time=451329 us cost=4178 size=13112 card=88)
       125        125        125       TABLE ACCESS FULL T_IOT_INTERFACE_NEW (cr=5 pr=0 pw=0 time=161 us cost=4 size=4750 card=125)
      5028       5028       5028       NESTED LOOPS  (cr=963711 pr=0 pw=0 time=445788 us cost=4174 size=8214 card=74)
      5028       5028       5028        NESTED LOOPS  (cr=958463 pr=0 pw=0 time=432696 us cost=4174 size=8214 card=76)
      5028       5028       5028         NESTED LOOPS  (cr=957911 pr=0 pw=0 time=421964 us cost=4024 size=5016 card=76)
     15578      15578      15578          TABLE ACCESS FULL T_IOT_CARD (cr=14177 pr=0 pw=0 time=401122 us cost=3856 size=40 card=1)
      5028       5028       5028          TABLE ACCESS BY INDEX ROWID T_IOT_CARD_MONITOR_TRANCATION (cr=943734 pr=0 pw=0 time=1154255 us cost=168 size=4238 card=163)
    917963     917963     917963           INDEX RANGE SCAN IOT_CARD_MONITOR_TRAN_CARD_ID (cr=25849 pr=0 pw=0 time=235675 us cost=2 size=0 card=165)(object id 278076)
      5028       5028       5028         INDEX UNIQUE SCAN T_IOT_CARD (cr=552 pr=0 pw=0 time=5220 us cost=1 size=0 card=1)(object id 205360)
      5028       5028       5028        TABLE ACCESS BY INDEX ROWID T_IOT_CARD (cr=5248 pr=0 pw=0 time=6615 us cost=2 size=45 card=1)
      1860       1860       1860     TABLE ACCESS BY INDEX ROWID T_IOT_CARD (cr=2346 pr=0 pw=0 time=4269 us cost=2 size=11 card=1)
      1860       1860       1860      INDEX UNIQUE SCAN T_IOT_CARD (cr=370 pr=0 pw=0 time=1699 us cost=1 size=0 card=1)(object id 205360)


pls help to reduce the logical read,thanks!

Step that does the most logical IO.

Rajeshwaran, Jeyabal, September 04, 2018 - 3:14 pm UTC

out of 957K of logical IO, nearly 935K is reported in the step#12.

|  10 |         NESTED LOOPS                |                               |      1 |     76 |   5030 |00:00:01.47 |     949K|       |       |    |
|* 11 |          TABLE ACCESS FULL          | T_IOT_CARD                    |      1 |      1 |  15665 |00:00:00.20 |   14166 |       |       |    |
|* 12 |          TABLE ACCESS BY INDEX ROWID| T_IOT_CARD_MONITOR_TRANCATION |  15665 |    163 |   5030 |00:00:01.26 |     935K|       |       |    |
|* 13 |           INDEX RANGE SCAN          | IOT_CARD_MONITOR_TRAN_CARD_ID |  15665 |    165 |    909K|00:00:00.23 |   25712 |       |       |    |


As Connor suggested
a) at step# 11 - in the execution plan we apply this below filter, and returned 15K rows from the total of 387K - see if any histogram/extended stats on the subset of columns - could help to fix the actual and estimated cardinality mismatch.

also check for the possibility of having any key column to be indexed to skip FTS (since we retrieve 15K rows out of 387K)

11 - filter(("CARD_ACTIVE_DATE"="CREATE_DATE" AND ("FLOW_SHARE"='00' OR "FLOW_SHARE" IS NULL) AND ("CARD_STATE"='1' OR
("CARD_SILENCE_CLOSING_DATE">SYSDATE@! AND "CARD_TEST_CLOSING_DATE"0)


b) at step no#13 - we did index range scan and returned 909K rows and while visiting the table using those rowid returned only 5030 rows - most of those rows got filtered here and produced 935K IO.
but the execution plan predicate section, dont tell what filter is getting applied at the step no#12 - please check that.

but i guess it that you need to have that filter column in this index IOT_CARD_MONITOR_TRAN_CARD_ID to reduce the number of rowid returned from that index range scan.

hope this helps.

feedback

jinwei huang, September 05, 2018 - 1:25 am UTC

after create index on UFLOWSYSTEM.T_IOT_CARD_MONITOR_TRANCATION(iot_card_id,package_used),it help much,the logical IO reduce to 40 thousands ,thanks a lot!

More to Explore

Performance

Get all the information about database performance in the Database Performance guide.