Oracle SQL
  • LICENSE

Yearly Archives: 2025

Parsing RTSM(Real-Time SQL Monitor) XML Reports

Posted on December 7, 2025 by Sayan Malakshinov Posted in oracle, RTSM 214 Page views Leave a comment

In the previous part, I forgot to mention one important detail: if you want to export or extract RTSM (Real-Time SQL Monitoring) reports directly in XML format for further analysis, you can simply use the following functions:

  • dbms_sql_monitor.report_sql_monitor_xml() or
    dbms_sqltune.report_sql_monitor_xml() — for reports still present in gv$sql_monitor.
  • dbms_auto_report.report_repository_detail_xml() — for reports already stored in history (AWR), under dba_hist_reports.

To format these XML reports into TEXT, HTML, or Active HTML, you can use:

dbms_report.format_report(
    report      IN xmltype,
    format_name IN varchar2
)

Setting format_name => 'ACTIVE' produces the Active HTML version.

The Main Topic: How to Parse RTSM XML Reports

Starting with Oracle 19.16, table SQL_MACROs became available, enabling a very elegant way to encapsulate XML parsing logic inside SQL macros.

For convenience, I updated the package PKG_RTSM and added the following SQL macro functions to parse various sections of an RTSM XML report:

  • function rtsm_xml_macro_report_info(xmldata xmltype) return varchar2 SQL_MACRO;
  • function rtsm_xml_macro_plan_info(xmldata xmltype) return varchar2 SQL_MACRO;
  • function rtsm_xml_macro_plan_ops(xmldata xmltype) return varchar2 SQL_MACRO;
  • function rtsm_xml_macro_plan_monitor(xmldata xmltype) return varchar2 SQL_MACRO;

(These declarations can be found in the package header in the uploaded source file pkg_rtsm.)

This means you can use them directly in SQL, for example:

select *
from pkg_rtsm.rtsm_xml_macro_plan_ops(:YOUR_XML_REPORT) ops;

Overview of the SQL Macro Functions

1. rtsm_xml_macro_report_info

Returns the main metadata of the RTSM report, such as:

  • sql_id, sql_exec_id, sql_exec_start
  • rep_date, inst_count, cpu_cores
  • con_name, platform information, optimizer environment
  • SQL text, execution statistics, activity samples, and more

In addition, the function exposes two extremely useful columns:

DBMS_REPORT.FORMAT_REPORT(XMLDATA,'TEXT'  )  as RTSM_REPORT_TEXT
DBMS_REPORT.FORMAT_REPORT(XMLDATA,'ACTIVE') as RTSM_REPORT_ACTIVE

These allow you to obtain the formatted TEXT or Active HTML version of the report directly from SQL without extra steps.

This logic is fully visible in the macro implementation in the package body.


2. rtsm_xml_macro_plan_info

Returns essential information about the execution plan, including:

  • has_user_tab
  • db_version
  • parse_schema
  • Full (adaptive) PHV and normal final plan hash value (plan_hash_full, plan_hash, plan_hash_2)
  • peeked_binds
  • xplan_stats
  • qb_registry
  • outline_data
  • hint_usage

This macro extracts the <other_xml> block attached to the root plan operation (id="1").


3. rtsm_xml_macro_plan_ops

Returns the full list of plan operations, including:

  • operation id, name, options, depth, position
  • object information
  • cardinality, bytes, cost
  • I/O and CPU cost
  • access and filter predicates

This essentially exposes the plan as a SQL-friendly dataset.


4. rtsm_xml_macro_plan_monitor

This is the most important macro for analyzing performance metrics.
It returns all operations from the plan monitor section, together with all runtime statistics, including:

  • starts, cardinality, memory usage, temp usage
  • I/O operations and spilled data
  • CPU and I/O optimizer estimates
  • Monitoring timestamps (first_active, last_active)
  • Activity samples by class (CPU, User I/O, Cluster, etc.)

Most importantly, it computes:

ROUND(100 * RATIO_TO_REPORT(NVL(wait_samples_total,0)) OVER (), 3)
    AS TIME_SPENT_PERCENTAGE

