Oracle: Deferred Segment Creation And Tablespace Restrictions

An interesting ‘problem’ surfaced a while ago, one where a user with zero quota on every tablespace could successfully create tables. Of course once it was time to insert data the inserts failed, but this was confusing the user creating the tables. The ‘problem’ stems from enabling deferred segment creation in the database. Let’s see how that can create a confusing situation,

Deferred segment creation allows tables and indexes to be created without physical segments which can be a double-edged sword as it allows objects to be created even if there is no space available for those objects in the specified tablespace. Just like the user who has no quota on the desired tablespace a false sense of security can result when the database reports ‘Table created’ or ‘Index created’. The data dictionary contains the object information so a query of DBA_TABLES, DBA_INDEXES or any of the %TAB% and %IND% tables and views will return the expected information. Querying DBA_SEGMENTS, however, may fail to return data since an object may not have any segments associated with it:


SQL> --
SQL> -- Prove the table exists
SQL> --
SQL> select table_name, tablespace_name
  2  from user_tables;

TABLE_NAME                     TABLESPACE_NAME
------------------------------ ------------------------------
KRONK                          USERS

SQL>
SQL> --
SQL> -- Now prove it doesn't exist
SQL> --
SQL> select segment_name, tablespace_name
  2  from user_segments;

no rows selected

SQL>

Looking at a session creating a table, owned by a user who has 0 quota on every tablespace, reveals this possibly confusing behavior:


SQL> 
SQL> --
SQL> -- See if deferred segment creation is enabled
SQL> --
SQL> show parameter defer

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
deferred_segment_creation            boolean     TRUE
SQL> 
SQL> --
SQL> -- We see that it is
SQL> --
SQL> -- Show quotas for user BLORPOFORST
SQL> --
SQL> @get_user_quotas.sql blorpoforst
SQL> with ttlbytes as (
  2  	select tablespace_name, sum(bytes) ttl_bytes, sum(blocks) ttl_blocks
  3  	from dba_data_files
  4  	group by tablespace_name
  5  ),
  6  userquotas as(
  7  	select
  8  	TABLESPACE_NAME,
  9  	USERNAME,
 10  	bytes,
 11  	nvl(max_bytes,0) max_bytes,
 12  	nvl(blocks,0) blocks,
 13  	nvl(max_blocks,0) MAX_BLOCKS
 14  	from dba_ts_quotas
 15  	where username = upper('&&1')
 16  )
 17  select
 18  s.tablespace_name,
 19  nvl(q.username, upper('&&1')) username,
 20  nvl(BYTES,0) bytes,
 21  case when nvl(q.MAX_BYTES,0) = -1 then s.ttl_bytes else nvl(q.max_bytes,0) end max_bytes,
 22  nvl(blocks, 0) blocks,
 23  case when nvl(q.MAX_BLOCKS,0) = -1 then s.ttl_blocks else nvl(q.max_blocks,0) end max_blocks,
 24  case when nvl(q.bytes,0) > 0 and abs(nvl(q.max_bytes,0) - nvl(q.bytes,0)) < 102400 then 'ALERT'
 25  	  when nvl(q.bytes,0) > 0 and abs(nvl(q.max_bytes,0) - nvl(q.bytes,0)) between 102400 and 10240000 then 'WARNING'
 26  	  else 'OK' end status
 27  from userquotas q full outer join ttlbytes s
 28  	   on (q.tablespace_name = s.tablespace_name)
 29  order by q.username, s.tablespace_name;
old  15:    where username = upper('&&1')
new  15:    where username = upper('blorpoforst')
old  19: nvl(q.username, upper('&&1')) username,
new  19: nvl(q.username, upper('blorpoforst')) username,

TABLESPACE_NAME                USERNAME                            BYTES  MAX_BYTES     BLOCKS MAX_BLOCKS STATUS
------------------------------ ------------------------------ ---------- ---------- ---------- ---------- -------
DFLT                           BLORPOFORST                             0          0          0          0 OK
INDX                           BLORPOFORST                             0          0          0          0 OK
SYSAUX                         BLORPOFORST                             0          0          0          0 OK
SYSTEM                         BLORPOFORST                             0          0          0          0 OK
UNDOTBS1                       BLORPOFORST                             0          0          0          0 OK
USERS                          BLORPOFORST                             0          0          0          0 OK

6 rows selected.

