Finding the Predicates that are different on the same Execution Plan
Ok, this may not be common but when it happens it becomes very hard to spot. Couple of cases:
- You have a SQL with multiple child cursors. They all could have the same Plan Hash Value (PHV) or maybe more than one. Performance wise even the same PHV can exhibit differences for multiple reason, and one of them is the possibility of having the same PHV but not exactly the same filter or access Predicates. If you have two or more predicates on the same Plan Operation, the difference could be the order of these Predicates, and if their execution performance is quite different, one version could perform better than the other.
- You have two or more versions of a core query, where some of these versions have slightly different Predicates (typical case where the SQL is dynamically assembled by an application). This family of similar queries would have different SQL_IDs but they may or may not produce the same PHV. When they do, it is hard to find in which Plan Operations the different Predicates are used.
I have seen many cases where we have to scratch our heads thinking: “Why do I have such a different performance when it is clear we have the same Execution Plan?”. If you are having one of those issues, you may want to use one of the two scripts below. The first inputs a SQL_ID and it outputs a list of those Predicates for each PHV where there is more than one version for any Plan Operation ID. The second inputs a PHV and performs the same analysis regardless if the PHV is associated to one or many SQL_IDs. Both are RAC aware. Both are stand-alone and install nothing.
Script to find differences in access/filter Predicates for a given SQL_ID
SPO difpred1.txt; SET HEA ON LIN 80 NEWP 1 PAGES 100 LIN 300 TRIMS ON TI OFF TIMI OFF; -- shows which plan lines have different predicates for given sql_id WITH d AS ( SELECT sql_id, plan_hash_value, id, COUNT(DISTINCT access_predicates) distinct_access_predicates, COUNT(DISTINCT filter_predicates) distinct_filter_predicates FROM gv$sql_plan_statistics_all WHERE sql_id = '&&sql_id.' GROUP BY sql_id, plan_hash_value, id HAVING MIN(NVL(access_predicates, 'X')) != MAX(NVL(access_predicates, 'X')) OR MIN(NVL(filter_predicates, 'X')) != MAX(NVL(filter_predicates, 'X')) ) SELECT v.plan_hash_value, v.id, 'access' type, v.inst_id, v.child_number, v.access_predicates predicates FROM d, gv$sql_plan_statistics_all v WHERE v.sql_id = d.sql_id AND v.plan_hash_value = d.plan_hash_value AND v.id = d.id AND d.distinct_access_predicates > 1 UNION ALL SELECT v.plan_hash_value, v.id, 'filter' type, v.inst_id, v.child_number, v.filter_predicates predicates FROM d, gv$sql_plan_statistics_all v WHERE v.sql_id = d.sql_id AND v.plan_hash_value = d.plan_hash_value AND v.id = d.id AND d.distinct_filter_predicates > 1 ORDER BY 1, 2, 3, 6, 4, 5; SET HEA ON LIN 80 NEWP 1 PAGES 14 LIN 80 TRIMS OFF TI OFF TIMI OFF; SPO OFF;
Output Sample:
old 8: WHERE sql_id = '&&sql_id.' new 8: WHERE sql_id = 'cy9pxhp4y6u78' PLAN_HASH_VALUE ID TYPE INST_ID CHILD_NUMBER --------------- ---------- ------ ---------- ------------ PREDICATES -------------------------------------------------------------------------------- 3724264953 2 filter 1 0 ("C2"='WHATEVER' AND TRANSLATE(TO_CHAR(SIN("N4")),'1','a')='a') 3724264953 2 filter 1 1 (TRANSLATE(TO_CHAR(SIN("N4")),'1','a')='a' AND "C2"='WHATEVER')
Script to find differences in access/filter Predicates for a given PHV
SPO difpred2.txt; SET HEA ON LIN 80 NEWP 1 PAGES 100 LIN 300 TRIMS ON TI OFF TIMI OFF; -- shows which plan lines have different predicates for given phv WITH d AS ( SELECT plan_hash_value, id, COUNT(DISTINCT access_predicates) distinct_access_predicates, COUNT(DISTINCT filter_predicates) distinct_filter_predicates FROM gv$sql_plan_statistics_all WHERE plan_hash_value = &&plan_hash_value. GROUP BY plan_hash_value, id HAVING MIN(NVL(access_predicates, 'X')) != MAX(NVL(access_predicates, 'X')) OR MIN(NVL(filter_predicates, 'X')) != MAX(NVL(filter_predicates, 'X')) ) SELECT v.id, 'access' type, v.sql_id, v.inst_id, v.child_number, v.access_predicates predicates FROM d, gv$sql_plan_statistics_all v WHERE v.plan_hash_value = d.plan_hash_value AND v.id = d.id AND d.distinct_access_predicates > 1 UNION ALL SELECT v.id, 'filter' type, v.sql_id, v.inst_id, v.child_number, v.filter_predicates predicates FROM d, gv$sql_plan_statistics_all v WHERE v.plan_hash_value = d.plan_hash_value AND v.id = d.id AND d.distinct_filter_predicates > 1 ORDER BY 1, 2, 6, 3, 4, 5; SET HEA ON LIN 80 NEWP 1 PAGES 14 LIN 80 TRIMS OFF TI OFF TIMI OFF; SPO OFF;
Output Sample:
Enter value for plan_hash_value: 2339135578 old 7: WHERE plan_hash_value = &&plan_hash_value. new 7: WHERE plan_hash_value = 2339135578 ID TYPE SQL_ID INST_ID CHILD_NUMBER ---------- ------ ------------- ---------- ------------ PREDICATES -------------------------------------------------------------------------------- 5 filter 72jhgzs8nb1p4 1 0 MAX("E2"."HIREDATE")=:B1 5 filter 8a4x4867rajuv 1 0 (MAX("E2"."HIREDATE")=:B1 OR :B2 IS NULL) 5 filter 8a4x4867rajuv 1 1 (MAX("E2"."HIREDATE")=:B1 OR :B2 IS NULL)
[…] two times, each time with a different access and/or filter predicates. This is confirmed by the sql scripts given by Carlos sierra which when applied to this particular case gives […]
Different sql id, different force matching signature, different rows processed with the same plan hash value | Mohamed Houri’s Oracle Notes
June 19, 2013 at 5:07 am
[…] SQL is for and fortunately, Carlos Sierra has already done most of the work for us. See his post on Identifying Which Predicates Don’t Match. Of course I usually can’t leave well enough alone so I modified one of his diff scripts […]
Kerry Osborne’s Oracle Blog » Blog Archive » SQL Gone Bad – But Plan Not Changed? – Part 2 – Kerry Osborne’s Oracle Blog
June 20, 2013 at 4:32 pm