{"id":4304,"date":"2025-06-18T21:53:55","date_gmt":"2025-06-18T21:53:55","guid":{"rendered":"https:\/\/w3buddy.com\/?post_type=cposts&#038;p=4304"},"modified":"2025-06-18T21:58:35","modified_gmt":"2025-06-18T21:58:35","slug":"purge-sql-plan-from-shared-pool-in-oracle","status":"publish","type":"cposts","link":"https:\/\/w3buddy.com\/blog\/notes\/performance-tuning\/purge-sql-plan-from-shared-pool-in-oracle\/","title":{"rendered":"Purge SQL Plan from Shared Pool in Oracle"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Purging a SQL plan<\/strong> means removing the compiled version of a SQL statement from Oracle&#8217;s <strong>shared pool (memory)<\/strong>. This forces Oracle to <strong>reparse the SQL<\/strong> the next time it&#8217;s executed, which may result in a <strong>better execution plan<\/strong> if the current one is inefficient or outdated.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Purging is a <strong>temporary memory cleanup step<\/strong> \u2014 it does <strong>not delete the SQL from disk<\/strong>, nor does it prevent bad plans from returning unless further actions (like creating a baseline) are taken.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">When Should You Purge?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use plan purging when:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The SQL is <strong>performing poorly<\/strong>, and a bad plan is currently in memory.<\/li>\n\n\n\n<li>You&#8217;re troubleshooting plan issues and want to <strong>clear the in-memory version<\/strong>.<\/li>\n\n\n\n<li>You need Oracle to <strong>re-optimize<\/strong> the query on next execution.<\/li>\n\n\n\n<li>You&#8217;re preparing to <strong>monitor a fresh parse<\/strong> (e.g., after fixing stats or hinting).<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">\u26a0\ufe0f <strong>Do NOT purge casually.<\/strong><br>It only clears memory \u2014 it won\u2019t prevent Oracle from loading the same bad plan again unless you take action (e.g., use baselines or hints).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Important Pre-Check<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Before purging:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>\u2705 Make sure <strong>no sessions are actively using the SQL ID<\/strong>.<\/li>\n\n\n\n<li>\ud83d\udcac Ask the application team to <strong>pause usage<\/strong> or approve session termination.<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code>SELECT sid, serial#, sql_id, status, username\nFROM v$session\nWHERE sql_id = 'f14nzf4gyq4y4';  -- \ud83d\udd04 Replace with your SQL_ID<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Step 1: Check SQL Execution History<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This step helps you analyze how a SQL ID has performed over time \u2014 across different plans. You can identify which plan hash was stable and how recent executions behaved.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>CLEAR COLUMNS BREAKS COMPUTES\nSET LINESIZE 200\nSET PAGESIZE 1000\n\nCOLUMN sql_id          FORMAT A15        HEADING \"SQL|ID\"\nCOLUMN s_time          FORMAT A12        HEADING \"Snap|Time\"\nCOLUMN executions      FORMAT 999999999  HEADING \"Execs\"\nCOLUMN elapsed_t       FORMAT 99999999.99 HEADING \"Elapsed|Secs\"\nCOLUMN cpu_t           FORMAT 99999999.99 HEADING \"CPU|Secs\"\nCOLUMN user_io         FORMAT 99999999.99 HEADING \"User I\/O|Secs\"\nCOLUMN buffer_gets     FORMAT 999999999  HEADING \"Buffer|Gets\"\nCOLUMN disk_r          FORMAT 999999999  HEADING \"Disk|Reads\"\nCOLUMN rows_processed  FORMAT 999999999  HEADING \"Rows|Processed\"\nCOLUMN sec_per_exec    FORMAT 99999999.9999 HEADING \"Seconds|per Exec\"\nCOLUMN plan_hash_value FORMAT A15        HEADING \"Plan|Hash\"\nBREAK ON sql_id ON plan_hash_value SKIP 1\n\nACCEPT 1_sqlid PROMPT 'Enter SQL ID: '\nACCEPT 1_days DEFAULT 42 PROMPT 'Enter days in the past (default 42): '\n\nSELECT \n    sql_id,\n    TO_CHAR(plan_hash_value) plan_hash_value,\n    TO_CHAR(begin_interval_time, 'DD-MON-YYYY') s_time,\n    SUM(executions_delta) executions,\n    ROUND(SUM(elapsed_time_delta) \/ 1e6, 2) elapsed_t,\n    ROUND(SUM(cpu_time_delta) \/ 1e6, 2) cpu_t,\n    ROUND(SUM(iowait_delta) \/ 1e6, 2) user_io,\n    ROUND(SUM(buffer_gets_delta)) buffer_gets,\n    ROUND(SUM(disk_reads_delta)) disk_r,\n    ROUND(SUM(rows_processed_delta)) rows_processed,\n    DECODE(SUM(executions_delta), 0, ROUND(SUM(elapsed_time_delta) \/ 1e6, 2),\n        ROUND(SUM(elapsed_time_delta)\/1e6\/SUM(executions_delta), 4)) sec_per_exec\nFROM \n    dba_hist_sqlstat a, dba_hist_snapshot b\nWHERE \n    a.snap_id = b.snap_id\n    AND a.instance_number = b.instance_number\n    AND a.sql_id = '&amp;&amp;1_sqlid'\n    AND TRUNC(begin_interval_time) >= TRUNC(SYSDATE - &amp;&amp;1_days)\nGROUP BY \n    sql_id, plan_hash_value, TO_CHAR(begin_interval_time, 'DD-MON-YYYY')\nORDER BY \n    TO_DATE(TO_CHAR(begin_interval_time, 'DD-MON-YYYY'), 'DD-MON-YYYY'),\n    plan_hash_value;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\ud83d\udcdd Sample Output <\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>SQL|ID         Plan|Hash     Snap|Time   Execs  Elapsed|Secs  CPU|Secs  User I\/O|Secs  Buffer|Gets  Disk|Reads  Rows|Processed  Seconds|per Exec\n------------- ------------- ---------- ------ ------------- ----------- -------------- ------------ ------------ ---------------- ------------------\nf14nzf4gyq4y4  2881287423   16-JUN-25     150        220.50     150.10         45.20      890000      26000          100000               1.4700\nf14nzf4gyq4y4  1734569820   15-JUN-25      45         90.80      65.40         18.70      310000       9000           32000               2.0178<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 What to Check in Output:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Check how many distinct Plan Hash Values<\/strong> (<code>PLAN_HASH_VALUE<\/code>) exist for the SQL_ID.\n<ul class=\"wp-block-list\">\n<li>If there is <strong>only one<\/strong>, you can proceed with a targeted purge more confidently.<\/li>\n\n\n\n<li>If there are <strong>multiple<\/strong>, identify which one is bad based on:\n<ul class=\"wp-block-list\">\n<li>High <strong>elapsed time<\/strong><\/li>\n\n\n\n<li>High <strong>buffer gets\/disk reads<\/strong><\/li>\n\n\n\n<li>Low <strong>rows processed<\/strong><\/li>\n\n\n\n<li>Very high <strong>seconds per execution<\/strong><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li>Confirm <strong>recency<\/strong> of bad plans (check <code>Snap|Time<\/code>).<\/li>\n\n\n\n<li>If multiple plans were used recently, a purge <strong>may not help<\/strong>, or may even <strong>cause Oracle to reload the same bad plan<\/strong>.<\/li>\n<\/ul>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\ud83d\udca1 If the SQL frequently switches between plans, it&#8217;s better to:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Fix with a <strong>SQL Plan Baseline<\/strong>, or<\/li>\n\n\n\n<li>Investigate bind peeking \/ adaptive plan causes.<\/li>\n<\/ul>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Step 2: Get SQL Memory Address &amp; Hash Value<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code><code>COL ADDRESS FORMAT A20\nCOL HASH_VALUE FORMAT 999999999\nCOL PLAN_HASH_VALUE FORMAT 999999999\nCOL ELAPSED_TIME FORMAT 999999999\nCOL ROWS_PROCESSED FORMAT 99999999\nSELECT \n  ADDRESS, \n  HASH_VALUE, \n  PLAN_HASH_VALUE, \n  ELAPSED_TIME, \n  ROWS_PROCESSED\nFROM \n  GV$SQLAREA\nWHERE \n  SQL_ID = 'f14nzf4gyq4y4';  -- \ud83d\udd04 Replace with your SQL_ID<\/code><\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">\ud83d\udccc <strong>Note down<\/strong> <code>ADDRESS<\/code> and <code>HASH_VALUE<\/code>. You will need these in the purge step.<\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Step 3: Purge from Shared Pool (Memory)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code><code>EXEC sys.DBMS_SHARED_POOL.PURGE('&amp;ADDRESS, &amp;HASH_VALUE', 'C');<\/code><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\u2705 <strong>Replace<\/strong>:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>&amp;ADDRESS<\/code> with actual SQL address<\/li>\n\n\n\n<li><code>&amp;HASH_VALUE<\/code> with actual SQL hash value<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><code>'C'<\/code> indicates it&#8217;s a cursor. Use <code>'P'<\/code> for packages if applicable.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u2705 Step 4: Confirm Purge<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Re-run the memory check to ensure SQL is gone:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code><code><code>COL ADDRESS FORMAT A20\nCOL HASH_VALUE FORMAT 999999999\nCOL PLAN_HASH_VALUE FORMAT 999999999\nCOL ELAPSED_TIME FORMAT 999999999\nCOL ROWS_PROCESSED FORMAT 99999999\nSELECT \n  ADDRESS, \n  HASH_VALUE, \n  PLAN_HASH_VALUE, \n  ELAPSED_TIME, \n  ROWS_PROCESSED\nFROM \n  GV$SQLAREA\nWHERE \n  SQL_ID = 'f14nzf4gyq4y4'; <\/code><\/code><\/code><\/pre>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">If no rows are returned: \u2705 <strong>Plan successfully purged from memory.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<h3 class=\"wp-block-heading\">Step 5: Re-check AWR Performance (Optional)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>CLEAR COLUMNS BREAKS COMPUTES\nSET LINESIZE 200\nSET PAGESIZE 1000\n\nCOLUMN sql_id          FORMAT A15        HEADING \"SQL|ID\"\nCOLUMN s_time          FORMAT A12        HEADING \"Snap|Time\"\nCOLUMN executions      FORMAT 999999999  HEADING \"Execs\"\nCOLUMN elapsed_t       FORMAT 99999999.99 HEADING \"Elapsed|Secs\"\nCOLUMN cpu_t           FORMAT 99999999.99 HEADING \"CPU|Secs\"\nCOLUMN user_io         FORMAT 99999999.99 HEADING \"User I\/O|Secs\"\nCOLUMN buffer_gets     FORMAT 999999999  HEADING \"Buffer|Gets\"\nCOLUMN disk_r          FORMAT 999999999  HEADING \"Disk|Reads\"\nCOLUMN rows_processed  FORMAT 999999999  HEADING \"Rows|Processed\"\nCOLUMN sec_per_exec    FORMAT 99999999.9999 HEADING \"Seconds|per Exec\"\nCOLUMN plan_hash_value FORMAT A15        HEADING \"Plan|Hash\"\nBREAK ON sql_id ON plan_hash_value SKIP 1\n\nACCEPT 1_sqlid PROMPT 'Enter SQL ID: '\nACCEPT 1_days DEFAULT 42 PROMPT 'Enter days in the past (default 42): '\n\nSELECT \n    sql_id,\n    TO_CHAR(plan_hash_value) plan_hash_value,\n    TO_CHAR(begin_interval_time, 'DD-MON-YYYY') s_time,\n    SUM(executions_delta) executions,\n    ROUND(SUM(elapsed_time_delta) \/ 1e6, 2) elapsed_t,\n    ROUND(SUM(cpu_time_delta) \/ 1e6, 2) cpu_t,\n    ROUND(SUM(iowait_delta) \/ 1e6, 2) user_io,\n    ROUND(SUM(buffer_gets_delta)) buffer_gets,\n    ROUND(SUM(disk_reads_delta)) disk_r,\n    ROUND(SUM(rows_processed_delta)) rows_processed,\n    DECODE(SUM(executions_delta), 0, ROUND(SUM(elapsed_time_delta) \/ 1e6, 2),\n        ROUND(SUM(elapsed_time_delta)\/1e6\/SUM(executions_delta), 4)) sec_per_exec\nFROM \n    dba_hist_sqlstat a, dba_hist_snapshot b\nWHERE \n    a.snap_id = b.snap_id\n    AND a.instance_number = b.instance_number\n    AND a.sql_id = '&amp;&amp;1_sqlid'\n    AND TRUNC(begin_interval_time) >= TRUNC(SYSDATE - &amp;&amp;1_days)\nGROUP BY \n    sql_id, plan_hash_value, TO_CHAR(begin_interval_time, 'DD-MON-YYYY')\nORDER BY \n    TO_DATE(TO_CHAR(begin_interval_time, 'DD-MON-YYYY'), 'DD-MON-YYYY'),\n    plan_hash_value;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udca1 Additional Purging Scenarios<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">Purge All Plans for a SQL_ID (if multiple child cursors exist)<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code><code>SELECT 'EXEC SYS.DBMS_SHARED_POOL.PURGE(''' || address || ', ' || hash_value || ''', ''C'');'\nFROM gv$sqlarea\nWHERE sql_id = 'f14nzf4gyq4y4';  -- Replace as needed<\/code><\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Run the generated <code>EXEC<\/code> lines to purge all memory plans.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Kill Sessions Using SQL_ID (if active)<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\"><code>SELECT <br>  s.sid, <br>  s.serial#, <br>  s.username, <br>  s.status<br>FROM <br>  gv$session s<br>JOIN <br>  gv$sqlarea a ON s.sql_id = a.sql_id<br>WHERE <br>  a.sql_id = 'f14nzf4gyq4y4';<br><br>-- Then kill using:<br>ALTER SYSTEM KILL SESSION 'sid,serial#' IMMEDIATE;<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udccc Use only with proper approval or coordination with the app team.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">\ud83d\udccc Summary<\/h2>\n\n\n\n<figure class=\"wp-block-table has-small-font-size\"><table><thead><tr><th>Scenario<\/th><th>Action<\/th><\/tr><\/thead><tbody><tr><td>SQL using bad plan in memory<\/td><td>Purge with <code>DBMS_SHARED_POOL.PURGE()<\/code><\/td><\/tr><tr><td>SQL ID has multiple cursors<\/td><td>Loop purge for all cursors<\/td><\/tr><tr><td>Active sessions exist<\/td><td>Kill or request session pause<\/td><\/tr><tr><td>Need permanent fix<\/td><td>Use <a href=\"https:\/\/w3buddy.com\/blog\/notes\/performance-tuning\/create-a-sql-plan-baseline-in-oracle\/\">SQL Plan Baselines<\/a> instead<\/td><\/tr><\/tbody><\/table><\/figure>\n","protected":false},"excerpt":{"rendered":"<p>Purging a SQL plan means removing the compiled version of a SQL statement from Oracle&#8217;s shared pool (memory). This forces Oracle to reparse the SQL the next time it&#8217;s executed, which may result in a better execution plan if the current one is inefficient or outdated. Purging is a temporary memory cleanup step \u2014 it [&hellip;]<\/p>\n","protected":false},"template":"","meta":{"googlesitekit_rrm_CAowu461DA:productID":""},"categories":[952,992],"class_list":["post-4304","cposts","type-cposts","status-publish","hentry","category-notes","category-performance-tuning"],"_links":{"self":[{"href":"https:\/\/w3buddy.com\/blog\/wp-json\/wp\/v2\/cposts\/4304","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/w3buddy.com\/blog\/wp-json\/wp\/v2\/cposts"}],"about":[{"href":"https:\/\/w3buddy.com\/blog\/wp-json\/wp\/v2\/types\/cposts"}],"wp:attachment":[{"href":"https:\/\/w3buddy.com\/blog\/wp-json\/wp\/v2\/media?parent=4304"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/w3buddy.com\/blog\/wp-json\/wp\/v2\/categories?post=4304"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}