SQL> 
SQL> 
SQL> --
SQL> -- Absolutely no quotas on any tablespace
SQL> --
SQL> -- Try to create a table, which should fail
SQL> --
SQL> -- Succeeds due to deferred segment creation
SQL> --
SQL> create table kronk(ipso number, egrud varchar2(40))
  2  tablespace users;

Table created.

SQL> 
SQL> --
SQL> -- This will be confusing to users creating tables
SQL> --
SQL> -- 'Table' creates, but no data can be added
SQL> --
SQL> insert into kronk(ipso, egrud)
  2  values (1, 'Insert fails!!!!!');
insert into kronk(ipso, egrud)
            *
ERROR at line 1:
ORA-01950: no privileges on tablespace 'USERS'


SQL> 
SQL> --
SQL> -- Deferred segment creation allows the 'object' to
SQL> -- exist in the data dictionary even though the quota
SQL> -- prohibits any actual segments being created
SQL> --
SQL> -- Try this again, with an additional parameter
SQL> -- to the create table statement
SQL> --
SQL> drop table kronk purge;

Table dropped.

SQL> 
SQL> create table kronk(ipso number, egrud varchar2(40))
  2  segment creation immediate
  3  tablespace users;
create table kronk(ipso number, egrud varchar2(40))
*
ERROR at line 1:
ORA-01950: no privileges on tablespace 'USERS'


SQL> 
SQL> 
SQL> --
SQL> -- Now the table create fails, as expected
SQL> -- because segment creation was forced
SQL> --
SQL> -- Another way to do this is to disable
SQL> -- deferred segment creation at the session
SQL> -- level
SQL> --
SQL> -- This can also be done at the system level
SQL> -- without restarting the database
SQL> --
SQL> alter session set deferred_segment_creation=false;

Session altered.

SQL> 
SQL> --
SQL> -- Now the original create table statement will fail
SQL> --
SQL> create table kronk(ipso number, egrud varchar2(40))
  2  tablespace users;
create table kronk(ipso number, egrud varchar2(40))
*
ERROR at line 1:
ORA-01950: no privileges on tablespace 'USERS'


SQL> 

Besides confusing any users creating tables it can also take a DBA by surprise if, in earlier releases of Oracle, the DBA checked for object existence by querying the DBA_SEGMENTS view:


SQL> show parameter defer

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
deferred_segment_creation            boolean     TRUE
SQL>
SQL> create table yazzoo(plunk number, geepo varchar2(40), alpinga date)
  2  tablespace users;

Table created.

SQL>
SQL> select table_name, tablespace_name
  2  from user_tables;

TABLE_NAME                     TABLESPACE_NAME
------------------------------ ------------------------------
YAZZOO                         USERS
DUMMY                          USERS
SALGRADE                       USERS
BONUS                          USERS
DEPT                           USERS
EMP                            USERS

6 rows selected.

SQL>
SQL> select segment_name, tablespace_name
  2  from user_segments;

SEGMENT_NAME                                                                      TABLESPACE_NAME
--------------------------------------------------------------------------------- ------------------------------
DEPT                                                                              USERS
DUMMY                                                                             USERS
EMP                                                                               USERS
SALGRADE                                                                          USERS

SQL>

Notice that two tables, the one just created (YAZZOO) and one of the demonstration tables, BONUS, have no segments even though they do exist in USER_TABLES. I know of DBAs who use DBA_SEGMENTS to check objects and with deferred segment creation that may provide a list that doesn’t include every table a user has created. With deferred segment creation enabled older scripts that used DBA_SEGMENTS to successfully report on all objects need to be modified to use DBA_TABLES and DBA_INDEXES to return all tables and indexes regardless of whether segments are created or not; the older scripts can also be modified to show tables and indexes which have no segments as these example using USER_TABLES, USER_INDEXES and USER_SEGMENTS illustrate:


SQL> select table_name, tablespace_name, nvl(ttlbytes,0) ttlbytes, nvl(ttlext,0) ttlext
  2  from
  3  (select t.table_name, t.tablespace_name, sum(s.bytes) ttlbytes, sum(s.extents) ttlext
  4  from user_tables t left outer join user_segments s on (s.segment_name = t.table_name)
  5  group by t.table_name, t.tablespace_name);

TABLE_NAME                     TABLESPACE_NAME                  TTLBYTES     TTLEXT
------------------------------ ------------------------------ ---------- ----------
DUMMY                          USERS                               65536          1
EMP                            USERS                               65536          1
DEPT                           USERS                               65536          1
BONUS                          USERS                                   0          0
YAZZOO                         USERS                                   0          0
SALGRADE                       USERS                               65536          1

6 rows selected.

