{
  "date": "2014-11-08T19:48:24",
  "slug": "oracle-sql-profile-why-multiple-optestimate",
  "link": "https://www.dbi-services.com/blog/oracle-sql-profile-why-multiple-optestimate/",
  "title": {
    "rendered": "Oracle SQL Profile: why multiple OPT_ESTIMATE?"
  },
  "content": {
    "rendered": "<h2>By Franck Pachot</h2>\n<p>.<br />\nIn a <a href=\"https://www.dbi-services.com/index.php/blog/entry/oracle-sql-profiles-check-what-they-do-before-accepting-them-blindly\">previous blog</a> I&#8217;v shared my script to retrieve the OPT_ESTIMATE hints from a SQL Profile. In the example I made, I had two lines for each table:</p>\n<pre>--- PROFILE HINTS from dbiInSite (1) statement 4fz1vtn0w8aak:\n/*+\nOPT_ESTIMATE(@\"SEL$2CBA5DDD\", TABLE, \"EMPLOYEES\"@\"SEL$1\", SCALE_ROWS=2)\nOPT_ESTIMATE(@\"SEL$58A6D7F6\", TABLE, \"EMPLOYEES\"@\"SEL$1\", SCALE_ROWS=2)\nOPT_ESTIMATE(@\"SEL$6AE97DF7\", TABLE, \"DEPARTMENTS\"@\"SEL$1\", SCALE_ROWS=5.185185185)\nOPT_ESTIMATE(@\"SEL$58A6D7F6\", TABLE, \"DEPARTMENTS\"@\"SEL$1\", SCALE_ROWS=5.185185185)\n*/\n</pre>\n<p>The reason is that when the optimizer do some transformations to the query, then the query block identifiers can change. And when you adjust a cardinality estimation, you must do it for all transformations or you will completely mess up the optimizer choice.</p>\n<p>When I do an explain plan which show the query blocks, I have only the SEL$58A6D7F6 one:</p>\n<pre class=\"brush: sql; gutter: true; first-line: 1\">SQL&gt; explain plan for\n  2  select distinct DEPARTMENT_NAME  from DEPARTMENTS join EMPLOYEES\n  3  using(DEPARTMENT_ID)  where DEPARTMENT_NAME like '%ing' and SALARY&gt;20000 ;\n\nExplained.\n\nSQL&gt; select * from table(dbms_xplan.display(format=&gt;'basic +alias'));\n\nPLAN_TABLE_OUTPUT\n-------------------------------------------------------------------\nPlan hash value: 3041748347\n-------------------------------------------------------------------\n| Id  | Operation                             | Name              |\n-------------------------------------------------------------------\n|   0 | SELECT STATEMENT                      |                   |\n|   1 |  HASH UNIQUE                          |                   |\n|   2 |   NESTED LOOPS SEMI                   |                   |\n|   3 |    TABLE ACCESS FULL                  | DEPARTMENTS       |\n|   4 |    TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES         |\n|   5 |     INDEX RANGE SCAN                  | EMP_DEPARTMENT_IX |\n-------------------------------------------------------------------\n\nQuery Block Name / Object Alias (identified by operation id):\n-------------------------------------------------------------\n\n   1 - SEL$58A6D7F6\n   3 - SEL$58A6D7F6 / DEPARTMENTS@SEL$1\n   4 - SEL$58A6D7F6 / EMPLOYEES@SEL$1\n   5 - SEL$58A6D7F6 / EMPLOYEES@SEL$1\n\n</pre>\n<p>In order to confirm that the duplicate OPT_ESTIMATE are coming from different transformations, I&#8217;ve generated a 10053 trace and searched for SEL$6AE97DF7:</p>\n<pre class=\"brush: sql; gutter: true; first-line: 1\">Registered qb: SEL$6AE97DF7 0x851d8eb8 (DISTINCT PLACEMENT SEL$58A6D7F6; SEL$58A6D7F6; \"EMPLOYEES\"@\"SEL$1\")\n---------------------\nQUERY BLOCK SIGNATURE\n---------------------\n  signature (): qb_name=SEL$6AE97DF7 nbfros=2 flg=0\n    fro(0): flg=0 objn=92595 hint_alias=\"DEPARTMENTS\"@\"SEL$1\"\n    fro(1): flg=1 objn=0 hint_alias=\"VW_DTP_43B5398E\"@\"SEL$43B5398E\"\n\n</pre>\n<p>that&#8217;s the Distinct Placement.<br />\nlet&#8217;s try the PLACE_DISTINCT hint:</p>\n<pre class=\"brush: sql; gutter: true; first-line: 1\">SQL&gt; explain plan for\n  2  select /*+ PLACE_DISTINCT(EMPLOYEES) */ distinct DEPARTMENT_NAME  from DEPARTMENTS join EMPLOYEES\n  3  using(DEPARTMENT_ID)  where DEPARTMENT_NAME like '%ing' and SALARY&gt;20000 ;\n\nExplained.\n\nSQL&gt; select * from table(dbms_xplan.display(format=&gt;'basic +alias'));\n\nPLAN_TABLE_OUTPUT\n--------------------------------------------------------------------\nPlan hash value: 2901355344\n\n--------------------------------------------------------------------\n| Id  | Operation                              | Name              |\n--------------------------------------------------------------------\n|   0 | SELECT STATEMENT                       |                   |\n|   1 |  HASH UNIQUE                           |                   |\n|   2 |   NESTED LOOPS SEMI                    |                   |\n|   3 |    TABLE ACCESS FULL                   | DEPARTMENTS       |\n|   4 |    VIEW PUSHED PREDICATE               | VW_DTP_43B5398E   |\n|   5 |     TABLE ACCESS BY INDEX ROWID BATCHED| EMPLOYEES         |\n|   6 |      INDEX RANGE SCAN                  | EMP_DEPARTMENT_IX |\n--------------------------------------------------------------------\n\nQuery Block Name / Object Alias (identified by operation id):\n-------------------------------------------------------------\n\n   1 - SEL$6AE97DF7\n   3 - SEL$6AE97DF7 / DEPARTMENTS@SEL$1\n   4 - SEL$9B757045 / VW_DTP_43B5398E@SEL$43B5398E\n   5 - SEL$9B757045 / EMPLOYEES@SEL$1\n   6 - SEL$9B757045 / EMPLOYEES@SEL$1\n\n</pre>\n<p>Here is where the</p>\n<pre>OPT_ESTIMATE(@\"SEL$6AE97DF7\", TABLE, \"DEPARTMENTS\"@\"SEL$1\", SCALE_ROWS=5.185185185)</pre>\n<p>makes sense. The same cardinality adjustment must be done for each transformation that the optimizer is evaluating.</p>\n<p>That observation brings me to the following: what happens to your profiles when you upgrade to a version that brings new optimizer transformations? The optimizer will compare some plans with adjusted cardinalities, compared with some plans with non-adjusted cardinalites. And that is probably not a good idea.</p>\n<p>In my opinion, SQL Profiles are just like hints: a short term workaround that must be documented and re-evaluated at each upgrade.</p>\n",
    "protected": false
  }
}
