Skip to Main Content
  • Questions
  • How to avoid network data transmission with autorace traceonly enabled?

Breadcrumb

Question and Answer

Connor McDonald

Thanks for the question, Alexandre.

Asked: October 31, 2022 - 3:47 am UTC

Last updated: November 02, 2022 - 4:40 am UTC

Version: 19c

Viewed 10K+ times! This question is

You Asked

Hi

Thanks for sparing some time.

Im running a query in autotrace traceonly mode in order to not get a big dump of data, but although its set to not show the data, theres a network data transmission done to clients recorded in the statistics....

Is there a way to not let this network data transmission to be sent?

There are two 'clumsy' strategies im using today to avoid the network traffic

1) encase the query as a table expression ( select count(*) from (select ....))
2) dump the data into a temp table. This one should skew some data

am i missing something?

set autotrace traceonly;
select trunc(data_inicio), count(trunc(data_inicio)) from xxxx group by trunc(data_inicio) order by trunc(data_inicio) desc

Statistics
-----------------------------------------------------------
18 CPU used by this session
21 CPU used when call started
84 DB time
46 Requests to/from client
46 SQL*Net roundtrips to/from client
2 buffer is not pinned count
724 bytes received via SQL*Net from client
102023 bytes sent via SQL*Net to client
2 calls to get snapshot scn: kcmgss
20 calls to kcmgcs
26947 consistent gets
26947 consistent gets from cache
26947 consistent gets pin
26947 consistent gets pin (fastpath)
825876 cumulative DB time in requests
1 cursor authentications
2 execute count
220749824 logical read bytes from cache
26929 no work - consistent read gets
64 non-idle wait count
31 non-idle wait time
2 opened cursors cumulative
1 opened cursors current
2 parse count (total)
2 process last non-idle time
31 scheduler wait time
2 session cursor cache count
26947 session logical reads
2 sorts (memory)
3809 sorts (rows)
26929 table scan blocks gotten
313819 table scan disk non-IMC rows gotten
313819 table scan rows gotten
1 table scans (short tables)
47 user calls


Thanks

and Chris said...

The TRACEONLY option just suppresses the display of the results. The client still has to fetch the data to know when it's reached the end of the result set.

Why are you trying to eliminate the network? An app still needs to fetch the data, so this is generally relevant.

Wrapping the query in COUNT(*) or writing to a temp table can change the execution plan, affecting your analysis.

If you want to process the whole query with no network, you could place it in a PL/SQL block:

begin
  for rws in ( select ... ) loop
   null;
  end loop;
end;
/


You'll have to pull the plan from memory and/or query v$ views to get the stats for this.

Rating

  (4 ratings)

Is this answer out of date? If it is, please let us know via a Comment

Comments

Useful for real IO statistics withou needind to dump a lot of data

alexandre guerra, October 31, 2022 - 8:03 pm UTC

Hi

Thanks for the update. Its really nice the idea to encase in a block. Should work, although a bit cumbersome to get the IO statistics.

Its really usefull when you have a slow/remote network. Why i need to do it (get the data) if i wont be able to read it ? and on top of that, spend more time and resources? Its really painful to measure autotrace on those queries returning a lot of rows.

It also allows me to have a reasonable time of how much it would take at the server's network, given the network variable is out of consideration.




Chris Saxon
November 01, 2022 - 3:57 pm UTC

You can get the I/O stats in the plan. Look at the Buffers column for the logical I/O/consistent gets.

Note that PL/SQL standardizes SQL within it, so when searching for the statement in v$sql it's all in uppercase (apart from literals and quoted identifiers) and unnecessary whitespace removed:

alter session set statistics_level = all;
begin
  for rws in ( select * from hr.employees ) loop
    null;
  end loop;
end;
/

select p.*  
from   v$sql s, table (  
  dbms_xplan.display_cursor (  
    s.sql_id, s.child_number, 'ALLSTATS LAST'  
  )  
) p  
where s.sql_text = 'SELECT * FROM HR.EMPLOYEES ';

-----------------------------------------------------------------------------------------
| Id  | Operation         | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |           |      1 |        |    107 |00:00:00.01 |       7 |
|   1 |  TABLE ACCESS FULL| EMPLOYEES |      1 |    107 |    107 |00:00:00.01 |       7 |
-----------------------------------------------------------------------------------------