This is an analogue of “Activity%” in the Active HTML report — showing what percentage of sampled activity belongs to each plan step.

The full implementation, with all xmltable parsing logic, is available in the uploaded code file pkg_rtsm.


Getting the Updated Code

You can download the latest version of the package here:

https://github.com/xtender/xt_scripts/blob/master/rtsm/parsing/pkg_rtsm.sql

oracle RTSM troubleshooting

Parsing Real-Time SQL Monitor (RTSM) ACTIVE Reports Stored as HTML

Posted on December 6, 2025 by Sayan Malakshinov Posted in java, oracle, RTSM, SQL, statistics, troubleshooting 380 Page views Leave a comment

When you work with a large number of Real-Time SQL Monitor (RTSM) reports in the ACTIVE format (the interactive HTML report with JavaScript), it quickly becomes inconvenient to open them one by one in a browser. Very often you want to load them into the database, store them, index them, and analyze them in bulk.

Some RTSM reports are easy to process — for example, those exported directly from EM often contain a plain XML payload that can be extracted and parsed with XMLTABLE().

But most ACTIVE reports do not store XML directly.
Instead, they embed a base64-encoded and zlib-compressed XML document inside a <report> element.
These reports typically look like this:

<html>
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
  <script ...>
     var version = "19.0.0.0.0";
     ...
  </script>
 </head>
 <body onload="sendXML();">
  <script id="fxtmodel" type="text/xml">
   <!--FXTMODEL-->
   <report db_version="19.0.0.0.0" ... encode="base64" compress="zlib">
     <report_id><![CDATA[/orarep/sqlmonitor/main?...]]></report_id>
       eAHtXXtz2ki2/38+hVZ1a2LvTQwS4pXB1GJDEnYc8ALOJHdrSyVA2GwAYRCOfT/9
       ...
       ffUHVA==
   </report>
   <!--FXTMODEL-->
  </script>
 </body>
</html>

At first glance it’s obvious what needs to be done:

  1. Extract the base64 block
  2. Decode it
  3. Decompress it with zlib
  4. Get the original XML <sql_monitor_report>...</sql_monitor_report>

And indeed — if the database had a built-in zlib decompressor, this would be trivial.

Unfortunately, Oracle does NOT provide a native zlib inflate function.

  • UTL_COMPRESS cannot be used — it expects Oracle’s proprietary LZ container format, not a standard zlib stream.
  • There is no PL/SQL API for raw zlib/DEFLATE decompression.
  • XMLType, DBMS_CRYPTO, XDB APIs also cannot decompress zlib.

Because the RTSM report contains a real zlib stream (zlib header + DEFLATE + Adler-32), Oracle simply cannot decompress it natively.

Solution: use Java stored procedure

The only reliable way to decompress standard zlib inside the database is to use Java.
A minimal working implementation looks like this:

InflaterInputStream inflaterIn = new InflaterInputStream(in);

InflaterInputStream with default constructor expects exactly the same format that RTSM uses.

I created a tiny Java helper ZlibHelper that inflates the BLOB directly into another BLOB.
It lives in the database, requires no external libraries, and works in all Oracle versions that support Java stored procedures.

Source code: https://github.com/xtender/xt_scripts/blob/master/rtsm/parsing/ZlibHelper.sql

PL/SQL API: PKG_RTSM

On top of the Java inflater I wrote a small PL/SQL package that:

  1. Extracts and cleans the base64 block
  2. Decodes it into a BLOB
  3. Calls Java to decompress it
  4. Returns the resulting XML as CLOB
  5. Optionally parses it with XMLTYPE

Package here:
pkg_rtsm.sql
https://github.com/xtender/xt_scripts/blob/master/rtsm/parsing/pkg_rtsm.sql

This allows you to do things like:

  xml:=xmltype(pkg_rtsm.rtsm_html_to_xml(:blob_rtsm));

Or load many reports, store them in a table, and analyze execution statistics across hundreds of SQL executions.

The next part of this series explains how to parse the extracted XML and work with the SQL Monitor data directly: Parsing RTSM(Real-Time SQL Monitor) XML Reports

