These are just a few SQL scripts I use to find out about performance issues.
Sorry the page isn’t simple but WordPress doesn’t want to allow public access to text files, unlike images. Ho hum.
Use at your own risk!
Not validated for correctness or even working under any or all circumstances.
Think about what you are running in your database.
To run these you will need the Diagnostics and Tuning Licenses.
perf_advice - query advisors (unreliable at large allocations)perf_blockers - show blocking from gv$sessionperf_longquery - look through dba_hist_active_sess_history and see where we have queries - outside of transactions! - running longperf_longtrans - look through ASH history and see where we have transactions (XID) running longer than 3 minutesperf_monrep - generate monitoring report : dbms_perf.REPORT_SQLperf_pga - check the overall settings, generic SQL and ASH by processperf_planflip - show sql's with more than 1 PHVperf_sesstemp - show pga and TEMP for sessions, and grouped by Parallel QCperf_sga - show some SGA information, including subpools and resize operationsperf_spm_hints - show all of the hints associated with sql_profiles/_patch/_plan_baselinesperf_sqlcost - for a sql_id, show historic costs; dba_hist_sqlstat, ASH, GV$SQL infoperf_sqlobj - show object information associated with a SQL_IDperf_sqlplan - dbms_xplan.display_cursor, awr, manual extract of plan inc some XML,perf_sqlstats - show stats and indexes related to OBJECTS in a sql_idperf_statsprefs - show global, inc hidden, and table stats prefsperf_stathist - show stat history for a table + operational duration of each gatherperf_tabfrag - check dbms_space to see how fragmented a table has become
perf_advice– query advisors (not accurate for very large settings)
-- perf_advice.sql v1.0 n chandler---- pull advisor information from current PDBset lines 400 pages 5000 verify oncol cdb for a15col pdb for a15col name for a60col this_inst for a12col value for 999999999999999999999col pga_target_for_estimate for 999999999999999999999col STATISTIC for a80 wrapcol JAVA_POOL_SIZE_FOR_ESTIMATE heading JAVA_POOL_SZ_ESTIMATEcol PGA_TARGET_FOR_ESTIMATE heading PGA_TARG_ESTIMATEcol SHARED_POOL_SIZE_FOR_ESTIMATE heading S_POOL_SIZE_ESTIMATEcol SHARED_POOL_SIZE_FACTOR heading SIZE_FACTORcol this_pdb_name new_value this_pdb_nm for a15col this_cdb_name new_value this_cdb_nm for a15SELECT sys_context('userenv','cdb_name') AS this_cdb_name,sys_context('userenv','con_name') AS this_pdb_name FROM dual;spool perf_advice.out appendprompt ==========================================================================================prompt remember - this is an estimate and the larger the allocation, the less accurate it becomesprompt e.g. if your SGA is over 50G, its pretty unreliableprompt ==========================================================================================promptSELECT '&&this_cdb_nm' cdb, '&&this_pdb_nm' pdb,SYS_CONTEXT('userenv','instance') this_inst,inst_id,name,open_mode FROM gv$pdbs order by inst_id,name;PROMPT gv$aq_message_cache_adviceSELECT '&&this_cdb_nm' cdb, '&&this_pdb_nm' pdb,gva.* FROM gv$aq_message_cache_advice gva ORDER BY inst_id,size_for_estimate;PROMPT gv$java_pool_adviceSELECT '&&this_cdb_nm' cdb, '&&this_pdb_nm' pdb,gvjpa.* FROM gv$java_pool_advice gvjpa ORDER BY inst_id,java_pool_size_for_estimate;PROMPT gv$memory_target_adviceSELECT '&&this_cdb_nm' cdb, '&&this_pdb_nm' pdb,gvmta.* FROM gv$memory_target_advice gvmta ORDER BY inst_id,memory_size;PROMPT gv$mttr_target_adviceSELECT '&&this_cdb_nm' cdb, '&&this_pdb_nm' pdb,gvmttr.* FROM gv$mttr_target_advice gvmttr ORDER BY inst_id,mttr_target_for_estimate;PROMPT gv$pga_target_adviceSELECT '&&this_cdb_nm' cdb, '&&this_pdb_nm' pdb,gvpga.* FROM gv$pga_target_advice gvpga ORDER BY inst_id,pga_target_for_estimate;PROMPT gv$px_buffer_adviceSELECT '&&this_cdb_nm' cdb, '&&this_pdb_nm' pdb,gvpx.* FROM gv$px_buffer_advice gvpx ORDER BY inst_id,statistic;PROMPT gv$sga_target_adviceSELECT '&&this_cdb_nm' cdb, '&&this_pdb_nm' pdb,gvsga.* FROM gv$sga_target_advice gvsga ORDER BY inst_id,sga_size;PROMPT gv$shared_pool_adviceSELECT '&&this_cdb_nm' cdb, '&&this_pdb_nm' pdb,gvspa.* FROM gv$shared_pool_advice gvspa ORDER BY inst_id,shared_pool_size_for_estimate;PROMPT gv$streams_pool_adviceSELECT '&&this_cdb_nm' cdb, '&&this_pdb_nm' pdb,gvstream.* FROM gv$streams_pool_advice gvstream ORDER BY inst_id,streams_pool_size_for_estimate;spool off
perf_blockers – show blocking from gv$session
-- perf_blockers.sql v1.0 n chandler.-- v1.1 n chandler. materialize gv$session for improved performance and consistency - it will change across the 2 queries---- show blocking sessions, and then again with the SQL textset lines 250 pages 200col username_program for a30col wait_time_sec for 999,999.00col logon_time for a19col wait_class for a11 trunccol event for a64 trunccol status for a5 trunccol final_blocker for a13 trunccol program for a20 trunccol machine for a20 trunccol module for a10 trunccol service_name for a15 trunccol sid_serial_inst for a15col sql_text for a80 wrapcol osuser for a10 truncatespool perf_blockers.out appendprompt gv$session onlyWITH mysess AS (SELECT /*+ MATERIALIZE */ * FROM GV$SESSION)SELECT gvs.sid||','||gvs.serial#||'@'||gvs.inst_id sid_serial_inst ,LPAD(' ', (level-1)*2, '>') || NVL(username,REGEXP_REPLACE(program,'^.* ','')) username_program ,gvs.osuser ,gvs.lockwait ,gvs.status ,gvs.sql_id ,gvs.event ,DECODE(gvs.final_blocking_session||'@'||gvs.final_blocking_instance,'@','blocker!',gvs.final_blocking_session||'@'||gvs.final_blocking_instance) final_blocker ,TO_CHAR(gvs.logon_time,'YYYY-MM-DD HH24:MI:SS') logon_time ,wait_time_micro/1000000 wait_time_sec ,WAIT_CLASSfrom mysess gvswhere level > 1 or exists (SELECT 1 FROM mysess WHERE blocking_session = gvs.sid AND blocking_instance=gvs.inst_id)connect by prior gvs.sid = gvs.blocking_session and prior gvs.inst_id=gvs.blocking_instancestart with gvs.blocking_session is null/set lines 250prompt gv$session with sql_textWITH mysess AS (SELECT /*+ MATERIALIZE */ * FROM GV$SESSION), blocks as (SELECT /*+ MATERIALIZE */ gvs.sid||','||gvs.serial#||'@'||gvs.inst_id sid_serial_inst ,LPAD(' ', (level-1)*2, '>') || NVL(username,REGEXP_REPLACE(program,'^.* ','')) username_program ,gvs.osuser ,gvs.status ,gvs.sql_id ,gvs.prev_sql_id ,gvs.module ,gvs.machine ,gvs.program ,TO_CHAR(gvs.logon_time,'YYYY-MM-DD HH24:MI:SS') logon_time ,wait_time_micro/1000000 wait_time_sec ,wait_class ,gvs.service_name FROM mysess gvs WHERE level > 1 OR EXISTS (SELECT 1 FROM mysess WHERE blocking_session = gvs.sid AND blocking_instance=gvs.inst_id) CONNECT BY PRIOR gvs.sid = gvs.blocking_session AND PRIOR gvs.inst_id = gvs.blocking_instance START WITH gvs.blocking_session is null )SELECT blk.sid_serial_inst ,blk.username_program ,blk.status ,blk.module ,blk.machine ,blk.program ,blk.service_name ,blk.sql_id ,blk.prev_sql_id ,'| '||replace(gvsql.sql_text,CHR(13),' ') as sql_text FROM blocks blk LEFT OUTER JOIN gv$sql gvsql ON (blk.sql_id=gvsql.sql_id)/spool off
perf_longquery – look through dba_hist_active_sess_history and see where we have queries – outside of transactions! – running long
-- perf_longquery.sql v1.0 n chandler 2025-10-10-- v1.1 n chandler 2025-10-14 better grouping---- look through ASH history and see where we have queries - outside of transactions - running long-- excluding any Oracle internal users-- queries are also grouped by SQL_EXEC_ID and SQL_START_TIME as well as SID/SERIAL#/INST_ID to minimise-- catching the wrong start/edn times across repeated queries with the same SQL_ID.-- qc_sid_serial_instance shows if there was a query coordinator, and therefore if it's a parallel query slaveset line 2000 pages 5000 head on feed off trimspool oncol awr_retention for 9999999999col this_host for a30col this_cdb for a15col this_pdb for a15col transaction_id for a16col start_day for a9col start_time for a19col end_day for a9col end_time for a19col duration for a25col duration_seconds for 999,999,999col sid_serial_instance for a20col qc_sid_serial_instance for a20set colsep ","spool perf_longquery.out appendSELECT (SELECT EXTRACT(DAY FROM retention) AS awr_retention FROM dba_hist_wr_control WHERE con_id = SYS_CONTEXT('userenv','con_id') ) awr_retention ,SYS_CONTEXT('userenv','host') this_host ,SYS_CONTEXT('userenv','cdb_name') this_cdb ,SYS_CONTEXT('userenv','con_name') this_pdb ,sql_id sql_id ,sql_exec_id execution_number ,session_id||'.'||session_serial#||' @'||instance_number sid_serial_instance ,DECODE(qc_session_id||'.'||qc_session_serial#||' @'||qc_instance_id ,'. @',null ,qc_session_id||'.'||qc_session_serial#||' @'||qc_instance_id) qc_sid_serial_instance-- ,TO_CHAR(sql_exec_start,'YYYY-MM-DD HH24:MI:SS') sql_exec_start-- ,TO_CHAR(MIN(sample_time),'DAY') start_day ,TO_CHAR(MIN(sample_time),'YYYY-MM-DD HH24:MI:SS') start_time-- ,TO_CHAR(MAX(sample_time),'DAY') end_day ,TO_CHAR(MAX(sample_time),'YYYY-MM-DD HH24:MI:SS') end_time ,MAX(sample_time)-MIN(sample_time) duration ,EXTRACT(DAY FROM MAX(sample_time)-MIN(sample_time)) * 86400 + EXTRACT(HOUR FROM MAX(sample_time)-MIN(sample_time)) * 3600 + EXTRACT(MINUTE FROM MAX(sample_time)-MIN(sample_time)) * 60 + EXTRACT(SECOND FROM MAX(sample_time)-MIN(sample_time)) duration_seconds FROM dba_hist_active_sess_history-- FROM gv$active_session_history WHERE xid IS NULL AND sql_id IS NOT NULL AND sql_exec_id IS NOT NULL -- exclude oracle internal users AND user_id NOT IN (SELECT user_id FROM dba_users WHERE oracle_maintained='Y') -- restrict to last 32 days AND sample_time > systimestamp - INTERVAL '33' DAY GROUP BY sql_id,instance_number,session_id,session_serial#,qc_instance_id,qc_session_id,qc_session_serial#,sql_exec_id,sql_exec_start HAVING max(sample_time)-min(sample_time) > INTERVAL '20' SECOND ORDER BY duration,start_time/spool offset head on feed on colsep " "
perf_longtrans – look through ASH history and see where we have transactions (XID) running longer than 3 minutes
-- perf_longtrans.sql v1.1 n chandler 2025-10-10---- look through ASH history and see where we have transactions (XID) running longer than 3 minutes-- and show their SQL ID list so we can see what's going on-- useful for TAC processing and drain_timeout-- NOTE: queries outside of transactions are excluded (XID IS NULL) as are internal Oracle users-- this is a sample of a sample. the information is approximate. the exact start and end,-- and some mid-transaction SQL may be missedset line 2000 pages 0 trimspool on head off feed off verify off echo offcol awr_retention for 9999999999col this_host for a30col this_cdb for a15col this_pdb for a15col transaction_id for a16col start_day for a9col start_time for a19col end_day for a9col end_time for a19col duration for a25col duration_minutes for 999,999,999col sql_id_list for a80 wrapset colsep ","spool perf_longtrans.out appendprompt AWR_RETENTION,THIS_HOST ,THIS_CDB ,THIS_PDB ,TRANSACTION_ID ,START_DAY,START_TIME ,END_DAY ,END_TIME ,DURATION ,DURATION_MINUTES,SQL_ID_LISTSELECT (SELECT EXTRACT(DAY FROM retention) AS awr_retention FROM dba_hist_wr_control WHERE con_id = SYS_CONTEXT('userenv','con_id') ) awr_retention ,SYS_CONTEXT('userenv','host') this_host ,SYS_CONTEXT('userenv','cdb_name') this_cdb ,SYS_CONTEXT('userenv','con_name') this_pdb ,xid transaction_id ,TO_CHAR(MIN(sample_time),'DAY') start_day ,TO_CHAR(MIN(sample_time),'YYYY-MM-DD HH24:MI:SS') start_time ,TO_CHAR(MAX(sample_time),'DAY') end_day ,TO_CHAR(MAX(sample_time),'YYYY-MM-DD HH24:MI:SS') end_time ,MAX(sample_time)-MIN(sample_time) duration ,EXTRACT(DAY FROM MAX(sample_time)-MIN(sample_time)) * 1440 + EXTRACT(HOUR FROM MAX(sample_time)-MIN(sample_time)) * 60 + EXTRACT(MINUTE FROM MAX(sample_time)-MIN(sample_time)) duration_minutes ,LISTAGG(DISTINCT sql_id,' ' ON OVERFLOW TRUNCATE) WITHIN GROUP (ORDER BY sql_id) sql_id_list FROM dba_hist_active_sess_history-- FROM gv$active_session_history WHERE xid IS NOT NULL -- exclude oracle internal users AND user_id NOT IN (SELECT user_id FROM dba_users WHERE oracle_maintained='Y') -- maybe restrict to last 32 days-- AND sample_time > systimestamp - INTERVAL '33' DAY GROUP BY xid HAVING max(sample_time)-min(sample_time) > INTERVAL '3' MINUTE ORDER BY duration,start_time/set lines 250 pages 250 head on feed on colsep " "spool off
perf_monrep – generate monitoring report : dbms_perf.REPORT_SQL
perf_pga – check the overall settings, generic SQL and ASH by process
-- perf_pga.sql v0.9 n chandler---- check the overall settings, generic SQL and ASH by process-- needs to run as sysdba either from the CDB$ROOT or the container but will switch between the 2 to show different PGA-- aspects - PDB related and CDB related, which is a higher allocation-- IF you have multiple PDBs, you need to consider the allocation in those PDBs toocol HOST for a30col DT for a10col CDB for a12col PDB_NAME for a12col DISPLAY_VALUE for a30col CDB_VALUE for a30col SPFILE_VALUE for a30col PDB_VALUE for a30col inst_id for 9999col name for a40spool perf_pga.out append-- get the current pdbcol pdb_nm new_value con_name for a15select systimestamp,sys_context('userenv','con_name') pdb_nm from dual;prompt switch to CDB to get full parameter setalter session set container=CDB$ROOT;SELECT SYS_CONTEXT('userenv','host') host ,to_char(sysdate,'YYYY-MM-DD') dt ,SYS_CONTEXT('userenv','cdb_name') CDB ,vp.name pdb_name ,gvp.name ,gvp.inst_id ,gvp.display_value ,gvp.value cdb_value ,gvsp.sid||'.'||gvsp.value spfile_value ,decode(to_char(pdbs.spare2),'1','deleted ('||pdbs.value$||')','0',decode(pdbs.value$,null,'not set in PDB',pdbs.value$)) pdb_value ,gvp.isdefaultFROM gv$parameter gvpINNER JOIN gv$pdbs vp ON (gvp.inst_id=vp.inst_id)LEFT OUTER JOIN gv$spparameter gvsp ON (gvp.name=gvsp.name AND gvp.inst_id=gvsp.inst_id)LEFT OUTER JOIN pdb_spfile$ pdbs ON (gvp.name=pdbs.name AND pdbs.pdb_uid = vp.con_uid)WHERE 1=1 AND vp.name != 'PDB$SEED' AND gvp.name in ( 'sga_target' ,'sga_max_size' ,'db_name' ,'pga_aggregate_target' ,'pga_aggregate_limit' ,'parallel_max_server' ,'cpu_count' ,'parallel_threads_per_cpu' ,'db_cache_size' ,'shared_pool_size' ,'target_pdbs' ,'streams_pool_size' ,'large_pool_size' ,'java_pool_size' ,'parallel_force_local') ORDER BY cdb,vp.name,gvp.name,gvp.inst_id,gvsp.sid/prompt switch back to &&con_namealter session set container=&&con_name;col host for a30col cdb_name for a15col pdb_name for a15col time_now for a20col gb for 999,999,999,999.00col name for a64col value for 999,999,999,999,999prompt Get overall PGA informationprompt display gv$pgastat - only shows current container &&con_name (CDB$ROOT contains the full memory alloc)select sys_context('userenv','host') host ,sys_context('userenv','cdb_name') cdb_name ,sys_context('userenv','con_name') pdb_name ,to_char(sysdate,'YYYY-MM-DD HH24:MI:SS') time_now ,gvp.inst_id ,gvp.name ,gvp.con_id ,gvp.value ,gvp.unit ,DECODE(unit,'bytes',VALUE/1024/1024/1024) GB FROM gv$pgastat gvp ORDER BY host,cdb_name,pdb_name,inst_id,name/prompt MAX PGA Allocation for &&con_nameselect sys_context('userenv','host') host ,sys_context('userenv','cdb_name') cdb_name ,sys_context('userenv','con_name') pdb_name ,to_char(sysdate,'YYYY-MM-DD HH24:MI:SS') time_now ,gvp.inst_id ,gvp.name ,gvp.con_id ,gvp.value ,gvp.unit ,DECODE(unit,'bytes',VALUE/1024/1024/1024) GB FROM gv$pgastat gvp WHERE name = 'maximum PGA allocated' ORDER BY host,cdb_name,pdb_name,inst_id,name/alter session set container=CDB$ROOT;prompt MAX PGA allocation for the CDB$ROOTselect sys_context('userenv','host') host ,sys_context('userenv','cdb_name') cdb_name ,sys_context('userenv','con_name') pdb_name ,to_char(sysdate,'YYYY-MM-DD HH24:MI:SS') time_now ,gvp.inst_id ,gvp.name ,gvp.con_id ,gvp.value ,gvp.unit ,DECODE(unit,'bytes',VALUE/1024/1024/1024) GB FROM gv$pgastat gvp WHERE name = 'maximum PGA allocated' ORDER BY host,cdb_name,pdb_name,inst_id,name/prompt switch back to &&con_namealter session set container=&&con_name;col max_pga_allocated for 999,999,999,999,999col max_pga_allocated_GB for 999,999.0col max_temp_space_allocated_gb for 999,999.0col inst_id for 99999999col day_group for a10prompt look for peak PGA for sessionsprompt Top PGA consuming SQL from gv$active_session_history grouped by day, sql_id, phvSELECT inst_id ,to_char(sample_time,'YYYY-MM-DD') day_group ,sys_context('userenv','con_name') pdb_name ,sql_id ,sql_plan_hash_value ,MAX(pga_allocated) max_pga_allocated ,MAX(pga_allocated/1024/1024/1024) max_pga_allocated_GB ,MAX(temp_space_allocated/1024/1024/1024) max_temp_space_allocated_gb FROM gv$active_session_history GROUP BY inst_id,sys_context('userenv','con_name'),to_char(sample_time,'YYYY-MM-DD'), sql_id, sql_plan_hash_value ORDER BY max_pga_allocated DESC,sql_id FETCH FIRST 30 ROWS ONLY/prompt Top PGA consuming SQL from dba_hist_active_sess_history grouped by day, sql_id, phvSELECT to_char(sample_time,'YYYY-MM-DD') day_group ,sys_context('userenv','con_name') pdb_name ,sql_id ,sql_plan_hash_value ,MAX(pga_allocated) max_pga_allocated ,MAX(pga_allocated/1024/1024/1024) max_pga_allocated_GB ,MAX(temp_space_allocated/1024/1024/1024) max_temp_space_allocated_gb FROM dba_hist_active_sess_history WHERE pga_allocated IS NOT NULL GROUP BY sys_context('userenv','con_name'),to_char(sample_time,'YYYY-MM-DD'), sql_id, sql_plan_hash_value ORDER BY max_pga_allocated DESC,sql_id FETCH FIRST 30 ROWS ONLY/spool off
perf_planflip – show sql’s with more than 1 PHV
-- perf_planflip.sql v1.0 n chandler.---- go through wherever there is a plan hash value, pull it out and show all against all sql_ids-- where there is more than one plan across all locations-- DBA_HIST_SQL_PLAN does not have a parser, so we get all of those but for everywhere else, excluse-- all of the oracle maintained users, like SYS and SYSTEM as we probably don't care about those.set pages 200 lines 200col plan_hashes for a150col parsing_schema_name for a30spool perf_planflip.out appendprompt Show all SQL with multiple different PLAN_HASH_VALUES, ignoring ORACLE_MAINTAINED users except with plans from DBA_HIST_SQL_PLANprompt Data from GV$SQL, GV$ACTIVE_SESSION_HISTORY, DBA_HIST_ACTIVE_SESS_HISTORY and DBA_HIST_SQL_PLANWITH source_data AS ( SELECT distinct(plan_hash_value) phv, sql_id, parsing_schema_name FROM gv$sql gvs WHERE plan_hash_value != 0 AND parsing_schema_name NOT IN (SELECT username FROM dba_users WHERE oracle_maintained='Y') UNION ALL SELECT distinct(sql_plan_hash_value) phv, sql_id, (SELECT username FROM dba_users WHERE user_id = gvsh.user_id) parsing_schema_name FROM gv$active_session_history gvsh WHERE sql_plan_hash_value != 0 AND user_id NOT IN (SELECT user_id FROM dba_users WHERE oracle_maintained = 'Y') UNION ALL SELECT distinct(sql_plan_hash_value) phv, sql_id, (SELECT username FROM dba_users WHERE user_id = dhash.user_id) parsing_schema_name FROM dba_hist_active_sess_history dhash WHERE sql_plan_hash_value != 0 AND user_id NOT IN (SELECT user_id FROM dba_users WHERE oracle_maintained = 'Y') UNION ALL SELECT distinct(plan_hash_value) phv, sql_id, '.from DBA_HIST_SQL_PLAN' parsing_schema_name FROM dba_hist_sql_plan WHERE plan_hash_value != 0 ), grouped_data AS (SELECT parsing_schema_name,sql_id ,LISTAGG(distinct RPAD(phv,12), ': ' ON OVERFLOW TRUNCATE) WITHIN GROUP (ORDER BY phv) AS plan_hashes FROM source_data WHERE sql_id is not null GROUP BY parsing_schema_name,sql_id )SELECT parsing_schema_name,sql_id,plan_hashes FROM grouped_data WHERE LENGTH(plan_hashes) > 12 --AND sql_id = '&sql_id' ORDER BY parsing_schema_name, LENGTH(plan_hashes), sql_id/prompt Investigate individually with perf_sqlcost.sqlspool off
perf_sesstemp – show pga and TEMP for sessions, and grouped by Parallel QC
-- perf_sesstemp.sql v0.1 n chandler 2025-10-25 show temp usage by session, group parallel sessions by qc to sum them togetherset lines 250 pages 200col sid for a15col qc_sid for a15col osuser for a15 trunccol program for a15 trunccol temp_size_mb for 999,999,999col "PGA_ALLOC_MB (MAX)" for a20 trunccol total_temp_size_mb for 999,999,999,999col curr_time for a22 trunccol name for a15col db_unique_name for a15col this_pdb for a15spool perf_sesstemp.out appendSELECT TO_CHAR(systimestamp,'YYYY-MM-DD HH24:MI:SS') curr_time ,name ,db_unique_name ,SYS_CONTEXT('userenv','con_name') this_pdb FROM v$database;SELECT vs.SID||','||vs.serial#||'@'||vs.inst_id sid ,vs.username ,vs.osuser ,vs.sql_id ,REGEXP_REPLACE(vs.program,'.* ') program ,vs.status ,vsu.TABLESPACE ,ROUND ((( vsu.blocks * param.VALUE ) /1024/1024 ) ,2 ) temp_size_mb ,LPAD(ROUND(vp.PGA_ALLOC_MEM/1024/1024),7,' ')|| ' ('|| LPAD(ROUND(vp.PGA_MAX_MEM/1024/1024),7,' ')||')' "PGA_ALLOC_MB (MAX)" ,gpx.qcsid||','||gpx.qcserial#||'@'||gpx.qcinst_id qc_sid ,gpx.degree ,gpx.req_degree FROM gv$session vs INNER JOIN gv$process vp ON (vs.inst_id = vp.inst_id AND vs.paddr=vp.addr) INNER JOIN gv$parameter param ON (vs.inst_id = param.inst_id AND param.name='db_block_size') LEFT OUTER JOIN gv$sort_usage vsu ON (vs.inst_id = vsu.inst_id AND vs.saddr=vsu.session_addr) LEFT OUTER JOIN gv$px_session gpx ON (gpx.saddr=vs.saddr) WHERE vs.type <> 'BACKGROUND'ORDER BY vsu.TABLESPACE, vsu.blocks,vs.status desc/prompt Sessions grouped by Query Coordinator so all parallel slaves are added upwith base_data as (SELECT vs.SID||','||vs.serial#||'@'||vs.inst_id sid ,vs.username ,vs.osuser ,vs.sql_id ,REGEXP_REPLACE(vs.program,'.* ') program ,vs.status ,substr(status,1,1) status_bit ,vsu.TABLESPACE ,ROUND ((( vsu.blocks * param.VALUE ) /1024/1024 ) ,2 ) size_mb ,ROUND(vp.PGA_ALLOC_MEM/1024/1024) pga_alloc_mb ,ROUND(vp.PGA_MAX_MEM/1024/1024) pga_max_mb ,gpx.qcsid ,gpx.qcserial# ,gpx.qcinst_id FROM gv$session vs INNER JOIN gv$process vp ON (vs.inst_id = vp.inst_id AND vs.paddr=vp.addr) INNER JOIN gv$parameter param ON (vs.inst_id = param.inst_id AND param.name='db_block_size') LEFT OUTER JOIN gv$sort_usage vsu ON (vs.inst_id = vsu.inst_id AND vs.saddr=vsu.session_addr) LEFT OUTER JOIN gv$px_session gpx ON (gpx.saddr=vs.saddr) WHERE vs.type <> 'BACKGROUND'ORDER BY vsu.TABLESPACE ,vsu.segfile# ,vsu.segblk# ,vsu.blocks )select qcsid ,qcserial# ,qcinst_id ,sql_id ,LISTAGG(DISTINCT status_bit,' ' ON OVERFLOW TRUNCATE) statuses ,sum(pga_alloc_mb) ,sum(pga_max_mb) ,sum(size_mb) total_temp_size_mb ,count(*) num_parallel_procs FROM base_data WHERE qcsid IS NOT NULL GROUP BY qcsid,qcserial#,qcinst_id,sql_id ORDER BY qcsid,qcserial# NULLS FIRST,qcinst_id/spool off
perf_sga – show some SGA information, including subpools and resize operations
-- perf_sga.sql v0.8 n chandler 2025-10-28---- check the overall settings, generic SQL and ASH by process-- needs to run as sysdba either from the CDB$ROOT or the container but will switch between the 2 to show different SGA-- aspects - PDB related and CDB related, which is a higher allocation-- If you have multiple PDBs, you need to consider the allocation in those PDBs toocol HOST for a30col DT for a10col CDB for a12col PDB_NAME for a12col DISPLAY_VALUE for a30col CDB_VALUE for a30col SPFILE_VALUE for a30col PDB_VALUE for a30col inst_id for 9999col name for a40col mb for 999,999,999,999col gets for 999,999,999,999spool perf_sga.out append-- get the current pdbcol pdb_nm new_value con_name for a15select systimestamp,sys_context('userenv','con_name') pdb_nm from dual;prompt switch to CDB to get full parameter setalter session set container=CDB$ROOT;SELECT SYS_CONTEXT('userenv','host') host ,to_char(sysdate,'YYYY-MM-DD') dt ,SYS_CONTEXT('userenv','cdb_name') CDB ,vp.name pdb_name ,gvp.name ,gvp.inst_id ,gvp.display_value ,gvp.value cdb_value ,gvsp.sid||'.'||gvsp.value spfile_value ,decode(to_char(pdbs.spare2),'1','deleted ('||pdbs.value$||')','0',decode(pdbs.value$,null,'not set in PDB',pdbs.value$)) pdb_value ,gvp.isdefaultFROM gv$parameter gvpINNER JOIN gv$pdbs vp ON (gvp.inst_id=vp.inst_id)LEFT OUTER JOIN gv$spparameter gvsp ON (gvp.name=gvsp.name AND gvp.inst_id=gvsp.inst_id)LEFT OUTER JOIN pdb_spfile$ pdbs ON (gvp.name=pdbs.name AND pdbs.pdb_uid = vp.con_uid)WHERE 1=1 AND vp.name != 'PDB$SEED' AND gvp.name in ( 'sga_target' ,'sga_max_size' ,'db_name' ,'pga_aggregate_target' ,'pga_aggregate_limit' ,'parallel_max_server' ,'cpu_count' ,'parallel_threads_per_cpu' ,'db_cache_size' ,'shared_pool_size' ,'target_pdbs' ,'streams_pool_size' ,'large_pool_size' ,'java_pool_size' ,'parallel_force_local') ORDER BY cdb,vp.name,gvp.name,gvp.inst_id,gvsp.sid/prompt switch back to &&con_namealter session set container=&&con_name;col value for a30prompt FROM gv$sga_infoselect gvsga.inst_id ,gvsga.con_id ,NVL((SELECT name FROM gv$pdbs gvp WHERE gvp.con_id =gvsga.con_id AND gvp.inst_id=gvsga.inst_id),'CDB') pdb_name ,gvsga.name ,gvsga.bytes ,ROUND(gvsga.bytes/1024/1024) mb ,gvsga.resizeable FROM gv$sgainfo gvsga ORDER BY inst_id,con_id,name;prompt FROM gv$sgastatSELECT gvsga.inst_id ,gvsga.con_id ,NVL((SELECT name FROM gv$pdbs gvp WHERE gvp.con_id =gvsga.con_id AND gvp.inst_id=gvsga.inst_id),'CDB') pdb_name ,gvsga.pool ,'free' ,round(sum(gvsga.bytes/1024/1024)) MB FROM gv$sgastat gvsga WHERE pool IS NOT NULL AND name = 'free memory' GROUP BY inst_id,con_id,poolUNION ALLSELECT gvsga.inst_id ,gvsga.con_id ,NVL((SELECT name FROM gv$pdbs gvp WHERE gvp.con_id =gvsga.con_id AND gvp.inst_id=gvsga.inst_id),'CDB') pdb_name ,gvsga.pool ,'used' ,round(sum(gvsga.bytes/1024/1024)) mb FROM gv$sgastat gvsgaWHERE pool IS NOT NULL AND name != 'free memory'GROUP BY gvsga.inst_id,gvsga.con_id,gvsga.poolUNION ALLSELECT gvsga.inst_id ,gvsga.con_id ,NVL((SELECT name FROM gv$pdbs gvp WHERE gvp.con_id =gvsga.con_id AND gvp.inst_id=gvsga.inst_id),'CDB') pdb_name ,gvsga.pool ,gvsga.name ,round(gvsga.bytes/1024/1024) MB FROM gv$sgastat gvsgaWHERE gvsga.pool IS NULLORDER BY 1,4 NULLS LAST,2/prompt How many subpools in shared pool (this instance only)SELECT COUNT(DISTINCT kghluidx) num_subpools FROM sys.x$kghlu WHERE kghlushrpool = 1;prompt latch children for shared pool (which subpools are used)prompt is one exponentially more used than the others? if so, why?SELECT inst_id,child#, gets FROM gv$latch_children WHERE name = 'shared pool' ORDER BY inst_id,child#;prompt get subpool breakdown from CDB$ROOTalter session set container=CDB$ROOT;prompt X$KSMSS subpool high level info ON THIS NODEprompt Are the sizes fairly balanced across subpools or is one dominatingSELECT xksmss.inst_id ,xksmss.con_id ,(SELECT NVL(name,'CDB') FROM v$pdbs gvp WHERE gvp.con_id = xksmss.con_id) pdb_name ,xksmss.ksmdsidx subpool ,'shared pool' pool ,'free memory' name ,SUM(xksmss.ksmsslen)/1024/1024 MB FROM x$ksmss xksmss WHERE xksmss.ksmssnam='free memory' GROUP BY xksmss.inst_id,xksmss.con_id,xksmss.ksmdsidxUNION ALLSELECT xksmss.inst_id ,xksmss.con_id ,(SELECT NVL(name,'CDB') FROM v$pdbs gvp WHERE gvp.con_id =xksmss.con_id) pdb_name ,xksmss.ksmdsidx subpool ,'shared pool' pool ,'used' name ,SUM(xksmss.ksmsslen)/1024/1024 MB FROM x$ksmss xksmss WHERE xksmss.ksmssnam!='free memory' GROUP BY xksmss.inst_id,xksmss.con_id,xksmss.ksmdsidx ORDER BY 1,4,2;prompt first 50 biggest chunks in the SGA subpools on THIS NODE, excluding con_id 1 and 2SELECT xksmss.inst_id ,xksmss.con_id ,(SELECT NVL(name,'CDB') FROM v$pdbs gvp WHERE gvp.con_id =xksmss.con_id) pdb_name ,xksmss.ksmdsidx subpool ,'shared pool' pool ,xksmss.ksmssnam name ,xksmss.ksmsslen/1024/1024 MB FROM x$ksmss xksmss WHERE xksmss.ksmssnam!='free memory' AND xksmss.con_id not in (1,2) ORDER BY inst_id,mb desc,subpool,con_idFETCH FIRST 50 ROWS ONLY/col db_block_size for a15prompt an overview of the db_cache from the CDBprompt We are mainly looking to see if the db_cache is underused (lots of free space)SELECT gvbh.inst_id ,gvbh.con_id ,NVL((SELECT name FROM gv$pdbs gvp WHERE gvp.con_id = gvbh.con_id AND gvp.inst_id=gvbh.inst_id),'CDB') pdb_name ,gvbh.status ,count(*) num_blocks ,(count(*) * gvp.value)/1024/1024 MB ,gvp.value db_block_size FROM gv$bh gvbh LEFT OUTER JOIN gv$parameter gvp ON (gvp.name = 'db_block_size' AND gvp.inst_id = gvbh.inst_id) GROUP BY gvbh.inst_id,gvbh.con_id,gvbh.status,gvp.value ORDER BY gvbh.inst_id,gvbh.con_id,gvbh.status,gvp.value/prompt back to PDB &&con_namealter session set container=&&con_name;prompt resize operations, transferring memory between poolsprompt check the size of the db_cache(s) and ensure they have not become too smallprompt Check for IMMEDIATE GROW operations of a pool, which indicates it was going to run out of memoryprompt Check that the resizes were successful.prompt ERROR status can indicate memory leaks, lack of overall memory for this system and/or other memory issuescol component for a40col parameter for a40SELECT inst_id ,con_id ,NVL((SELECT name FROM gv$pdbs gvp WHERE gvp.con_id = gvsro.con_id AND gvp.inst_id=gvsro.inst_id),'CDB') pdb_name ,component ,oper_type ,oper_mode ,parameter ,ROUND(initial_size/1024/1024) initial_size_mb ,ROUND(target_size/1024/1024) target_size_mb ,ROUND(final_size/1024/1024) final_size_mb ,ROUND(final_size-initial_size)/1024/1024 change_mb ,status ,start_time ,end_time FROM gv$sga_resize_ops gvsro ORDER BY inst_id,start_time,component,parameter/spool off
perf_spm_hints – show all of the hints associated with sql_profiles, sql_patches and sql_plan_baselines
-- perf_spm_hints.sql n chandler. v1.1 2025-09-11: show all SPM and the SPM hints-- n chandler. v1.2 2025-11-11: commentset lines 250 pages 5000col typ for a30col name for a50col signature for 99999999999999999999col category for a14col outline_hints for a140 wrapcol comp_data for a100col con_name for a15col status for a20set long 199col sp1 new_value sp2select name||'-'||sys_context('userenv','con_name') sp1 from v$database;spool perf_spm_hints.&&sp2..outprompt Show all profiles, patches and baselinesWITH spm_list as ( SELECT 'dba_sql_profiles' typ, con_id,1 as obj_type, name,signature,status,category FROM CONTAINERS(dba_sql_profiles) UNION ALL SELECT 'dba_sql_plan_baselines' typ, con_id, 2 as obj_type, plan_name as name,signature,DECODE(enabled,'YES','ENABLED','NOT ENABLED')||':'||DECODE(accepted,'YES','ACCEPTED','NOT ACCEPTED') as status,null as category FROM CONTAINERS(dba_sql_plan_baselines) UNION ALL SELECT 'dba_sql_patches' typ, con_id,3 as obj_type, name,signature,status,category FROM CONTAINERS(dba_sql_patches) )SELECT s.con_id, s.obj_type, s.typ, s.name, s.signature, s.status, s.category FROM spm_list sORDER BY s.obj_type,s.con_id,s.status,s.name/WITH spm_list as ( SELECT 'dba_sql_profiles' typ, con_id, 1 as obj_type, name,signature,status,category FROM CONTAINERS(dba_sql_profiles) UNION ALL SELECT 'dba_sql_plan_baselines' typ, con_id, 2 as obj_type, plan_name as name,signature,DECODE(enabled,'YES','ENABLED','NOT ENABLED')||':'||DECODE(accepted,'YES','ACCEPTED','NOT ACCEPTED') as status,null as category FROM CONTAINERS(dba_sql_plan_baselines) UNION ALL SELECT 'dba_sql_patches' typ, con_id, 3 as obj_type, name,signature,status,category FROM CONTAINERS(dba_sql_patches) ), profiles as (select name,signature,category,obj_type,hint as outline_hints from (select p.name, p.signature, p.category, sd.obj_type, row_number() over (partition by sd.signature, sd.category order by sd.signature) row_num, extractValue(value(t), '/hint') hint from CONTAINERS(sys.sqlobj$data) sd JOIN spm_list p on (p.signature=sd.signature and p.con_id = sd.con_id and p.obj_type=sd.obj_type), table(xmlsequence(extract(xmltype(sd.comp_data), '/outline_data/hint'))) t ) order by row_num ), baselines as (select name,signature,category,obj_type,hint as outline_hints from (select p.name, p.signature, p.category, sd.obj_type, row_number() over (partition by sd.signature, sd.category order by sd.signature) row_num, extractValue(value(t), '/hint') hint from CONTAINERS(sys.sqlobj$plan) sd JOIN spm_list p on (p.signature=sd.signature and p.con_id = sd.con_id and p.obj_type=sd.obj_type), table(xmlsequence(extract(xmltype(sd.other_xml), '/other_xml/outline_data/hint'))) t where 1=1 and other_xml is not null ) order by row_num ), patches as (select name,signature,category,obj_type,hint as outline_hints from (select p.name, p.signature, p.category, sd.obj_type, row_number() over (partition by sd.signature, sd.category order by sd.signature) row_num, extractValue(value(t), '/hint') hint from CONTAINERS(sys.sqlobj$plan) sd JOIN spm_list p on (p.signature=sd.signature and p.con_id = sd.con_id and p.obj_type=sd.obj_type), table(xmlsequence(extract(xmltype(sd.other_xml), '/other_xml/outline_data/hint'))) t where 1=1 and other_xml is not null ) order by row_num )SELECT * FROM profilesUNION ALLSELECT * FROM baselinesUNION ALLSELECT * FROM patchesorder by 1,3/spool offprompt example dbms_xplan to show hints: select * from dbms_xplan.DISPLAY_SQL_PLAN_BASELINE(plan_name=>'SQL_PLAN_g0t8ntd7ck5kd8a6d2c10',FORMAT=>'OUTLINE');
perf_sqlcost – for a sql_id, show historic costs; dba_hist_sqlstat, ASH, GV$SQL info
-- perf_sqlcost.sql v1.0 n chandler-- v1.1 n chandler 2025-11-11 use NULLIF, add elapsed time per execution per row-- v1.2 n chandler 2025-11-11 add dba_hist_sqlstat-- v1.3 n chandler 2025-11-12 add extra fudge of figures if executions or row_processed is 0-- v1.4 n chandler 2025-11-28 add per-execution info from ASH-- v1.5 and bug fix-- v1.6 n chandler 2025-12-24 sort elapsed per row. ignore null sql_exec_id-- v1.7 n chandler 2025-12-30 tweak output from micro to ms---- show plans and time for a given sql_idset lines 250 pages 200 verify offcol sql_id for a15col profile for a7col patch for a5col baseline for a8col is_bind_sensitive for a17col is_bind_aware for a13col is_shareable for a12col inst_id for 9999col sql_profile for a10 truncatecol PARSING_SCHEMA_NAME for a12 truncatecol "SQL_ID (CHILD)" for a18 truncateset long 1000000define sql_idundefine sql_idspool perf_sqlcost.&&sql_id..out appendprompt SQL Full Textset long 200000col sql_text for a199select sql_text from dbA_hist_sqltext where sql_id = '&&sql_id' AND rownum=1;col cost for 999,999,999,999col cpu_cost for 999,999,999,999col io_cost for 999,999,999,999col timestamp for a25prompt Get some execution history dba_hist_sqlstatSELECT TO_CHAR(snap.end_interval_time,'YYYY-MM-DD HH24:MI:SS') end_interval_time ,dhs.instance_number ,snap.snap_id ,snap.con_id ,dhs.dbid ,dhs.sql_id ,dhs.plan_hash_value ,dhs.optimizer_cost ,dhs.sql_profile ,dhs.parsing_schema_name ,dhs.executions_delta ,dhs.invalidations_delta ,dhs.rows_processed_delta ,dhs.elapsed_time_delta ,ROUND(dhs.elapsed_time_delta/NULLIF(dhs.executions_delta,0)/NULLIF(dhs.rows_processed_delta,0)) elapsed_per_exec_per_row_micro -- ,dhs.io_offload_elig_bytes_delta -- ,dhs.io_interconnect_bytes_delta FROM dba_hist_sqlstat dhs JOIN dba_hist_snapshot snap on (dhs.snap_id = snap.snap_id AND dhs.instance_number = snap.instance_number) JOIN v$pdbs pdbs ON (pdbs.dbid = dhs.dbid) WHERE sql_id = '&&sql_id' AND snap.con_id = sys_context('userenv','con_id') AND dhs.executions_delta > 0 ORDER BY snap.end_interval_time,dhs.dbid,snap.snap_id;prompt Plan and cost from DBA_HIST_SQL_PLAN for &&sql_idSELECT sql_id ,plan_hash_value ,cost ,cpu_cost ,io_cost ,to_char(timestamp,'YYYY-MM-DD HH24:MI:SS') timestamp FROM dba_hist_sql_plan WHERE sql_id = '&&sql_id' AND other_xml is not null ORDER BY cost,sql_id,timestamp;prompt Plans and basic info from DBA_HIST_ACTIVE_SESS_HISTORYSELECT instance_number inst_id ,sql_id||' ('||sql_child_number||')' "SQL_ID (CHILD)" ,sql_plan_hash_value ,to_char(sample_time,'YYYY-MM-DD HH24:MI') sample_time_minute_group ,ROUND(MAX(pga_allocated)/1024/1024) max_pga_allocated_mb ,ROUND(MAX(temp_space_allocated)/1024/1024) max_temp_space_allocated_mb ,count(*) ash_count FROM dba_hist_active_sess_history WHERE sql_id = '&&sql_id' GROUP BY instance_number,sql_id||' ('||sql_child_number||')',sql_plan_hash_value,to_char(sample_time,'YYYY-MM-DD HH24:MI') ORDER BY 1,2,4,3/col sample_time_minute_group for a25prompt Plans and basic info from GV$ACTIVE_SESSION_HISTORYSELECT inst_id ,sql_id||' ('||sql_child_number||')' "SQL_ID (CHILD)" ,sql_plan_hash_value ,to_char(sample_time,'YYYY-MM-DD HH24:MI') sample_time_minute_group ,ROUND(MAX(pga_allocated)/1024/1024) max_pga_allocated_mb ,ROUND(MAX(temp_space_allocated)/1024/1024) max_temp_space_allocated_mb ,count(*) ash_count FROM gv$active_session_history WHERE sql_id = '&&sql_id' GROUP BY inst_id,sql_id||' ('||sql_child_number||')',sql_plan_hash_value,to_char(sample_time,'YYYY-MM-DD HH24:MI') ORDER BY 1,2,4,3/col duration for a30col start_time_approx for a30col end_time_approx for a30prompt Plans and basic info from GV$ACTIVE_SESSION_HISTORY EVERY RUNSELECT inst_id ,sql_id||' ('||sql_child_number||')' "SQL_ID (CHILD)" ,sql_plan_hash_value ,sql_exec_id ,sql_exec_start ,MIN(sample_time) start_time_approx ,MIN(sample_time) end_time_approx ,(MAX(sample_time)-MIN(sample_time)) duration ,ROUND(MAX(pga_allocated)/1024/1024) max_pga_allocated_mb ,ROUND(MAX(temp_space_allocated)/1024/1024) max_temp_space_allocated_mb ,count(*) ash_count FROM gv$active_session_history WHERE sql_id = '&&sql_id' AND sql_exec_id IS NOT NULL GROUP BY inst_id,sql_exec_id,sql_exec_start,sql_id||' ('||sql_child_number||')',sql_plan_hash_value ORDER BY 1,2,4,3/prompt Plans and speed in GV$SQL for &&sql_id - total per child, per row and per executionSELECT inst_id ,sql_id||' ('||child_number||')' "SQL_ID (CHILD)" ,plan_hash_value ,parsing_schema_name ,DECODE(sql_profile,null,'N','Y') profile ,DECODE(sql_patch,null,'N','Y') patch ,DECODE(sql_plan_baseline,null,'N','Y') baseline ,is_bind_sensitive ,is_bind_aware ,is_shareable ,executions ,rows_processed ,ROUND(rows_processed/NULLIF(executions,0))rows_per_exec ,ROUND(elapsed_time/1000,1) elapsed_time_ms ,ROUND(elapsed_time/NULLIF(rows_processed,0)/1000,1) elapsed_per_row_ms ,ROUND(elapsed_time/NULLIF(executions,0)/1000,1) elapsed_per_exec_ms FROM gv$sql WHERE sql_id = '&&sql_id' ORDER BY inst_id,sql_id,child_number,plan_hash_value/col IO_CELL_OFFLOAD_ELIGIBLE_MB for 999,999,999,999col IO_CELL_OFFLOAD_RETURNED_MB for 999,999,999,999col IO_CELL_OFFLOAD_ELIGIBLE_MB for 999,999,999,999col IO_CELL_UNCOMPRESSED_MB for 999,999,999,999col PHYSICAL_READ_MB for 999,999,999,999col IO_CELL_OFFLOAD_ELIGIBLE_MB for 999,999,999,999col OPTIMIZED_PHY_READ_REQUESTS for 999,999,999,999--PHYSICAL_READ_BYTES — number of bytes read by the SQL--PHYSICAL_READ_REQUESTS — number of read requests issued by the SQL--PHYSICAL_WRITE_BYTES — number of bytes written by the SQL--PHYSICAL_WRITE_REQUESTS — number of write requests issued by the SQL--IO_CELL_OFFLOAD_ELIGIBLE_BYTES — number of bytes eligible for predicate offload to Exadata storage server--IO_CELL_OFFLOAD_RETURNED_BYTES — number of bytes returned by smart scans--IO_INTERCONNECT_BYTES — number of bytes exchanged between the database and the storage servers--IO_CELL_UNCOMPRESSED_BYTES — number of uncompressed bytes read, where uncompressed bytes is the size after decompression--OPTIMIZED_PHY_READ_REQUESTS — number of read requests satisfied from Exadata Smart Flash Cache, or read requests avoided due to storage index or columnar cacheprompt Offloadingprompt hard to be conclusive for multiple executions...prompt could have been 1 good and 2 bad, etc...SELECT sql_id ,child_number ,plan_hash_value phv ,executions execs ,ROUND(IO_CELL_OFFLOAD_ELIGIBLE_BYTES /1024/1024) IO_CELL_OFFLOAD_ELIGIBLE_MB ,ROUND(IO_CELL_OFFLOAD_RETURNED_BYTES /1024/1024) IO_CELL_OFFLOAD_RETURNED_MB ,ROUND(IO_CELL_UNCOMPRESSED_BYTES /1024/1024) IO_CELL_UNCOMPRESSED_MB ,ROUND(IO_INTERCONNECT_BYTES/1024/1024) IO_INTERCONNECT_MB ,ROUND(PHYSICAL_READ_BYTES/1024/1024) PHYSICAL_READ_MB ,ROUND(OPTIMIZED_PHY_READ_REQUESTS) OPTIMIZED_PHY_READ_REQUESTS ,100 - (100*(IO_INTERCONNECT_BYTES) / NULLIF(IO_CELL_OFFLOAD_ELIGIBLE_BYTES,0)) PCT_OFFLOADED from gv$sql swhere executions > 0 AND sql_id = '&&sql_id'/spool off
perf_sqlobj – show object information associated with a SQL_ID
-- perf_sqlobj.sql v1.0 n chandler.-- v1.1 n chandler. 2025-11-20 account for covering indexes-- v1.2 n chandler. 2025-11-28 formatting some columns--prompt show object relevant statistics for tables/indexes accessed by a sql_idprompt object names are pulled out of dba_hist_sql_plan and basic info shownprompt this should help you determine staleness, selectivity and cardinality, and identify alternate execution optionsprompt also ensure SAMPLE is 0 or 100% - anything else is using ESTIMATE which is less stable and can lead to bad histogramsset pages 200 lines 250col OWNER for a30col TABLE_NAME for a30col OBJECT_OWNER for a30col COLUMN_NAME for a30col PARTITION_NAME for a15col SUBPARTITION_NAME for a30col STALE_STATS for a5col SAMPLE for a5col OBJECTS for a80col plan_hash_values for a80 wrapcol object_type for a10col density for 0.999999999999999col num_distinct for 999999999999999col num_nulls for 999999999999999define sql_idundefine sql_idbreak on index_nameprompt Enter the sql_id of the query to pull all objects out of the execution plansprompt &&sql_idspool perf_sqlstats.&&sql_id..out appendpromptprompt What objects are in the execution plans:prompt all objects may not appear in all executions plansprompt - different indexes can be selectedprompt - objects can be transformed out of plansprompt - covering indexes can eleminate table accessWITH objects as ( SELECT distinct dhsp.object_type||': '||dhsp.object_owner||'.'||dhsp.object_name objects ,dhsp.sql_id ,dhsp.plan_hash_value FROM dba_hist_sql_plan dhsp WHERE 1=1 AND ( dhsp.object_type like '%TABLE%' OR dhsp.object_type like '%INDEX%' ) AND sql_id = '&&sql_id' UNION SELECT distinct gvp.object_type||': '||gvp.object_owner||'.'||gvp.object_name objects ,gvp.sql_id ,gvp.plan_hash_value FROM gv$sql_plan gvp WHERE 1=1 AND ( gvp.object_type like '%TABLE%' OR gvp.object_type like '%INDEX%' ) AND sql_id = '&&sql_id' ORDER BY 1)SELECT objects,sql_id,LISTAGG(plan_hash_value,' ' ON OVERFLOW TRUNCATE) WITHIN GROUP (order by plan_hash_value) plan_hash_values FROM objects GROUP BY objects,sql_id ORDER BY objects,sql_id/promptprompt object_stats for table and indexes used in the queryprompt if you are using a covering index on a partitioned table, partition stats will not showWITH objects AS ( SELECT dhsp.object_type ,dhsp.object_owner ,dhsp.object_name ,dhsp.sql_id FROM dba_hist_sql_plan dhsp WHERE 1=1 AND ( dhsp.object_type like '%TABLE%' OR dhsp.object_type like '%INDEX%' ) AND sql_id = '&&sql_id' UNION SELECT gvp.object_type ,gvp.object_owner ,gvp.object_name ,gvp.sql_id FROM gv$sql_plan gvp WHERE 1=1 AND ( gvp.object_type like '%TABLE%' OR gvp.object_type like '%INDEX%' ) AND sql_id = '&&sql_id' ORDER BY 1)SELECT objects.object_type ,NVL(dts.owner,dis.owner) owner ,NVL(dts.table_name,dis.table_name) table_name ,dis.index_name ,NVL(dts.partition_name,dis.partition_name) partition_name ,NVL(dts.subpartition_name,dis.subpartition_name) subpartition_name ,NVL(dts.global_stats,dis.global_stats) global_stats ,NVL(dts.num_rows,dis.num_rows) num_rows ,NVL(dts.blocks,dis.leaf_blocks) blocks ,NVL(dts.last_analyzed,dis.last_analyzed) last_analyzed ,NVL(dts.stale_stats,dis.stale_stats) stale_stats ,NVL(dts.stattype_locked,dis.stattype_locked) locked ,dts.avg_row_len ,dis.clustering_factor -- DECODE to prevent divide by 0 error ,TO_CHAR(ROUND((NVL(dts.sample_size,dis.sample_size)/NVL(DECODE(dts.num_rows,0,1,dts.num_rows),DECODE(dis.num_rows,0,1,dis.num_rows)))*100,2))||'%' sample FROM objects LEFT OUTER JOIN dba_tab_statistics dts ON (dts.owner = objects.object_owner AND dts.table_name=objects.object_name) LEFT OUTER JOIN dba_ind_statistics dis ON (dis.owner = objects.object_owner AND dis.index_name=objects.object_name)WHERE objects.sql_id = '&&sql_id'ORDER BY dts.owner,dts.table_name,dts.partition_name NULLS FIRST,dts.subpartition_name NULLS FIRST,dis.index_name/col index_owner for a30col table_owner for a30col is_used_in_plan for a16promptprompt now show every index definition on the relevant tables so we can see if there could be an alternativeprompt you will need to look at the predicates / FILTER conditions in the execution plan to determineprompt if other indexes could be suitable for the query-- get all tables and indexes from the plan-- pull all table names associated with those objects-- and show indexes related to the table namesWITH feed AS ( SELECT dhsp.object_type ,dhsp.object_owner ,dhsp.object_name ,dhsp.sql_id FROM dba_hist_sql_plan dhsp WHERE 1=1 AND ( dhsp.object_type like '%TABLE%' OR dhsp.object_type like '%INDEX%' ) AND sql_id = '&&sql_id' UNION SELECT gvp.object_type ,gvp.object_owner ,gvp.object_name ,gvp.sql_id FROM gv$sql_plan gvp WHERE 1=1 AND ( gvp.object_type like '%TABLE%' OR gvp.object_type like '%INDEX%' ) AND sql_id = '&&sql_id' ORDER BY 1), -- now we need to add all of the tables for the indexes mentioned above in case of covering index objects AS ( SELECT ind.table_owner, ind.table_name, ind.index_name FROM dba_indexes ind JOIN feed obj ON (obj.object_owner=ind.table_owner AND (obj.object_name = ind.table_name OR obj.object_name = ind.index_name) ) )SELECT dic.table_owner ,dic.table_name ,dic.index_owner ,dic.index_name ,dic.column_name ,dic.column_position ,dic.descend ,(select 'YES' from feed WHERE dic.index_name IN (SELECT feed.object_name FROM feed) AND rownum = 1) is_used_in_plan FROM dba_ind_columns dic JOIN objects ob ON (dic.table_owner=ob.table_owner AND (dic.table_name = ob.table_name OR dic.index_name = ob.index_name)) GROUP BY dic.table_owner,dic.table_name,dic.index_owner,dic.index_name,dic.column_name,dic.column_position,dic.descend ORDER BY dic.table_owner,dic.table_name,is_used_in_plan desc NULLS LAST,dic.index_owner,dic.index_name,dic.column_position/col column_name for a30col data_type for a10 truncatecol nullable for a4col histogram for a30prompt and lets have a look at those columns, just in the table (global stats if partitioned)WITH feed AS ( SELECT dhsp.object_type ,dhsp.object_owner ,dhsp.object_name ,dhsp.sql_id FROM dba_hist_sql_plan dhsp WHERE 1=1 AND ( dhsp.object_type like '%TABLE%' OR dhsp.object_type like '%INDEX%' ) AND sql_id = '&&sql_id' UNION SELECT gvp.object_type ,gvp.object_owner ,gvp.object_name ,gvp.sql_id FROM gv$sql_plan gvp WHERE 1=1 AND ( gvp.object_type like '%TABLE%' OR gvp.object_type like '%INDEX%' ) AND sql_id = '&&sql_id' ORDER BY 1), -- now we need to add all of the tables for the indexes mentioned above in case of covering index objects AS ( SELECT ind.table_owner, ind.table_name, ind.index_name FROM dba_indexes ind JOIN feed obj ON (obj.object_owner=ind.table_owner AND (obj.object_name = ind.table_name OR obj.object_name = ind.index_name) ) )SELECT dtc.owner ,dtc.table_name ,dtc.column_name ,dtc.data_type ,dtc.nullable ,dtc.density ,dtc.num_distinct ,dtc.num_nulls ,dtc.histogram ,dtc.num_buckets FROM dba_tab_columns dtc JOIN objects ON (dtc.owner=objects.table_owner AND dtc.table_name = objects.table_name) GROUP BY dtc.owner ,dtc.table_name ,dtc.column_name ,dtc.data_type ,dtc.nullable ,dtc.density ,dtc.num_distinct ,dtc.num_nulls ,dtc.histogram,dtc.num_buckets,dtc.column_id ORDER BY dtc.owner,dtc.table_name,dtc.column_id/-- it would be handy to extract predicates from the filter to concentrate on the ones I care aboutspool offbreak on xxx_xxx
perf_stathist – show statistics history for a table
-- perf_stathist.sql n chandler v1.0 2025-11-11 show history of stats for a table-- v2.0 2026-06-28 also view statistic operation history so you can see how long each bit tookalter session set nls_date_format='YYYY-MM-DD HH24:MI:SS';alter session set nls_timestamp_format='YYYY-MM-DD HH24:MI:SS';alter session set nls_timestamp_tz_format='YYYY-MM-DD HH24:MI:SS';define table_namedefine ownerundefine table_nameundefine ownercol owner for a30col table_name for a30col partition_name for a30col subpartition_name for a30prompt Locate table and ownerSELECT owner,table_name FROM dba_tables WHERE UPPER(table_name) LIKE UPPER('%&&table_name%') ORDER BY owner,table_name/prompt stats historyundefine table_nameprompt Enter Owner and table_name. Wildcards accepted.prompt &&ownerprompt &&table_namespool perf_stathist.&&owner..&&table_name..out appendwith jpl_tab_history ( owner, table_name, partition_name, subpartition_name, num_rows, blocks, avg_row_len, sample_size, last_analyzed) as ( select u.name, o.name, null, null, h.rowcnt, h.blkcnt, h.avgrln, h.samplesize, h.analyzetime from sys.user$ u, sys.obj$ o, sys.wri$_optstat_tab_history h where h.obj# = o.obj# and o.type# = 2 and o.owner# = u.user# and h.savtime <= systimestamp union all -- partitions select u.name, o.name, o.subname, null, h.rowcnt, h.blkcnt, h.avgrln, h.samplesize, h.analyzetime from sys.user$ u, sys.obj$ o, sys.wri$_optstat_tab_history h where h.obj# = o.obj# and o.type# = 19 and o.owner# = u.user# and h.savtime <= systimestamp union all -- sub partitions select u.name, osp.name, ocp.subname, osp.subname, h.rowcnt, h.blkcnt, h.avgrln, h.samplesize, h.analyzetime from sys.user$ u, sys.obj$ osp, sys.obj$ ocp, sys.tabsubpart$ tsp, sys.wri$_optstat_tab_history h where h.obj# = osp.obj# and osp.type# = 34 and osp.obj# = tsp.obj# and tsp.pobj# = ocp.obj# and osp.owner# = u.user# and h.savtime <= systimestamp)SELECT owner ,table_name ,partition_name ,subpartition_name ,blocks ,num_rows ,avg_row_len ,sample_size ,last_analyzed FROM jpl_tab_history WHERE 1=1 AND owner LIKE NVL('%&&owner%','%') AND table_name LIKE NVL('%&&table_name%','%')ORDER BY owner,table_name,partition_name NULLS FIRST,subpartition_name NULLS FIRST,last_analyzed;col notes for a10 trunccol target for a80col start_time for a20col end_time for a20col operation for a30col tgt for a80col duration for a23 truncatecol status for a12col target_type for a15col job_name for a22prompt Show statistics gathering operations (the last 20 operations)prompt The automatic jobs show with a target of "AUTO"SELECT * FROM dba_optstat_operations ORDER BY start_time descFETCH FIRST 20 ROWS ONLY/define opidundefine opidPROMPT Enter the operation id for details of that operationSELECT opid ,target ,target_objn ,target_type ,target_size ,start_time ,end_time ,end_time-start_time duration ,status ,job_name ,priority -- ,notes FROM dba_optstat_operation_tasks WHERE opid = &&op_id ORDER BY start_time/prompt Priority grouping for targets, ignoring SYS, for end-to-end duration for that operation (tab/idx/part/etc)SELECT MIN(target) tgt ,MIN(start_time) start_time ,MAX(end_time) end_time ,max(end_time)-min(start_time) duration ,priority FROM dba_optstat_operation_tasks WHERE opid = &&op_id AND target not like '"SYS".%' GROUP BY priority ORDER BY min(target)/prompt do we have any synopsis for incremental partitioning?col obj for a40 truncatecol spare2 for a40 truncateselect do.owner||','||do.object_name||'.'||do.subobject_name obj,do.object_type,ANALYZETIME,SPARE2 from SYS.WRI$_OPTSTAT_SYNOPSIS_HEAD$ wosh join dba_objects do on (wosh.group#/2)=do.object_id join dba_tab_columns dtc on (do.owner=dtc.owner and do.objecT_name=dtc.table_name and wosh.intcol#=dtc.column_id)where do.owner = '&&schema_name'order by 1,2/spool off
perf_sqlplan – dbms_xplan.display_cursor, awr, manual extract of plan inc some XML
-- perf_sqlplan.sql v1.0 n chandler.-- v1.1 n chandler. 2025-11-11 extrat XML in a readable format for manual side of plans---- show execution plansset lines 250 pages 200col sql_id for a15col profile for a7col patch for a5col baseline for a8col is_bind_sensitive for a17col is_bind_aware for a13col is_shareable for a12col inst_id for 9999define sql_idundefine sql_idspool perf_sqlplan.&&sql_id..out appendprompt Plans and speed in GV$SQL for &&sql_idSELECT inst_id ,sql_id ,child_number ,plan_hash_value ,DECODE(sql_profile,null,'N','Y') profile ,DECODE(sql_patch,null,'N','Y') patch ,DECODE(sql_plan_baseline,null,'N','Y') baseline ,is_bind_sensitive ,is_bind_aware ,is_shareable ,executions ,rows_processed ,ROUND(rows_processed/decode(executions,0,1,executions)) rows_per_exec ,ROUND(elapsed_time/1000,1) elapsed_time_ms ,ROUND(elapsed_time/decode(executions,0,1,executions)/1000,1) elapsed_per_exec_ms FROM gv$sql WHERE sql_id = '&&sql_id' ORDER BY inst_id,sql_id,child_number,plan_hash_value;prompt get all current plans in GV$SQL for sql_id &&sql_idSELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_CURSOR('&&sql_id',null,'TYPICAL +ALIAS +NOTE +PEEKED_BINDS +OUTLINE +HINT_REPORT +ADAPTIVE'));prompt get all plans in AWR for sql_id &&sql_idpause [p] Press enter to continue or CTRL+C to stop-- display_awr is deprecated--SELECT * FROM TABLE(dbms_xplan.display_awr('&&sql_id',null,null,'TYPICAL +ALIAS +NOTE +PEEKED_BINDS +OUTLINE +HINT_REPORT +ADAPTIVE'));SELECT * FROM TABLE(DBMS_XPLAN.DISPLAY_WORKLOAD_REPOSITORY('&&sql_id',null,'TYPICAL +ALIAS +NOTE +PEEKED_BINDS +OUTLINE +HINT_REPORT +ADAPTIVE'));prompt Sometimes the AWR SQL Plan does not extract.prompt This is some manual code to extract from DBA_HIST_SQL_PLANprompt Fruther SQL will extract XML info such as outline and notes from the other_xml CLOB.pause [p] Press enter to continue or CTRL+C to stopcol operation for a30col object for a40col partition_start_stop for a20col mb for 999,999,999.00col temp_space_mb for 999,999,999.00select sql_id ,plan_hash_value ,position ,lpad(' ',depth)||operation operation ,object_owner||'.'||object_name||' ('||object_type||')' AS object ,parent_id ,cost ,bytes/1024/1024 mb ,cpu_cost ,io_cost ,temp_space/1024/1024 temp_space_mb ,partition_id||' ('||partition_start||'-'||partition_stop||')' partition_start_stop FROM dba_hist_sql_plan WHERE sql_id = '&&sql_id' ORDER BY sql_id,plan_hash_value,parent_id NULLS FIRST, position/col bindval for a50 wrapcol hint for a150 wrapprompt InfoSELECT sql_id,plan_hash_value,tag, typ, valFROM dba_hist_sql_plan dhsp, XMLTABLE( '/other_xml/*' PASSING XMLTYPE( dhsp.other_xml ) COLUMNS tag VARCHAR2(20) PATH 'name()', typ VARCHAR2(20) PATH '@type', val VARCHAR2(20) PATH 'text()' )WHERE sql_id = '&&sql_id' AND other_xml is not null/prompt bindsSELECT sql_id,plan_hash_value,bind,bindnam,bindpos,binddty,bindcsi,bindfrm,bindmxl,bindvalFROM dba_hist_sql_plan dhsp, XMLTABLE( '/other_xml/peeked_binds/*' PASSING XMLTYPE( dhsp.other_xml ) COLUMNS bind VARCHAR2(20) PATH 'name()', bindnam VARCHAR2(20) PATH '@nam', bindpos VARCHAR2(20) PATH '@pos', binddty VARCHAR2(20) PATH '@dty', bindcsi VARCHAR2(20) PATH '@csi', bindfrm VARCHAR2(20) PATH '@frm', bindmxl VARCHAR2(20) PATH '@mxl', bindval VARCHAR2(300) PATH 'text()' )WHERE sql_id = '&&sql_id' AND other_xml is not nullORDER BY 1,2,3,4/prompt outline hints to recreate planSELECT sql_id,plan_hash_value,decode(outline,'hint','outline_hint',outline) outline,hintFROM dba_hist_sql_plan dhsp, XMLTABLE( '/other_xml/outline_data/*' PASSING XMLTYPE( dhsp.other_xml ) COLUMNS outline VARCHAR2(20) PATH 'name()', hint VARCHAR2(300) PATH 'text()' )WHERE sql_id = '&&sql_id' AND other_xml is not nullORDER BY 1,2,3/prompt hint_usageSELECT sql_id,plan_hash_value,tab,origin,status,f,n,t,x,hFROM dba_hist_sql_plan dhsp, XMLTABLE( '/other_xml/hint_usage/q/*' PASSING XMLTYPE( dhsp.other_xml ) COLUMNS tab VARCHAR2(30) PATH 'text()', origin VARCHAR2(10) PATH './h/@o', status VARCHAR2(10) PATH './h/@st', f VARCHAR2(20) PATH './f/text()', n VARCHAR2(20) PATH './n/text()', t VARCHAR2(20) PATH './t/text()', x VARCHAR2(20) PATH './x/text()', h VARCHAR2(20) PATH './h/x/text()' )WHERE sql_id = '&&sql_id' AND other_xml is not null/spool off
perf_statsprefs – show global, inc hidden, and table stats prefs
-- perf_statsprefs.sql v1.0 2025-12-30 show global and table prefs which are set-- v1.1 2026-05-07 tweak output formattingcolumn sp1 new_value sp_nameSELECT SYS_CONTEXT('userenv','CDB_NAME')||'-'||DECODE(SYS_CONTEXT('userenv','CON_NAME'),'CDB$ROOT','CDB',SYS_CONTEXT('userenv','CON_NAME')) AS sp1 FROM dual;spool perf_statsprefs.&&sp_name..out appendcolumn today format a20column cdb_name format a20column pdb_name format a20column owner format a30column table_name format a30column preference_name format a50SELECT TO_CHAR(sysdate,'YYYY-MM-DD HH24:MI:SS') today,SYS_CONTEXT('userenv','CDB_NAME') AS cdb_name,SYS_CONTEXT('userenv','CON_NAME') AS pdb_name FROM dual;column preference_value format a120column ANDV_ALGO_INTERNAL_OBSERVE format a80 wrap heading "ANDV_ALGO_INTERNAL_OBSERVE - FALSE"column APPROXIMATE_NDV_ALGORITHM format a80 wrap heading "APPROXIMATE_NDV_ALGORITHM - REPEAT OR HYPERLOGLOG"column APPROXIMATE_NDV format a80 wrap heading "APPROXIMATE_NDV - TRUE"column AUTO_STAT_EXTENSIONS format a80 wrap heading "AUTO_STAT_EXTENSIONS - OFF"column AUTOSTATS_TARGET format a80 wrap heading "AUTOSTATS_TARGET - AUTO"column AUTO_TASK_INTERVAL format a80 wrap heading "AUTO_TASK_INTERVAL - 900"column AUTO_TASK_MAX_RUN_TIME format a80 wrap heading "AUTO_TASK_MAX_RUN_TIME - 3600"column AUTO_TASK_STATUS format a80 wrap heading "AUTO_TASK_STATUS - OFF"column CASCADE format a80 wrap heading "CASCADE - DBMS_STATS.AUTO_CASCADE"column CONCURRENT format a80 wrap heading "CONCURRENT - FALSE"column COORDINATOR_TRIGGER_SHARD format a80 wrap heading "COORDINATOR_TRIGGER_SHARD - FALSE"column DEBUG format a80 wrap heading "DEBUG - 0"column DEGREE format a80 wrap heading "DEGREE - NULL"column ENABLE_HYBRID_HISTOGRAMS format a80 wrap heading "ENABLE_HYBRID_HISTOGRAMS - 3"column ENABLE_TOP_FREQ_HISTOGRAMS format a80 wrap heading "ENABLE_TOP_FREQ_HISTOGRAMS - 3"column ESTIMATE_PERCENT format a80 wrap heading "ESTIMATE_PERCENT - DBMS_STATS.AUTO_SAMPLE_SIZE"column GATHER_AUTO format a80 wrap heading "GATHER_AUTO - AFTER_LOAD"column GATHER_SCAN_RATE format a80 wrap heading "GATHER_SCAN_RATE - HADOOP_ONLY"column GLOBAL_TEMP_TABLE_STATS format a80 wrap heading "GLOBAL_TEMP_TABLE_STATS - SESSION"column GRANULARITY format a80 wrap heading "GRANULARITY - AUTO"column INCREMENTAL_INTERNAL_CONTROL format a80 wrap heading "INCREMENTAL_INTERNAL_CONTROL - TRUE"column INCREMENTAL format a80 wrap heading "INCREMENTAL - FALSE"column INCREMENTAL_LEVEL format a80 wrap heading "INCREMENTAL_LEVEL - PARTITION"column INCREMENTAL_STALENESS format a80 wrap heading "INCREMENTAL_STALENESS - ALLOW_MIXED_FORMAT"column JOB_OVERHEAD_PERC format a80 wrap heading "JOB_OVERHEAD_PERC - 1"column JOB_OVERHEAD format a80 wrap heading "JOB_OVERHEAD - .5"column MAINTAIN_STATISTICS_STATUS format a80 wrap heading "MAINTAIN_STATISTICS_STATUS - FALSE"column METHOD_OPT format a80 wrap heading "METHOD_OPT - FOR ALL COLUMNS SIZE AUTO"column NO_INVALIDATE format a80 wrap heading "NO_INVALIDATE - DBMS_STATS.AUTO_INVALIDATE"column OPTIONS format a80 wrap heading "OPTIONS - GATHER"column PREFERENCE_OVERRIDES_PARAMETER format a80 wrap heading "PREFERENCE_OVERRIDES_PARAMETER- FALSE"column PUBLISH format a80 wrap heading "PUBLISH - TRUE"column ROOT_TRIGGER_PDB format a80 wrap heading "ROOT_TRIGGER_PDB - FALSE"column SCAN_RATE format a80 wrap heading "SCAN_RATE - 0"column STALE_PERCENT format a80 wrap heading "STALE_PERCENT - 10"column STAT_CATEGORY format a80 wrap heading "STAT_CATEGORY - OBJECT_STATS, REALTIME_STATS"column SYS_FLAGS format a80 wrap heading "SYS_FLAGS - 1"column TABLE_CACHED_BLOCKS format a80 wrap heading "TABLE_CACHED_BLOCKS - 1"column TRACE format a80 wrap heading "TRACE - 0"column WAIT_TIME_TO_UPDATE_STATS format a80 wrap heading "WAIT_TIME_TO_UPDATE_STATS - 0.0033 FROM 19.22. Was 1"prompt STATS:prompt Global Parameters (12.2 - inc hidden - so expect errors)SELECT dbms_stats.get_param('ANDV_ALGO_INTERNAL_OBSERVE') as ANDV_ALGO_INTERNAL_OBSERVE FROM dual;SELECT dbms_stats.get_param('APPROXIMATE_NDV_ALGORITHM') as APPROXIMATE_NDV_ALGORITHM FROM dual;SELECT dbms_stats.get_param('APPROXIMATE_NDV') as APPROXIMATE_NDV FROM dual;SELECT dbms_stats.get_param('AUTO_STAT_EXTENSIONS') as AUTO_STAT_EXTENSIONS FROM dual;SELECT dbms_stats.get_param('AUTOSTATS_TARGET') as AUTOSTATS_TARGET FROM dual;SELECT dbms_stats.get_param('AUTO_TASK_INTERVAL') as AUTO_TASK_INTERVAL FROM dual;SELECT dbms_stats.get_param('AUTO_TASK_MAX_RUN_TIME') as AUTO_TASK_MAX_RUN_TIME FROM dual;SELECT dbms_stats.get_param('AUTO_TASK_STATUS') as AUTO_TASK_STATUS FROM dual;SELECT dbms_stats.get_param('CASCADE') as CASCADE FROM dual;SELECT dbms_stats.get_param('CONCURRENT') as CONCURRENT FROM dual;SELECT dbms_stats.get_param('COORDINATOR_TRIGGER_SHARD') as COORDINATOR_TRIGGER_SHARD FROM dual;SELECT dbms_stats.get_param('DEBUG') as DEBUG FROM dual;SELECT dbms_stats.get_param('DEGREE') as DEGREE FROM dual;SELECT dbms_stats.get_param('ENABLE_HYBRID_HISTOGRAMS') as ENABLE_HYBRID_HISTOGRAMS FROM dual;SELECT dbms_stats.get_param('ENABLE_TOP_FREQ_HISTOGRAMS') as ENABLE_TOP_FREQ_HISTOGRAMS FROM dual;SELECT dbms_stats.get_param('ESTIMATE_PERCENT') as ESTIMATE_PERCENT FROM dual;SELECT dbms_stats.get_param('GATHER_AUTO') as GATHER_AUTO FROM dual;SELECT dbms_stats.get_param('GATHER_SCAN_RATE') as GATHER_SCAN_RATE FROM dual;SELECT dbms_stats.get_param('GLOBAL_TEMP_TABLE_STATS') as GLOBAL_TEMP_TABLE_STATS FROM dual;SELECT dbms_stats.get_param('GRANULARITY') as GRANULARITY FROM dual;SELECT dbms_stats.get_param('INCREMENTAL_INTERNAL_CONTROL') as INCREMENTAL_INTERNAL_CONTROL FROM dual;SELECT dbms_stats.get_param('INCREMENTAL') as INCREMENTAL FROM dual;SELECT dbms_stats.get_param('INCREMENTAL_LEVEL') as INCREMENTAL_LEVEL FROM dual;SELECT dbms_stats.get_param('INCREMENTAL_STALENESS') as INCREMENTAL_STALENESS FROM dual;SELECT dbms_stats.get_param('JOB_OVERHEAD_PERC') as JOB_OVERHEAD_PERC FROM dual;SELECT dbms_stats.get_param('JOB_OVERHEAD') as JOB_OVERHEAD FROM dual;SELECT dbms_stats.get_param('MAINTAIN_STATISTICS_STATUS') as MAINTAIN_STATISTICS_STATUS FROM dual;SELECT dbms_stats.get_param('METHOD_OPT') as METHOD_OPT FROM dual;SELECT dbms_stats.get_param('NO_INVALIDATE') as NO_INVALIDATE FROM dual;SELECT dbms_stats.get_param('OPTIONS') as OPTIONS FROM dual;SELECT dbms_stats.get_param('PREFERENCE_OVERRIDES_PARAMETER') as PREFERENCE_OVERRIDES_PARAMETER FROM dual;SELECT dbms_stats.get_param('PUBLISH') as PUBLISH FROM dual;SELECT dbms_stats.get_param('ROOT_TRIGGER_PDB') as ROOT_TRIGGER_PDB FROM dual;SELECT dbms_stats.get_param('SCAN_RATE') as SCAN_RATE FROM dual;SELECT dbms_stats.get_param('STALE_PERCENT') as STALE_PERCENT FROM dual;SELECT dbms_stats.get_param('STAT_CATEGORY') as STAT_CATEGORY FROM dual;SELECT dbms_stats.get_param('SYS_FLAGS') as SYS_FLAGS FROM dual;SELECT dbms_stats.get_param('TABLE_CACHED_BLOCKS') as TABLE_CACHED_BLOCKS FROM dual;SELECT dbms_stats.get_param('TRACE') as TRACE FROM dual;SELECT dbms_stats.get_param('WAIT_TIME_TO_UPDATE_STATS') as WAIT_TIME_TO_UPDATE_STATS FROM dual;prompt STATS: dba_tab_stat_prefsSELECT * FROM DBA_TAB_STAT_PREFSWHERE owner in (select username FROM dba_users where oracle_maintained='N')ORDER BY owner,table_name,preference_name;spool off
perf_tabfrag – check dbms_space to see how fragmented a table has become
-- perf_tabfrag.sql v1.0 n chandler 2024-01-15 use dbms_space to check how fragmented a table is - where the data is and how much--define schemadefine table_nameundefine schemaundefine table_nameset verify offset serveroutput on size unlimitedpromtp Enter the schema owner and the table_name for the table to checkprompt &&schemaprompt &&table_namespool perf_tabfrag.&&schema..&&table_name..out appendcol systimestamp for a40col pdb for a12select systimestamp,sys_context('userenv','con_name') PDB from dual;prompt DBA_SEGMENTSselect owner,segment_name,tablespace_name,blocks from dba_segments where owner = '&&schema' and segment_name = '&&table_name';prompt DBA_EXTENTSselect owner,segment_name,tablespace_name,sum(blocks),count(*) from dba_extents where owner = '&&schema' and segment_name = '&&table_name' group by owner,segment_name,tablespace_name;prompt DBMS_SPACEset serveroutput on size unlimiteddeclare tvpd_unformatted_blocks number; tvpd_unformatted_bytes number; tvpd_fs1_blocks number; tvpd_fs1_bytes number; tvpd_fs2_blocks number; tvpd_fs2_bytes number; tvpd_fs3_blocks number; tvpd_fs3_bytes number; tvpd_fs4_blocks number; tvpd_fs4_bytes number; tvpd_full_blocks number; tvpd_full_bytes number; l_part varchar2(3);begin select partitioned into l_part from dba_tables where owner = '&&schema' and table_name = '&&table_name'; if l_part = 'NO' then sys.dbms_space.space_usage('&&schema','&&table_name','TABLE', tvpd_unformatted_blocks, tvpd_unformatted_bytes, tvpd_fs1_blocks, tvpd_fs1_bytes, tvpd_fs2_blocks, tvpd_fs2_bytes, tvpd_fs3_blocks, tvpd_fs3_bytes, tvpd_fs4_blocks, tvpd_fs4_bytes, tvpd_full_blocks, tvpd_full_bytes); dbms_output.put_line('&&schema..&&table_name: unformatted='||tvpd_unformatted_blocks||', 75-99% free='||tvpd_fs4_blocks||' ,50-75% free='||tvpd_fs3_blocks||' ,25-50% free='||tvpd_fs2_blocks||', 00-25% free='||tvpd_fs1_blocks||', full='||tvpd_full_blocks); elsif l_part = 'YES' then dbms_output.put_line('yes'); for ii in (select partition_name from dba_tab_partitions where table_owner = '&&schema' and table_name = '&&table_name' order by partition_name) loop sys.dbms_space.space_usage('&&schema','&&table_name','TABLE PARTITION', tvpd_unformatted_blocks, tvpd_unformatted_bytes, tvpd_fs1_blocks, tvpd_fs1_bytes, tvpd_fs2_blocks, tvpd_fs2_bytes, tvpd_fs3_blocks, tvpd_fs3_bytes, tvpd_fs4_blocks, tvpd_fs4_bytes, tvpd_full_blocks, tvpd_full_bytes, ii.partition_name); dbms_output.put_line('&&schema..&&table_name..'||ii.partition_name||' : unformatted='||tvpd_unformatted_blocks||', 75-99% free='||tvpd_fs4_blocks||' ,50-75% free='||tvpd_fs3_blocks||' ,25-50% free='||tvpd_fs2_blocks||', 00-25% free='||tvpd_fs1_blocks||', full='||tvpd_full_blocks); end loop; else dbms_output.put_line('Partitioned: '||l_part); end if;end;/
perf_gatherstale – gathers stale stats for a schema
-- perf_gatherstale.sql n chandler 2023-07-11 gathers stale objects for a schema-- WARNING - this changes stats!set serveroutput on size unlimitedprompt WARNING! this changes statspause [p]col owner for a30col table_name for a30col partition_name for a30col subpartition_name for a30col stale_stats for a12prompt All databases non-oracle users with tablesSELECT OWNER,COUNT(*) FROM dba_tab_statistics WHERE owner in ( SELECT username FROM dba_users WHERE oracle_maintained = 'N' ) GROUP BY owner ORDER BY owner;define schemaundefine schemaprompt Enter Schema &&schemaspool perf_gatherstale.&&schema..out appendselect name,db_unique_name,sysdate from v$database;prompt list potential objectsSELECT owner,table_name,partition_name,subpartition_name,num_rows,last_analyzed,NVL(stale_stats,'NULL') stale_stats FROM dba_tab_statistics WHERE owner = '&&schema' AND NVL(stale_stats,'YES')='YES'ORDER BY stale_stats NULLS LAST,owner,table_name,partition_name,subpartition_name/DECLARE obj_list dbms_stats.ObjectTab;BEGIN -- Performa a GATHER_AUTO, then output what was actually gathered. -- NOTE: This is commented out for safety. Uncomment the next line to actually gather stale stats -- DBMS_STATS.GATHER_SCHEMA_STATS(OWNNAME=>'&&schema',OBJLIST=>obj_list,OPTIONS=>'GATHER AUTO'); --get list of objects where stats were gathered IF obj_list.first IS NOT NULL THEN FOR ii in obj_list.FIRST..obj_list.LAST LOOP dbms_output.put_line(obj_list(ii).ownname||'.'||obj_list(ii).objname||' '||obj_list(ii).objtype||' '||obj_list(ii).partname); END LOOP; ELSE dbms_output.put_line('nothing to do'); END IF;END;/prompt list objects for &&schema stale and OKSELECT owner,table_name,partition_name,subpartition_name,num_rows,last_analyzed,NVL(stale_stats,'NULL') stale_stats FROM dba_tab_statistics WHERE owner = '&&schema'ORDER BY stale_stats NULLS LAST,owner,table_name,partition_name,subpartition_name/prompt Anything till stale for &&schemaSELECT owner,table_name,count(*) stale_object_count FROM dba_tab_statistics WHERE owner = '&&schema' AND NVL(stale_stats,'YES') = 'YES' GROUP BY owner,table_name ORDER BY owner,table_name,table_name;spool off
sysstats.sql – show calculation of system statistics, as they influence the optimizer
-- perf_sys_stats.sql v1.0 n chandler 2020.03.03 (based on a script by Franck Pachot, based on research by Chris Antognini)-- show the current system statistics and the relationship between single bytew reads (cost) and multibytes reads in terms of single byte reads-- which is a key relationship for the optimizer-- bug - I'm pretty sure this is an old version but it's the only one I can find now-- and it needs debugging as there's a couple of minor mistakes in there-- ALSO, there is defensive coding in System Stats, for example, if your SREADTIM > MREADTIM, or your MBRC is 0, Oracle will apply some default calcs instead-- I DO NOT TAKE THE DEFENSIVE CODING INTO ACCOUNT!!! Understand where your stats are impossible.set linesize 200column pname format a30column sname format a20column pval2 format a20column CALCULATED format 999999999.9999column narrative format a150column reset_systats_command format a80select sname,pname,pval1 from sys.aux_stats$ order by 1,2;spool perf_sysstat.out appendselect name from v$database;select pname,pval1,calculated,formula from sys.aux_stats$ where sname='SYSSTATS_MAIN'modelreference sga on (--select name,value from v$sgaselect 'Database Buffers' name,sum(bytes) value from v$sgastat where name in ('shared_io_pool','buffer_cache')) dimension by (name) measures(value)reference parameter on (select name,decode(type,3,to_number(value)) value from v$parameter where name='db_file_multiblock_read_count' and ismodified!='FALSE'union allselect name,decode(type,3,to_number(value)) value from v$parameter where name='sessions'union allselect name,decode(type,3,to_number(value)) value from v$parameter where name='db_block_size'union allSELECT a.ksppinm name, to_number(b.ksppstvl) value FROM x$ksppi a, x$ksppsv b WHERE a.indx=b.indx AND ksppinm like '_db_file_optimizer_read_count')dimension by (name) measures(value)partition by (sname) dimension by (pname) measures (pval1,pval2,cast(null as number) as calculated,cast(null as varchar2(120)) as formula) rules(calculated['MBRC']=coalesce(pval1['MBRC'],parameter.value['db_file_multiblock_read_count'],parameter.value['_db_file_optimizer_read_count'],8),calculated['MREADTIM']=coalesce(pval1['MREADTIM'],pval1['IOSEEKTIM'] + (parameter.value['db_block_size'] * calculated['MBRC'] ) / pval1['IOTFRSPEED']),calculated['SREADTIM']=coalesce(pval1['SREADTIM'],pval1['IOSEEKTIM'] + parameter.value['db_block_size'] / pval1['IOTFRSPEED']),calculated['_multi block Cost per block']=round(1/calculated['MBRC']*calculated['MREADTIM']/calculated['SREADTIM'],4),calculated['_single block Cost per block']=1,formula['MBRC']=case when pval1['MBRC'] is not null then 'MBRC' when parameter.value['db_file_multiblock_read_count'] is not null then 'db_file_multiblock_read_count' when parameter.value['_db_file_optimizer_read_count'] is not null then '_db_file_optimizer_read_count (db_file_multiblock_read_count not set, which is good!)' else '= not sure so used 8' end,formula['MREADTIM']=case when pval1['MREADTIM'] is null then '= IOSEEKTIM + db_block_size * MBRC / IOTFRSPEED' end||' = '||pval1['IOSEEKTIM']||'+('||parameter.value['db_block_size']||'*'||calculated['MBRC']||'/'||pval1['IOTFRSPEED']||')',formula['SREADTIM']=case when pval1['SREADTIM'] is null then '= IOSEEKTIM + db_block_size / IOTFRSPEED' end||' = '||pval1['IOSEEKTIM']||'+('||parameter.value['db_block_size']||'/'||pval1['IOTFRSPEED']||')',formula['_multi block Cost per block']='= 1/MBRC * MREADTIM/SREADTIM = 1/'||calculated['MBRC']||' * '||calculated['MREADTIM']||'/'||calculated['SREADTIM'],calculated['_maximum mbrc']=sga.value['Database Buffers']/(parameter.value['db_block_size']*parameter.value['sessions']),formula['_maximum mbrc']='= buffer cache blocks/sessions (small cache limiter) = ' || sga.value['Database Buffers']/parameter.value['db_block_size']||'/'||parameter.value['sessions'],formula['_single block Cost per block']='relative to the multi block cost per block. Always 1!',formula['CPUSPEED']='overrides CPUSPEEDNW when set',formula['CPUSPEEDNW']='CPU speed Ghz - non workload',formula['IOSEEKTIM']='IO seek time in ms',formula['IOTFRSPEED']='IO transfer speed in KB/s',formula['MAXTHR']='Maximum IO system throughput',formula['SLAVETHR']='average parallel slave IO throughput') order by 1;prompt Capture current System Statisticsselect sname,pname,pval1,'exec dbms_stats.set_system_stats('''||pname||''','||decode(pval1,null,'NULL',pval1)||');' as reset_systats_command from sys.aux_stats$ where sname = 'SYSSTATS_MAIN' order by 1,2;prompt Simplified check for an 8k block sizewith stats as( select pname,pval1,calculated from sys.aux_stats$ where sname='SYSSTATS_MAIN'modelreference sga on (--select name,value from v$sgaselect 'Database Buffers' name,sum(bytes) value from v$sgastat where name in ('shared_io_pool','buffer_cache')) dimension by (name) measures(value)reference parameter on (select name,decode(type,3,to_number(value)) value from v$parameter where name='db_file_multiblock_read_count' and ismodified!='FALSE'union allselect name,decode(type,3,to_number(value)) value from v$parameter where name='sessions'union allselect name,decode(type,3,to_number(value)) value from v$parameter where name='db_block_size'union allSELECT a.ksppinm name, to_number(b.ksppstvl) value FROM x$ksppi a, x$ksppsv b WHERE a.indx=b.indx AND ksppinm like '_db_file_optimizer_read_count')dimension by (name) measures(value)partition by (sname) dimension by (pname) measures (pval1,pval2,cast(null as number) as calculated) rules(calculated['MBRC']=coalesce(pval1['MBRC'],parameter.value['db_file_multiblock_read_count'],parameter.value['_db_file_optimizer_read_count'],8),calculated['MREADTIM']=coalesce(pval1['MREADTIM'],pval1['IOSEEKTIM'] + (parameter.value['db_block_size'] * calculated['MBRC'] ) / pval1['IOTFRSPEED']),calculated['SREADTIM']=coalesce(pval1['SREADTIM'],pval1['IOSEEKTIM'] + parameter.value['db_block_size'] / pval1['IOTFRSPEED']),calculated['_multi block Cost per block']=round(1/calculated['MBRC']*calculated['MREADTIM']/calculated['SREADTIM'],4),calculated['_single block Cost per block']=1))select pname ,calculated ,case when calculated < .2708 then 'System Stats have been set for execution plan costing '||round(abs((1-(calculated/0.2708)))*100)||'% lower for multiblock reads (favours SCANS).' when calculated > .2708 then 'System Stats have been set for execution plan costing '||round(abs((1-(calculated/0.2708)))*100)||'% higher for multiblock reads (favours INDEXES).' else 'Stats calculations shows this to be the DEFAULT' end as Narrative from stats where pname = '_multi block Cost per block'/--examples--PNAME CALCULATED NARRATIVE--_multi block Cost per block .2708 Stats calculations shows this to be the DEFAULT--PNAME CALCULATED NARRATIVE--_multi block Cost per block .0119 System Stats have been set for execution plan costing 96% lower for multiblock reads (favours SCANS).prompt NOTE: 0.2708 is for an 8k block size! 16k block size is slightly different.prompt Running a "gather_system_stats(exadata)" will generally result in a very low multi-block cost per block (e.g. 0.0119) meaning indexesprompt need to be extremely accurate and selective to be considered by the optimizer instead of full table scansprompt IF IT IS NOT THE DEFAULT (above), it should be heavily documented WHY and with proofs of why the different value is better than the defaultprompt Warning: changing it can affect a lot of SQL Optimizations. Proceed with caution.spool off
check.sql – database overview