SQL>
SQL> select index_name, tablespace_name, nvl(ttlbytes,0) ttlbytes, nvl(ttlext,0) ttlext
  2  from
  3  (select t.index_name, t.tablespace_name, sum(s.bytes) ttlbytes, sum(s.extents) ttlext
  4  from user_indexes t left outer join user_segments s on (s.segment_name = t.index_name)
  5  group by t.index_name, t.tablespace_name);

INDEX_NAME                     TABLESPACE_NAME                  TTLBYTES     TTLEXT
------------------------------ ------------------------------ ---------- ----------
SALGRD_IDX                     INDX                                65536          1
YAZZOO_IDX                     INDX                                    0          0
DEPT_IDX                       INDX                                65536          1
EMP_IDX                        INDX                                65536          1
BONUS_IDX                      INDX                                    0          0

SQL>

Deferred segment creation can be very helpful when creating objects that may not be used immediately; it can also create problems when those objects are created by a user account with no quota on a tablespace or when the available space in a tablespace would prevent a segment from being created. The problem won’t become apparent until the first insert is met with failure because of a missing quota or insufficient available space. The DBA should get used to checking DBA_TABLES, DBA_INDEXES and DBA_SEGMENTS to verify the object can contain data. Also users who create tables should test an insert after the table is created to prevent any surprises later. Indexes would be tested with that same test insert; if the index is the ‘offending’ object the test insert will reveal this:


SQL> insert into kronk(ipso, egrud)
  2  values (1, 'Insert fails!!!!!');
insert into kronk(ipso, egrud)
            *
ERROR at line 1:
ORA-01950: no privileges on tablespace 'INDX'


SQL>

Deferred segment creation is, for the most part, a good thing as it allows tables and indexes to be created even when space isn’t immediately available. (Of course it’s not a good idea to create tables and indexes when it’s known there is insufficient space, but in the course of table/index maintenance space may be freed by dropping old versions of a table or by archiving and purging old data no longer needed for daily operation.) A good example for deferred segment creation would be migrating a table to a new definition; deferring segmnent creation allows the new table to grow as the data is inserted rather than creating a large extent where some of that allocated space may remain unused. Another benefit is the new table may be smaller initially than the original even though the number of rows has not decreased. Deferred segment creation can also be ignored, either by setting the deferred_segment_creation parameter to FALSE or by explicitly coding SEGMENT CREATION IMMEDIATE in the create table/create index statement, as shown in the examples above.

How to manage segment creation is a decision that should not be made without serious discussion between the development teams and the DBAs. It can generate a lot of create table activity in a database, creating various incarnations of a table before deciding on the final structure (a task usually left to the architects as an exercise on paper before any code is written and executed). It’s also possible that the development team isn’t aware of deferred segment creation; this can result in the ‘surprise’ reported at the beginning of this article when the first attempt to use the created table is executed. It may be better to see the error when the statement is executed rather than later, when data cannot be inserted because segments cannot be created.

Should you use deferred segment creation? That choice is one both the DBA team and development team need to make. Look at all aspects of the argument then decide accordingly. Remember that the ‘right’ answer is the one that’s suited to your environment. One size does not fit all. Choose wisely for the best fit for your environment.

See all articles by David Fitzjarrell

David Fitzjarrell
David Fitzjarrell
David Fitzjarrell has more than 20 years of administration experience with various releases of the Oracle DBMS. He has installed the Oracle software on many platforms, including UNIX, Windows and Linux, and monitored and tuned performance in those environments. He is knowledgeable in the traditional tools for performance tuning – the Oracle Wait Interface, Statspack, event 10046 and 10053 traces, tkprof, explain plan and autotrace – and has used these to great advantage at the U.S. Postal Service, American Airlines/SABRE, ConocoPhilips and SiriusXM Radio, among others, to increase throughput and improve the quality of the production system. He has also set up scripts to regularly monitor available space and set thresholds to notify DBAs of impending space shortages before they affect the production environment. These scripts generate data which can also used to trend database growth over time, aiding in capacity planning. He has used RMAN, Streams, RAC and Data Guard in Oracle installations to ensure full recoverability and failover capabilities as well as high availability, and has configured a 'cascading' set of DR databases using the primary DR databases as the source, managing the archivelog transfers manually and montoring, through scripts, the health of these secondary DR databases. He has also used ASM, ASMM and ASSM to improve performance and manage storage and shared memory.

Get the Free Newsletter!

Subscribe to Cloud Insider for top news, trends & analysis

Latest Articles