analysis oracle Real-Time SQL Monitor RTSM

Oracle Telegram Bot

Posted on April 29, 2025 by Sayan Malakshinov Posted in oracle, Telegram 1,973 Page views Leave a comment

For the Oracle performance tuning and troubleshooting Telegram channel https://t.me/ora_perf, I developed a simple helpful Telegram bot. It simplifies common Oracle database tasks directly within Telegram.

Here’s what the bot can do:

FAQ Management

  • /faq add: Reply with !faq add {description} to save a message.
  • /faq list: Lists all FAQ articles.
  • /faq list [search words]: Search FAQ by keywords.
  • /faq {N}: Shows FAQ article number N.
  • /faq replace {N}: Updates FAQ article N.

Oracle Error Lookup

/oerr: Shows details of Oracle errors

/oerr ora 29024

29024, 00000, "Certificate validation failure"
// *Cause:  The certificate sent by the other side could not be validated. This may occur if
//          the certificate has expired, has been revoked, or is invalid for another reason.
// *Action: Check the certificate to determine whether it is valid. Obtain a new certificate,
//          alert the sender that the certificate has failed, or resend.

Oracle Parameter Search

/param: Finds Oracle parameters by mask.

/param %cnt%

  PAR_N  NAME                                 VAL_N  VALUE    IS_DEFAULT
-------  ---------------------------------  -------  -------  ------------
   5231  _approx_cnt_distinct_gby_pushdown        1  OFF      FALSE
   5231  _approx_cnt_distinct_gby_pushdown        2  FORCE    FALSE
   5231  _approx_cnt_distinct_gby_pushdown        3  CHOOSE   FALSE

Oracle Hints

/hint: Lists Oracle hints by mask

/hint 19

Oracle Fix Controls

/fix_control: Lists fix controls by keyword and version.

/fix_control count 8.1

   BUGNO    VALUE  SQL_FEATURE                      DESCRIPTION                                                       OFE         EVENT  DEF
--------  -------  -------------------------------  ----------------------------------------------------------------  --------  -------  -----
16954950        1  QKSFM_ACCESS_PATH_16954950       take into account empty partitions when prorating cost            12.1.0.2        0  Y
 3120429        1  QKSFM_ACCESS_PATH_3120429        account for join key sparsity in computing NL index access cost   10.1.0.3        0  Y
 6897034        1  QKSFM_ACCESS_PATH_6897034        index cardinality estimates not taking into account NULL rows     10.2.0.5        0  Y
 9456688        1  QKSFM_ACCESS_PATH_9456688        account for to_number/to_char cost after temp conversion          11.2.0.2        0  Y
14176203        1  QKSFM_CARDINALITY_14176203       Account for filter sel while computing join sel using histograms  11.2.0.4        0  Y
14254052        1  QKSFM_CARDINALITY_14254052       amend accounting for nulls in skip scan selectivity calculation   11.2.0.4        0  Y
16486095        1  QKSFM_CARDINALITY_16486095       Do not count predicate marked for no selectivity                  12.2.0.1        0  Y
23102649        1  QKSFM_CARDINALITY_23102649       correction to inlist element counting with constant expressions   12.2.0.1        0  Y
11843512        1  QKSFM_CBO_11843512               null value is not accounted in NVL rewrite                        11.2.0.3        0  Y
 1403283        1  QKSFM_CBO_1403283                CBO do not count 0 rows partitions                                8.1.6       10135  Y
22272439        1  QKSFM_CBO_22272439               correction to inlist element counting with bind variables         12.2.0.1        0  Y
25090203        1  QKSFM_CBO_25090203               account for selectivity of non sub subquery preds                 18.1.0          0  Y
 5483301        1  QKSFM_CBO_5483301                Use min repeat count in freq histogram to compute the density     10.2.0.4        0  Y
 5578791        1  QKSFM_CBO_5578791                do not discount branch io cost if inner table io cost is already  11.1.0.6        0  Y
 6694548        1  QKSFM_CBO_6694548                Account for chained rows when computing TA by ROWID from bitmap   10.2.0.4        0  Y
