-- Copyright 2018 Tanel Poder. All rights reserved. More info at https://tanelpoder.com -- Licensed under the Apache License, Version 2.0. See LICENSE.txt for terms & conditions. -------------------------------------------------------------------------------- -- -- Name: sqlflame.sql -- -- Purpose: Demo script for displaying execution plan profile as a flame chart -- -- Usage: @sqlflame.sql -- Note that you'll need to download Brendan Gregg's flamegraph.pl -- https://github.com/brendangregg/FlameGraph and make sure that it's -- in your PATH (or edit this script to use it from a hardcoded location) -- -- Note that you'll have to replace HOST OPEN with HOST START in the -- end of this file if you're using sqlplus client on Windows -- -- Author: Tanel Poder - https://tanelpoder.com -- -- Examples: https://tanelpoder.com/posts/visualizing-sql-plan-execution-time-with-flamegraphs/ -- https://tanelpoder.com/posts/sql-plan-flamegraph-loop-row-counts/ -- -- Other: This is an early version mostly meant as a proof of concept for -- illustrating that a modern RDBMS SQL execution plan profile -- can be treated just like any code profile (FlameGraphs were -- initially used for process stack profiling) -- -- This script currently relies on the V$SQL_PLAN_STATISTICS_ALL source -- so you'll need to run your query with statistics_level=all or -- with the GATHER_PLAN_STATISTICS hint. -- -- Credits: Brendan Gregg invented and popularized FlameGraphs, if you want to -- understand their logic better, read the articles at: -- http://www.brendangregg.com/flamegraphs.html -- -------------------------------------------------------------------------------- SET HEADING OFF LINESIZE 32767 PAGESIZE 0 TRIMSPOOL ON TRIMOUT ON LONG 9999999 VERIFY OFF LONGCHUNKSIZE 100000 FEEDBACK OFF APPINFO OFF PROMPT PROMPT -- SQLFlame 0.4 by Tanel Poder ( https://tanelpoder.com ) SET TERMOUT OFF WITH sq AS ( SELECT /*+ MATERIALIZE */ sp.id, sp.parent_id, sp.operation, sp.options , sp.object_owner, sp.object_name, ss.last_elapsed_time, ss.elapsed_time, ss.last_starts, ss.last_output_rows FROM v$sql_plan_statistics_all ss INNER JOIN v$sql_plan sp ON ( sp.sql_id=ss.sql_id AND sp.child_number=ss.child_number AND sp.address=ss.address AND sp.id=ss.id ) AND sp.sql_id='&1' AND sp.child_number=&2 ), deltas AS ( SELECT par.id, par.elapsed_time - SUM(chi.elapsed_time) self_elapsed_time FROM sq par LEFT OUTER JOIN sq chi ON chi.parent_id = par.id GROUP BY par.id, par.elapsed_time ), combined AS ( SELECT sq.id, sq.parent_id, sq.operation, sq.options , sq.object_owner, sq.object_name, sq.last_elapsed_time, sq.elapsed_time, sq.last_starts, sq.last_output_rows , NVL(deltas.self_elapsed_time, sq.elapsed_time) self_elapsed_time FROM sq, deltas WHERE sq.id = deltas.id ) SELECT '0 - SELECT STATEMENT'||TRIM(SYS_CONNECT_BY_PATH(id||' - '||operation||NVL2(options,' '||options,NULL) ||NVL2(object_owner||object_name, ' ['||object_owner||'.'||object_name||']', NULL) ||' starts='||last_starts|| ' rows='||last_output_rows, ';'))||' '||TRIM(ROUND(self_elapsed_time/1000)) FROM combined CONNECT BY parent_id = PRIOR id START WITH parent_id = 0 ORDER BY id ASC . spool sqlflame_stacks.txt / spool off SET TERMOUT ON HEADING ON PAGESIZE 5000 LINESIZE 999 FEEDBACK ON HOST flamegraph.pl --countname=Milliseconds --title="sql_id=&1" sqlflame_stacks.txt > sqlflame_&1..svg -- Windows -- HOST START sqlflame_&1..svg -- MacOS HOST OPEN sqlflame_&1..svg