Infrastructure at your Service

Franck Pachot

Oracle lateral inline view, cursor expression and 12c implicit statement result

By Franck Pachot

.
I’ll present here 3 ways to run a query for each result of another query. Let’s take an exemple: get all executions plan (select from dbms_xplan.display_cursor) for each of my queries (identified from v$sql). The 90’s way was to run the first query, which generates the second queries into a spool file, and execute that file. Here are easier ways, some of them coming from 12c new features lateral join and implicit statement result.

Test case

I’m running 2 queries and will identify them from their module (APPINFO from sqlplus) and user:

SQL> connect scott/tiger
Connected.

SQL> set appinfo DEMO

SQL> select * from DEPT;
SQL> select * from EMP;

I don’t show the result – you know it – but here is how I identify the queries:

SQL> select sql_id,child_number,sql_text from v$sql where plan_hash_value>0 and module='DEMO' and parsing_schema_name='SCOTT';

SQL_ID        CHILD_NUMBER SQL_TEXT
------------- ------------ ------------------------------
5aht0fch310ca            0 select * from EMP
18wrqtcj3ksap            0 select * from DEPT

And I want to run dbms_xplan.display_cursor(sql_id,child_number) for each of them. Here are 3 ways to do it

Cursor expression

Do you know cursor expressions? In your SELECT expressions, you can have a query. Your client will retrieve it as a cursor and have to run it. sqlplus and sqldeveloper does that. Here is an example:

SQL> select
  cursor (select * from table(dbms_xplan.display_cursor(sql_id,child_number,format=>'TYPICAL'))) PLAN
  from
  (select * from v$sql where plan_hash_value>0 and module='DEMO' and parsing_schema_name='SCOTT')
 /

which gives the following result:

PLAN
--------------------
CURSOR STATEMENT : 1

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
SQL_ID  5aht0fch310ca, child number 0
-------------------------------------
select * from EMP

Plan hash value: 3956160932

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     3 (100)|          |
|   1 |  TABLE ACCESS FULL| EMP  |    14 |   532 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

13 rows selected.

CURSOR STATEMENT : 1

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
SQL_ID  18wrqtcj3ksap, child number 0
-------------------------------------
select * from DEPT

Plan hash value: 3383998547

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     3 (100)|          |
|   1 |  TABLE ACCESS FULL| DEPT |     4 |    80 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

13 rows selected.

This is possible on all versions, and the output can change depending on the tool you use. sqlplus handles it as above, running each cursor.

implicit statement result

You want to do it from pl/sql? That’s possible. But you have to format the output yourself with dbms_output.

Except if you are in 12c and use the ‘implicit statement result’ new feature:

SQL> declare
  r sys_refcursor;
 begin
  for c in
  (select * from v$sql where plan_hash_value>0 and module='DEMO' and parsing_schema_name='SCOTT')
  loop
   open r for select * from table(dbms_xplan.display_cursor(c.sql_id,c.child_number));
   dbms_sql.return_result(r);
  end loop;
 end;
 /
PL/SQL procedure successfully completed.

which gives the following result:

ResultSet #1

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
SQL_ID  5aht0fch310ca, child number 0
-------------------------------------
select * from EMP

Plan hash value: 3956160932

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     3 (100)|          |
|   1 |  TABLE ACCESS FULL| EMP  |    14 |   532 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

13 rows selected.

ResultSet #2

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
SQL_ID  18wrqtcj3ksap, child number 0
-------------------------------------
select * from DEPT

Plan hash value: 3383998547

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     3 (100)|          |
|   1 |  TABLE ACCESS FULL| DEPT |     4 |    80 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

13 rows selected.

This is similar to the previous approach in the way that the client has to manage the result.

Lateral inline view

This is my preferred way which is only SQL and do not depend on the client formatting. A lateral inline view is a subquery that you put in the FROM clause, using the LATERAL keyword, and which can use data from the other tables on the left of it in the FROM clause:

SQL> select plan_table_output from
  (select * from v$sql where plan_hash_value>0 and module='DEMO' and parsing_schema_name='SCOTT'),
  lateral (select * from table(dbms_xplan.display_cursor(sql_id,child_number)))
 ;

which gives the following result:

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
SQL_ID  5aht0fch310ca, child number 0
-------------------------------------
select * from EMP

Plan hash value: 3956160932

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     3 (100)|          |
|   1 |  TABLE ACCESS FULL| EMP  |    14 |   532 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

SQL_ID  18wrqtcj3ksap, child number 0
-------------------------------------
select * from DEPT

Plan hash value: 3383998547

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     3 (100)|          |
|   1 |  TABLE ACCESS FULL| DEPT |     4 |    80 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

This is a 12c new feature. It existed on previous versions but not enabled by default and not documented, but you can set it with the following event:

SQL> host oerr ora 22829

SQL> alter session set events '22829 trace name context forever';

12c came with a lot of nice improvement in SQL and PL/SQL and they are available in all editions.

Update

What is very cool about twitter is that sometimes I have to update my blog post as soon as it is published. In that case it seems that I ignored the simplest way to do that since 9i:

@FranckPachot Why not just “from v$sql, table(dbms_xplan.display_cursor(…))”? Left correlation is official and it works since 9i

— Vladimir Sitnikov (@VladimirSitnikv) December 14, 2014

So here it is:

SQL> select plan_table_output from
  2   v$sql,
  3   table(dbms_xplan.display_cursor(sql_id,child_number))
  4  where plan_hash_value>0 and module='DEMO' and parsing_schema_name='SCOTT'
  5  ;

PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------
SQL_ID  5aht0fch310ca, child number 0
-------------------------------------
select * from EMP

Plan hash value: 3956160932

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     3 (100)|          |
|   1 |  TABLE ACCESS FULL| EMP  |    14 |   532 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

SQL_ID  18wrqtcj3ksap, child number 0
-------------------------------------
select * from DEPT

Plan hash value: 3383998547

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |       |       |     3 (100)|          |
|   1 |  TABLE ACCESS FULL| DEPT |     4 |    80 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------


26 rows selected.

That does not invalidate this blog post because I wanted to show those 3 features. It’s just my example which was badly chosen because dbms_xplan returns a table expression and left correlation can be used for it.

Thanks to Vladimir Sitnikov, I learned a 9i feature while blogging about 12c new feature 🙂

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Franck Pachot
Franck Pachot

Principal Consultant / Database Evangelist
Oracle ACE Director, Oracle Database OCM 12c
AWS Database Specialty certified, AWS Community Builder
Oak Table member

RSS for this blog: feed
Twitter: @FranckPachot
LinkedIn : www.linkedin.com/in/franckpachot
Podcast en français: DBPod