Monthly Archives: January 2018

Parse overhead: NOT NULL vs CHECK(xyz IS NOT NULL)

Jonathan Lewis’ update on his post “Constraints, Inserts and bind” prompted another thought: is there a difference between a check constraint (IS NOT NULL) and a NOT NULL column declaration with regards to recursive queries that are run during parsing?

Let’s start with “CHECK(xyz IS NOT NULL)” constraints:

create table t2 (
    n1 number       check(n1 is not null)
  , v1 varchar2(10) check(v1 is not null)
)
;

exec dbms_monitor.session_trace_enable(null, null, true, false)
begin
  for idx in 1..100 loop
    execute immediate 'insert into t2 values (' || idx || ', ''ABC'')';
  end loop;
end;
/
commit;
exec dbms_monitor.session_trace_disable(null, null)

Next, we replace the check constraints with “NOT NULL” declarations.

create table t1 (
    n1 number       not null
  , v1 varchar2(10) not null
)
;

exec dbms_monitor.session_trace_enable(null, null, true, false)
begin
  for idx in 1..100 loop
    execute immediate 'insert into t1 values (' || idx || ', ''ABC'')';
  end loop;
end;
/
commit;
exec dbms_monitor.session_trace_disable(null, null)

The SQL trace file from the check constraints shows 107 distinct SQLs. One of them being (as one would expect after reading Jonathan’s post):

select condition from cdef$ where rowid=:1

The trace file from the “NOT NULL” declarations shows 106 distinct SQLs. You know which one’s not in there, right? 😉

When every LIO counts, use “NOT NULL” declarations over “CHECK (xzy IS NOT NULL)” constraints!

Play fetch – get exactly one row at a time

Every once in a while I want SQL*Plus to fetch one row at a time, so I set ARRAYSIZE to 1.
But:

SQL> set autotrace traceonly statistics
SQL> set arraysize 1
SQL> select * from user1.cf1 where rownum <= 100;

Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
        102  consistent gets
          0  physical reads
          0  redo size
     258194  bytes sent via SQL*Net to client
       1091  bytes received via SQL*Net from client
         51  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        100  rows processed

We only did 51 network round trips to fetch 100 rows. This is because SQL*Plus is using OCI and the default for prefetch (OCI_ATTR_PREFETCH_ROWS) is set to 1. So we end up with 2 rows per fetch call (note that with prefetch 1, the first round trip already returns 1 row).

Now, I really, really want to fetch just one row at a time. Starting with Oracle 12.1 you can create an OCI configuration file to specify various settings, one of them being prefetch. See documentation for more details.

vi ${TNS_ADMIN}/oraaccess.xml

<?xml version="1.0"?>
 <oraaccess xmlns="http://xmlns.oracle.com/oci/oraaccess" xmlns:oci="http://xmlns.oracle.com/oci/oraaccess" schemaLocation="http://xmlns.oracle.com/oci/oraaccess http://xmlns.oracle.com/oci/oraaccess.xsd">
  <default_parameters>
    <prefetch>
      <rows>0</rows>
    </prefetch>
  </default_parameters>
</oraaccess>

With this configuration in place we should see 100+ network round trips.

SQL> set autotrace traceonly statistics
SQL> set arraysize 1
SQL> select * from user1.cf1 where rownum <= 100;

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
        102  consistent gets
          0  physical reads
          0  redo size
     267988  bytes sent via SQL*Net to client
       1652  bytes received via SQL*Net from client
        102  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
        100  rows processed

Go play fetch 😉