Why i need to do it (get the data) if i wont be able to read it ?

Getting the data is part of executing queries. Until you've fetched all the rows the query is incomplete. So any performance measures can be misleading.

How you're fetching the data also affects performance. Here's an example playing with the fetch size (arraysize in SQL*Plus).

Getting one row/fetch does lots more work (63 buffers) than when fetching 1,000 at a time (6 buffers):

set serveroutput off

set array 1

set feed only
select * from hr.employees;
set feed on

select * 
from   table(dbms_xplan.display_cursor( format => 'ALLSTATS LAST'));

Plan hash value: 1445457117
 
-----------------------------------------------------------------------------------------
| Id  | Operation         | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |           |      1 |        |    107 |00:00:00.01 |      63 |
|   1 |  TABLE ACCESS FULL| EMPLOYEES |      1 |    107 |    107 |00:00:00.01 |      63 |
-----------------------------------------------------------------------------------------

set array 1000

set feed only
select * from hr.employees;
set feed on

select * 
from   table(dbms_xplan.display_cursor( format => 'ALLSTATS LAST'));


Plan hash value: 1445457117
 
-----------------------------------------------------------------------------------------
| Id  | Operation         | Name      | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
-----------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |           |      1 |        |    107 |00:00:00.01 |       6 |
|   1 |  TABLE ACCESS FULL| EMPLOYEES |      1 |    107 |    107 |00:00:00.01 |       6 |
-----------------------------------------------------------------------------------------

Take into account

A reader, November 01, 2022 - 1:11 pm UTC

The NULL block will not access the contents of some datatypes, such as lobs, collections, xmltype.
begin
  for rws in ( select ... ) loop
   null;
  end loop;
end;



Chris Saxon
November 01, 2022 - 3:58 pm UTC

Provided you've listed the columns in SELECT, the query is still accessing the data. The NULL statement means you do nothing else with the data.

alexandre guerra, November 01, 2022 - 5:00 pm UTC

I agree totally that setting buffer sizes allows much better timings that doing RBARs (row by row)
But still, it would be great to have this fuctionality, in order to speed up real plan evalution and check
Im away from ASE quite some time, and was used to do this quite frequently.
Maybe if the idea could be more evaluated, it would be great to have a standard option regarding this

The idea to encase in a block still great and ill use it

Thanks!


Incomplete statistics

A reader, November 01, 2022 - 7:30 pm UTC

Provided you've listed the columns in SELECT, the query is still accessing the data.

The query returns the lob locator, not the content. If lobs are stored in a lob segment, access to it does not occur until the contents of the field are requested.
create table t1(cl clob);
insert into t1(cl) values (rpad(empty_clob(),99999,'A'));
commit;

declare
  type stva is table of number index by v$statname.name%type;
  sv0   stva;
  sv1   stva;
  sv2   stva;
  s     varchar2(1 char);
  function stfu return stva is
    sv stva; 
  begin begin
    sv := stva(for cu in ( select name, value from v$mystat ms
                            join v$statname using(statistic#)
                            where name in ('session logical reads','securefile direct read ops')
                          ) index cu.name => cu.value); end;
    return sv;
  end stfu;
begin
  sv0 := stfu();
  for c in (select * from t1) loop
    null;
  end loop;
  sv1 := stfu();
  for c in (select * from t1) loop
    s := substr(c.cl,1,1);
  end loop;
  sv2 := stfu();
  dbms_output.put_line('.                             0-1  1-2');
  for k in indices of sv0 loop
    dbms_output.put_line(lpad(k,26)||': '||to_char(sv1(k)-sv0(k),'9990')||to_char(sv2(k)-sv1(k),'9990'));
  end loop;
end;
/

.                             0-1  1-2
securefile direct read ops:     0    2
     session logical reads:     7    7

As you can see, when accessing the contents of the LOB (substr), statistics show additional securefile reads.
Connor McDonald
November 02, 2022 - 4:40 am UTC

Agreed, but we're also getting into the area then of trying to predict what the app is going to do with the fetched results.

The reality is - if you want to see how your app will run when data comes across the network, you really should pull that data across the network.

More to Explore

SQL

The Oracle documentation contains a complete SQL reference.