27500916        1  QKSFM_COMPILATION_27500916       only count one with clause reference from connect by              19.1.0          0  Y
10117760        1  QKSFM_CURSOR_SHARING_10117760    cardinality feedback should account for bloom filters             11.2.0.3        0  Y
 9841679        1  QKSFM_CVM_9841679                do not set col count for OPNTPLS                                  11.2.0.3        0  Y
26585420        1  QKSFM_DBMS_STATS_26585420        cap approx_count_distinct with non nulls                          18.1.0          0  Y
17760686        1  QKSFM_DYNAMIC_SAMPLING_17760686  Account for BMB blocks when dynamic sampling partitioned ASSM ta  12.1.0.2        0  Y

This bot helps streamline database maintenance and troubleshooting tasks. Join ora_perf to try it and share your feedback!

oracle

Partition Pruning and Global Indexes

Posted on March 17, 2025 by Sayan Malakshinov Posted in oracle, partition pruning, query optimizing, SQL, troubleshooting 1,316 Page views Leave a comment

There is a common misconception that partition pruning does not help in the case of global indexes and only works with local indexes or full table scans (FTS).

It is understandable how this misconception arose: indeed, when operations like PARTITION RANGE ITERATOR, PARTITION RANGE SINGLE, etc., appear in execution plans, partition pruning becomes strongly associated with local indexes and FTS.

It is also clear why this is the most noticeable case: the exclusion of partitions in PARTITION RANGE ITERATOR operations is hard to miss, especially since there is a dedicated line for it in the execution plan.

However, this is not all that partition pruning can do. In fact, this way of thinking is not entirely valid, and I will demonstrate this with some simple examples.

Continue reading→
indexes oracle partition pruning query optimization
Sayan Malakshinov Sayan Malakshinov

Software Development Architect (IC-6), Oracle

Oracle ACE Pro Oracle ACE Pro Alumni

DEVVY Award Oracle DB Developer Choice Award

Oracle performance tuning expert.

UK Global Talent; Fellow of BCS; Professional Member of ACM; Senior Member of IEEE.

United Kingdom / Cambridge

LinkedIn LinkedIn · Twitter Twitter · Twitter Github
sayan@orasql.org

Recent Posts

  • Parsing RTSM(Real-Time SQL Monitor) XML Reports
  • Parsing Real-Time SQL Monitor (RTSM) ACTIVE Reports Stored as HTML
  • Oracle Telegram Bot
  • Partition Pruning and Global Indexes
  • Interval Search: Part 4. Dynamic Range Segmentation – interval quantization

Popular posts

Recent Comments

  • Oracle SQL | Interval Search: Part 4. Dynamic Range Segmentation – interval quantization on Interval Search: Part 3. Dynamic Range Segmentation – Custom Domain Index
  • Oracle SQL | Interval Search: Part 4. Dynamic Range Segmentation – interval quantization on Interval Search: Part 2. Dynamic Range Segmentation – Simplified
  • Oracle SQL | Interval Search: Part 4. Dynamic Range Segmentation – interval quantization on Interval Search: Optimizing Date Range Queries – Part 1
  • Oracle SQL | Interval Search Series: Simplified, Advanced, and Custom Solutions on Interval Search: Part 2. Dynamic Range Segmentation – Simplified
  • Oracle SQL | Interval Search: Part 2. Dynamic Range Segmentation – Simplified on Interval Search: Part 3. Dynamic Range Segmentation – Custom Domain Index

Blogroll

  • Alex Fatkulin
  • Alexander Anokhin
  • Andrey Nikolaev
  • Charles Hooper
  • Christian Antognini
  • Coskan Gundogar
  • David Fitzjarrell
  • Igor Usoltsev
  • Jonathan Lewis
  • Karl Arao
  • Mark Bobak
  • Martin Bach
  • Martin Berger
  • Neil Chandler
  • Randolf Geist
  • Richard Foote
  • Riyaj Shamsudeen
  • Tanel Poder
  • Timur Akhmadeev
  • Valentin Nikotin
©Sayan Malakshinov. Oracle SQL