Quantcast
Channel: Andrew's Oracle Blog
Viewing all 330 articles
Browse latest View live

ORA-01998

$
0
0
This was tested on Oracle 11.2.0.2.7. I wondered what would happen if you tried to revoke SYSDBA or SYSOPER from SYS so I decided to try it out: 

SQL> revoke sysdba from sys;
revoke sysdba from sys
*
ERROR at line 1:
ORA-01998: REVOKE failed: user SYS always has SYSOPER
and SYSDBA

SQL> revoke sysoper from sys;
revoke sysoper from sys
*
ERROR at line 1:
ORA-01998: REVOKE failed: user SYS always has SYSOPER
and SYSDBA

SQL>

%NOTFOUND

$
0
0
This simple example was tested on Oracle 11.2. It uses SQL*Plus to list the tables owned by a user and counts the rows in each one:

SQL> select table_name
  2  from dba_tables
  3  where owner = 'ANDREW'
  4  /
 
TABLE_NAME
------------------------------
T1
T2
T3
 
SQL> select count(*) from andrew.t1
  2  /
 
  COUNT(*)
----------
      3082
 
SQL> select count(*) from andrew.t2
  2  /
 
  COUNT(*)
----------
      4851
 
SQL> select count(*) from andrew.t3
  2  /
 
  COUNT(*)
----------
     73942
 
SQL>

Then it does the same thing in PL/SQL. Note the use of %NOTFOUND to jump out of the loop when there is nothing more to return from the cursor:

SQL> DECLARE
  2   CURSOR c1 is
  3    SELECT owner||'.'||table_name fqn
  4    FROM dba_tables
  5    WHERE owner = 'ANDREW';
  6   c1_rec c1%ROWTYPE;
  7   row_count NUMBER;
  8   sql_statement VARCHAR2(200);
  9  BEGIN
10   OPEN c1;
11    LOOP
12     FETCH c1 INTO c1_rec;
13     EXIT WHEN c1%NOTFOUND;
14     sql_statement :=
15      'SELECT COUNT(*) FROM '||c1_rec.fqn;
16     DBMS_OUTPUT.PUT_LINE(sql_statement);
17     EXECUTE IMMEDIATE sql_statement
18      INTO row_count;
19     DBMS_OUTPUT.PUT_LINE(c1_rec.fqn||
20      ' has '||row_count||' rows');
21    END LOOP;
22   CLOSE c1;
23  END;
24  /
SELECT COUNT(*) FROM ANDREW.T1
ANDREW.T1 has 3082 rows
SELECT COUNT(*) FROM ANDREW.T2
ANDREW.T2 has 4851 rows
SELECT COUNT(*) FROM ANDREW.T3
ANDREW.T3 has 73942 rows
 
PL/SQL procedure successfully completed.
 
SQL>

How to See the Version Number of an Oracle Client

$
0
0
If you want to find out the version number of an Oracle client from Windows, open a Command Prompt, type tnsping and press Enter / Return. No parameters are required. You will see the Oracle version number (10.2.0.4.0 in this case) and whether it is 32-bit or 64-bit. As usual, click on the image to enlarge it and bring it into focus:

Another option, suggested by Laurent Schneider in the first comment at the end, is to use sqlplus -v:

Tru64 > sqlplus -v

SQL*Plus: Release 9.2.0.7.0 - Production

Tru64 >

H:\>sqlplus -v

SQL*Plus: Release 10.2.0.4.0 - Production

H:\> 

Solaris > sqlplus -v

SQL*Plus: Release 11.2.0.2.0 Production 

Solaris >

Laurent Schneider wrote the book advertised below:



In Spanish / en español

Swapping

$
0
0

You can see if any swapping has taken place since a UNIX server was started by running vmstat –s. If you see values other than zero on the swap ins or swap outs lines, then swapping has occurred:

Solaris > vmstat -s
     4123 swap ins
      118 swap outs
    12369 pages swapped in
    35254 pages swapped out
93593074720 total address trans. faults taken
16175338065 page ins
67075526 page outs
167117261315 pages paged in
153673896 pages paged out
157281194355 total reclaims
157268182842 reclaims from free list
        0 micro (hat) faults
93593074720 minor (as) faults
12433082155 major faults
22790008329 copy-on-write faults
30659757855 zero fill page faults
228888590 pages examined by the clock daemon
      112 revolutions of the clock hand
146623357 pages freed by the clock daemon
251218086 forks
  8106297 vforks
197058709 execs
994409866209 cpu context switches
584512421794 device interrupts
135390836607 traps
2614749602197 system calls
328390806433 total name lookups (cache hits 99%)
7844654930 user   cpu
8057496148 system cpu
25835471029 idle   cpu
        0 wait   cpu
Solaris >

ORA-01090

$
0
0
The first screen print below shows 3 sessions on the same UNIX machine.
 
In the first session, I connected to ORCL as an externally identified user at 14:10:46.
 
In the second session, I connected to ORCL as SYS at 14:12:07 and issued a shutdown command. There are several ways to do this e.g. shutdown normal, shutdown transactional, shutdown immediate, shutdown abort and shutdown timeout. The default, if none of these options are specified, is shutdown normal. This causes the shutdown command to wait until all other sessions have logged out and this is what happened here.
 
Once you have run a shutdown command, Oracle does not allow other users to connect to the database. In the third session below, I tried to connect to database ORCL as an externally identified user at 14:14:13. This connection failed with an ORA-01090. As usual, click on the images to enlarge them and bring them into focus:
   

The second screen print below shows sessions 1 and 2 above a little later. The externally identified user logged out of the database in session 1 at 14:18:51. This allowed the shutdown command in session 2 to complete:


 

How to Move System Tablespace Datafiles

$
0
0
In an earlier post I explained how to move datafiles. The method I used then is NOT suitable for datafiles belonging to the system, undo or temporary tablespaces as they cannot be taken offline. I said I would publish a post about this in due course and here it is.

I created a database using dbca recently. I was in a hurry and forgot to put a slash at the end of the directory name where I wanted to put the datafiles so they ended up like this:

SQL> l
  1  select file_id, file_name
  2  from dba_data_files
  3* order by 1
SQL> /

   FILE_ID FILE_NAME
---------- ------------------------------------------
         1 /agasprd/qcsprod/qcs_systemsystem01.dbf
         2 /agasprd/qcsprod/qcs_systemsysaux01.dbf
         3 /agasprd/qcsprod/qcs_systemundotbs01.dbf
         4 /agasprd/qcsprod/qcs_systemusers01.dbf
         5 /agasprd/qcsprod/qcs_data/qc_data_a.dbf

SQL>

Files 1 to 4 were in the wrong place. File 5, which was created afterwards, is OK. I closed the database and backed it up. Then I renamed the files in UNIX:

UNIX > pwd
/agasprd/qcsprod
UNIX > mv qcs_systemsystem01.dbf qcs_system/system01.dbf
UNIX > mv qcs_systemsysaux01.dbf qcs_system/sysaux01.dbf
UNIX > mv qcs_systemundotbs01.dbf qcs_system/undotbs01.dbf
UNIX > mv qcs_systemusers01.dbf qcs_system/users01.dbf
UNIX >

I mounted the database:

UNIX > sqlplus / as sysdba

SQL*Plus: Release 11.2.0.1.0 Production on Thu Dec 6 14:12:20 2012

Copyright (c) 1982, 2009, Oracle.  All rights reserved.

Connected to an idle instance.

SQL> startup mount

ORACLE instance started.

Total System Global Area  522092544 bytes
Fixed Size                  2149672 bytes
Variable Size             390075096 bytes
Database Buffers          121634816 bytes
Redo Buffers                8232960 bytes
Database mounted.

SQL>

... and renamed the files in the database 1 by 1:

SQL> l
  1  alter database rename file
  2  '/agasprd/qcsprod/qcs_systemsystem01.dbf' to
  3* '/agasprd/qcsprod/qcs_system/system01.dbf'
SQL> /

Database altered.

SQL>

SQL> l
  1  alter database rename file
  2  '/agasprd/qcsprod/qcs_systemsysaux01.dbf' to
  3* '/agasprd/qcsprod/qcs_system/sysaux01.dbf'
SQL> /

Database altered.

SQL>

SQL> l
  1  alter database rename file
  2  '/agasprd/qcsprod/qcs_systemundotbs01.dbf' to
  3* '/agasprd/qcsprod/qcs_system/undotbs01.dbf'
SQL> /

Database altered.

SQL>

SQL> l
  1  alter database rename file
  2  '/agasprd/qcsprod/qcs_systemusers01.dbf' to
  3* '/agasprd/qcsprod/qcs_system/users01.dbf'
SQL> /

Database altered.

SQL>

Finally, I opened the database:

SQL> alter database open;

Database altered.

SQL> select file_id, file_name
  2  from dba_data_files
  3  order by 1;

   FILE_ID FILE_NAME
---------- ---------------------------------------------
        1 /agasprd/qcsprod/qcs_system/system01.dbf
        2 /agasprd/qcsprod/qcs_system/sysaux01.dbf
        3 /agasprd/qcsprod/qcs_system/undotbs01.dbf
        4 /agasprd/qcsprod/qcs_system/users01.dbf
        5 /agasprd/qcsprod/qcs_data/qc_data_a.dbf

SQL>

The tempfile was in the wrong place too but I dealt with that by recreating the temporary tablespace.

The Curious Case of the Missing PL/SQL Procedure

$
0
0
I have to run files of SQL on databases for developers almost every day. These files often create or recreate PL/SQL packages. Any errors usually go back to the developers for correction as I am not a PL/SQL expert. Yesterday I ran some of these files then a tester asked me why a screen had stopped working. The screen was calling a PL/SQL package which I had just recreated for the developer. I will call it package_b. It had compilation errors and these were causing the screen to fail. The developer was on leave so I decided to have a look at the problem. I listed the compilation errors and could see that they happened when package_b tried to call a new procedure in another package. I will call the other package package_a and I will call the new procedure procedure_a2. I looked for compilation errors in package_a but there were none. I looked at the description for package_a but procedure_a2 was not mentioned. This was because the developer had provided a new body for package_a but no new header. The new header was found and package_a was recompiled. It was then possible to recompile package_b and the tester’s screen started to work again. I have reproduced this problem below on Oracle 11.2.0.2.7:
 
I created package_a and showed that its description contained procedure_a1:
 
SQL> create or replace package package_a is
  2  procedure procedure_a1;
  3  end package_a;
  4  /
 
Package created.
 
SQL> create or replace package body package_a is
  2  procedure procedure_a1 is
  3  begin
  4  null;
  5  end procedure_a1;
  6  end package_a;
  7  /
 
Package body created.
 
SQL> desc package_a
PROCEDURE PROCEDURE_A1
 
SQL>
 
I created package_b containing procedure_b1. This procedure called package_a.procedure_a1. Then I ran package_b.procedure_b1 successfully:
 
SQL> create or replace package package_b is
  2  procedure procedure_b1;
  3  end package_b;
  4  /
 
Package created.
 
SQL> create or replace package body package_b is
  2  procedure procedure_b1 is
  3  begin
  4  package_a.procedure_a1;
  5  end procedure_b1;
  6  end package_b;
  7  /
 
Package body created.
 
SQL> exec package_b.procedure_b1;
 
PL/SQL procedure successfully completed.
 
SQL>
 
I added procedure_a2 to package_a but did not change its header. Then I described it but could only see procedure_a1:
 
SQL> create or replace package body package_a is
  2  procedure procedure_a1 is
  3  begin
  4  null;
  5  end procedure_a1;
  6  procedure procedure_a2 is
  7  begin
  8  null;
  9  end procedure_a2;
10  end package_a;
11  /
 
Package body created.
 
SQL> desc package_a
PROCEDURE PROCEDURE_A1
 
SQL>
 
Then I changed package_b to call package_a.procedure_a2 but it failed to compile:
 
SQL> create or replace package body package_b is
  2  procedure procedure_b1 is
  3  begin
  4  package_a.procedure_a1;
  5  package_a.procedure_a2;
  6  end procedure_b1;
  7  end package_b;
  8  /
 
Warning: Package Body created with compilation errors.
 
SQL> show errors
Errors for PACKAGE BODY PACKAGE_B:
 
LINE/COL
-------------------------------------------------------
ERROR
-------------------------------------------------------
5/1
PL/SQL: Statement ignored
 
5/11
PLS-00302: component 'PROCEDURE_A2' must be declared
 
SQL>
 
I changed the header for package_a. Then its description included procedure_a2:
 
SQL> create or replace package package_a is
  2  procedure procedure_a1;
  3  procedure procedure_a2;
  4  end package_a;
  5  /
 
Package created.
 
SQL> desc package_a
PROCEDURE PROCEDURE_A1
PROCEDURE PROCEDURE_A2
 
SQL>
 
This allowed me to compile package_b and run package_b.procedure_b1 again:
 
SQL> alter package package_b compile
  2  /
 
Package altered.
 
SQL> exec package_b.procedure_b1;
 
PL/SQL procedure successfully completed.
 
SQL>

Please Answer my Poll

$
0
0
Another year is nearly over. For many of us, this means an annual appraisal and, if we are very lucky, details of the training we will receive next year.

I decided to look and see what is available. I found a company called Firebrand and looked at their OCA 11g course.

This gave me the idea for the poll to the right of this post. It will run until I have had 100 replies then I will publish the results.

Now have a go at the three simple questions below. They were tested on Oracle 11.2.0.2.7. I made them up myself so I cannot guarantee that they will come up in an exam. The answers and explanations are all within this post. If they prove popular, I will publish some more with the answers at a different URL:
 
Question 1:

Which of the statements below creates a view which allows data to be selected but not updated?
 
(a)
create or replace view my_view
as select * from my_table
where last_analyzed > '01-JAN-2012'
with check option
/
 
(b)
create or replace view my_view
as select * from my_table
where last_analyzed > '01-JAN-2012'
with read only
/
 
(c)
create or replace view my_view
as select * from my_table
where last_analyzed > '01-JAN-2012'
no update
/
 
(d)
None of the above.
 
Answer to question 1:

 
Option (a) is a valid SQL statement but it is incorrect as it does not stop you updating values via the view:

SQL> create or replace view my_view
  2  as select * from my_table
  3  where last_analyzed > '01-JAN-2012'
  4  with check option
  5  /
 
View created.
 
SQL> update my_view set owner = 'ANDREW'
  2  /
 
238 rows updated.
 
SQL>

I have already covered with check optionhere:

Option (b) is correct. If you create a view with read only and try to use it to update the underlying data, you get an ORA-42399:

SQL> create or replace view my_view
  2  as select * from my_table
  3  where last_analyzed > '01-JAN-2012'
  4  with read only
  5  /
 
View created.
 
SQL> update my_view set owner = 'ANDREW'
  2  /
update my_view set owner = 'ANDREW'
                   *
ERROR at line 1:
ORA-42399: cannot perform a DML operation on a
read-only view
 
SQL>

Option (c) is not a valid SQL statement:

SQL> create or replace view my_view
  2  as select * from my_table
  3  where last_analyzed > '01-JAN-2012'
  4  no update
  5  /
no update
*
ERROR at line 4:
ORA-00933: SQL command not properly ended
 
SQL>

Question 2:
 
Choose the only 2 system privileges from the list below:
 
(a) CREATE TABLE
(b) DROP TABLE
(c) CREATE INDEX
(d) MANAGE TABLESPACE
(e) ASSIGN USER
 
Answer to question 2:
 
The correct options are (a) and (d). The others are not valid:
 
SQL> grant create table to andrew
  2  /
 
Grant succeeded.
 
SQL> grant drop table to andrew
  2  /
grant drop table to andrew
      *
ERROR at line 1:
ORA-00990: missing or invalid privilege
 
SQL> grant create index to andrew
  2  /
grant create index to andrew
      *
ERROR at line 1:
ORA-00990: missing or invalid privilege
 
SQL> grant manage tablespace to andrew
  2  /
 
Grant succeeded.
 
SQL> grant assign user to andrew
  2  /
grant assign user to andrew
      *
ERROR at line 1:
ORA-00990: missing or invalid privilege
 
SQL>

Question 3:
 
You want to create a tablespace using Oracle Managed Files but you get the following error:

SQL> create tablespace andrew;
create tablespace andrew
                       *
ERROR at line 1:
ORA-02199: missing DATAFILE/TEMPFILE clause
 
SQL>

What should you do?

(a)
Make the tablespace locally managed using the following syntax:
create tablespace andrew extent management local;

(b)
Specify a file size like this:
create tablespace andrew datafile size 25m;
 
(c)
Download and apply the appropriate patch as this is a well known Oracle 11 bug.

(d)
Use the ALTER SYSTEM command to set the db_create_file_dest parameter and try again.
 
Answer to Question 3:
 
Option (a) results in the same error:


  1* create tablespace andrew extent management local
SQL> /
create tablespace andrew extent management local
                                               *
ERROR at line 1:
ORA-02199: missing DATAFILE/TEMPFILE clause

Option (b) gives an ORA-02236:

  1* create tablespace andrew datafile size 25m
SQL> /
create tablespace andrew datafile size 25m
                                  *
ERROR at line 1:
ORA-02236: invalid file name

I haven’t checked option (c) carefully but I made it up myself and I am not aware of such a bug.
 
Option (d) is correct as you can see below:
 
  1  alter system set db_create_file_dest
  2*  = '/export/home/oracle/andrew'
SQL> /
 
System altered.
 
SQL> create tablespace andrew;
 
Tablespace created.
 
SQL>

V$SGASTAT

$
0
0
You can use the query below to show the amount of free memory in your shared pool. It was run on an Oracle 11.1.0.6.0 database:
 
SQL> l
  1  select bytes from v$sgastat
  2  where pool = 'shared pool'
  3* and name = 'free memory'
SQL> /
 
     BYTES
----------
390505176
 
SQL>
 
If this figure remains high and you have insufficient physical memory on your server, you could consider reducing the size of your shared pool. This would allow you to reuse the memory elsewhere.

In Spanish / en español  

NOSORT and ORA-01409

$
0
0
When you create an index, Oracle usually does a sort. I read about the NOSORT option recently. This allows Oracle to create an index without doing a sort. I decided to give it a try on an Oracle 9.2.0.7.0 database. First I created a table, counted the number of sorts my session had done, created an index on the table, counted the number of sorts again and saw that it had increased by 1:
 
SQL> create table andrews_table
  2  as select owner, table_name
  3  from dba_tables
  4  /
 
Table created.
 
SQL> select a.name, b.value
  2  from v$sysstat a, v$mystat b
  3  where a.statistic# = b.statistic#
  4  and a.name like '%sorts%'
  5  /
 
NAME                      VALUE
-------------------- ----------
sorts (memory)               17
sorts (disk)                  0
sorts (rows)              11527
 
SQL> create index andrews_index
  2  on andrews_table(table_name)
  3  /
 
Index created.
 
SQL> select a.name, b.value
  2  from v$sysstat a, v$mystat b
  3  where a.statistic# = b.statistic#
  4  and a.name like '%sorts%'
  5  /
 
NAME                      VALUE
-------------------- ----------
sorts (memory)               18
sorts (disk)                  0
sorts (rows)              13069
 
SQL>
 
Then I dropped the index and tried to recreate it with the NOSORT option. I expected this to fail as the table was not ordered on the indexed column:
 
SQL> drop index andrews_index
  2  /
 
Index dropped.
 
SQL> create index andrews_index
  2  on andrews_table(table_name)
  3  nosort
  4  /
on andrews_table(table_name)
   *
ERROR at line 2:
ORA-01409: NOSORT option may not be used; rows are not
in ascending order
 
SQL>
 
Finally, I dropped and recreated the table in table_name order, counted the number of sorts my session had done, created an index with the NOSORT option on the sorted table_name column, counted the number of sorts again and saw that it had not increased:
 
SQL> drop table andrews_table
  2  /
 
Table dropped.
 
SQL> create table andrews_table
  2  as select owner, table_name
  3  from dba_tables
  4  order by table_name
  5  /
 
Table created.
 
SQL> select a.name, b.value
  2  from v$sysstat a, v$mystat b
  3  where a.statistic# = b.statistic#
  4  and a.name like '%sorts%'
  5  /
 
NAME                      VALUE
-------------------- ----------
sorts (memory)               35
sorts (disk)                  0
sorts (rows)              26085
 
SQL> create index andrews_index
  2  on andrews_table(table_name)
  3  nosort
  4  /
 
Index created.
 
SQL> select a.name, b.value
  2  from v$sysstat a, v$mystat b
  3  where a.statistic# = b.statistic#
  4  and a.name like '%sorts%'
  5  /
 
NAME                      VALUE
-------------------- ----------
sorts (memory)               35
sorts (disk)                  0
sorts (rows)              26085
 
SQL>

Data Dictionary Cache Hit Ratio

$
0
0
Every Oracle database has a data dictionary, which is owned by the SYS user and stored in the SYSTEM tablespace. This consists of read-only tables, which record information about the database e.g. table definitions, details of integrity constraints, usernames and the roles and privileges granted to them etc. You can read about this in detail in Oracle's own documentation.

So, for example, if user SCOTT tries to read a table, Oracle checks the data dictionary first to see whether he has been granted SELECT access to it.

Each time the data dictionary is accessed, the GETS column is updated in V$ROWCACHE.

Oracle retains parts of the data dictionary in a cache in the SGA. If the information (to determine if SCOTT will be allowed to read the table) is not  found in this cache, Oracle has to get it from the underlying tables. When this happens, the GETMISSES column is updated in V$ROWCACHE.

Once your database has been open for a while, you can use the number of GETS and GETMISSES to calculate the data dictionary cache hit ratio as shown in the SQL below, which I ran on an Oracle 11.2 database: 

SQL> select sum(gets) as "Gets",
  2  sum(getmisses) as "Misses",
  3  (1-(sum(getmisses)/sum(gets)))*100 as "DDC Hit Ratio"
  4  from v$rowcache
  5  /
 
      Gets     Misses DDC Hit Ratio
---------- ---------- -------------
  39375878      96143   99.7558327
 
SQL>

The hit ratio should be around 99% but anything over 90% should be OK. If it is too low, you can try increasing the size of your database's shared pool.

If you found this post useful, please vote in my poll if you have not already done so.

ORA-01502

$
0
0
This example was tested on Oracle 11.2.0.2.7. It looks at why an index might become unusable. First I created a table in the SYSTEM tablespace by mistake:

SQL> create table andrews_table
  2  tablespace system
  3  as select * from dba_tables
  4  /
 
Table created.
 
SQL>

I created an index on the table and checked that it was VALID:

SQL> create index andrews_index
  2  on andrews_table(table_name)
  3  /
 
Index created.
 
SQL> select status from user_indexes
  2  where index_name = 'ANDREWS_INDEX'
  3  /
 
STATUS
--------
VALID
 
SQL>

Then I made sure that Oracle would tell me if it tried to use an UNUSABLE index:

SQL> alter session
  2  set skip_unusable_indexes = false
  3  /
 
Session altered.
 
SQL>

I ran some SQL which would use the index and it worked OK:

SQL> select count(*) from andrews_table
  2  where table_name = 'BLAH'
  3  /
 
  COUNT(*)
----------
         0
 
SQL>

Then I moved the table to the correct tablespace:

SQL> alter table andrews_table
  2  move tablespace users
  3  /
 
Table altered.
 
SQL>

This invalidated the index:

SQL> select status from user_indexes
  2  where index_name = 'ANDREWS_INDEX'
  3  /
 
STATUS
--------
UNUSABLE
 
SQL>

So when I tried to use it I got an ORA-01502:

SQL> select count(*) from andrews_table
  2  where table_name = 'BLAH'
  3  /
select count(*) from andrews_table
*
ERROR at line 1:
ORA-01502: index 'OPS$ORACLE.ANDREWS_INDEX' or
partition of such index is in unusable state
 
SQL>

To fix this, I rebuilt the index:

SQL> alter index andrews_index rebuild
  2  /
 
Index altered.
 
SQL>

This made the index VALID:

SQL> select status from user_indexes
  2  where index_name = 'ANDREWS_INDEX'
  3  /
 
STATUS
--------
VALID
 
SQL>

... and I was able to use it again:

SQL> select count(*) from andrews_table
  2  where table_name = 'BLAH'
  3  /
 
  COUNT(*)
----------
         0
 
SQL>

ORA-01154

$
0
0
I was cloning one Oracle 10 test database into seven new test databases. I was doing this by copying the datafiles and recreating the control files. You can see this in progress below on one of the target databases, which I will call DB1. The @ccf instruction runs a file called ccf.sql to recreate the control file. The ccf.sql file was created by running alter database backup controlfile to trace on the source database: 

Solaris > sqlplus / as sysdba
 
SQL*Plus: Release 10.2.0.3.0 - Production on Thu Jan 10 14:31:55 2013
 
Copyright (c) 1982, 2006, Oracle.  All Rights Reserved.
 
Connected to an idle instance.
 
SQL> startup nomount
ORACLE instance started.
 
Total System Global Area  264241152 bytes
Fixed Size                  2029424 bytes
Variable Size              88082576 bytes
Database Buffers          167772160 bytes
Redo Buffers                6356992 bytes
SQL> @ccf
 
Control file created.
 
SQL> alter database open resetlogs
  2  /
 
While this was running, I tried to do the same thing (in a different server session) on another database, which I will call DB2. I forgot to run . oraenv first and Oracle was pointing to DB1 instead of DB2. Then I saw the following error:
 
SQL> alter database open resetlogs;
alter database open resetlogs
*
ERROR at line 1:
ORA-01154: database busy. Open, close, mount, and dismount not allowed now
 
SQL>
 
I returned to the UNIX prompt and ran . oraenv to point Oracle to DB2. Then when I tried to open the database again, the command was successful.

ORA-00904

$
0
0
I noticed this in Oracle 11. However, I do not think it is a bug as the same thing happens in Oracle 9. I joined two SELECT * statements with a UNION and added an ORDER BY at the end. This gave me an ORA-00904. It seemed to me that the ORDER BY did not know the names of the columns in the output from the SELECT *:
 
SQL> create table andrew (col1 number)
  2  /
 
Table created.
 
SQL> create table john (col1 number)
  2  /
 
Table created.
 
SQL> select *
  2  from user_tables
  3  where table_name = 'JOHN'
  4  union
  5  select *
  6  from user_tables
  7  where table_name = 'ANDREW'
  8  order by table_name
  9  /
order by table_name
         *
ERROR at line 8:
ORA-00904: "TABLE_NAME": invalid identifier
 
SQL>
 
I found the following workarounds. One of them may be suitable if you hit this problem yourself. I used SELECT TABLE_NAME instead of SELECT *:
 
SQL> select table_name
  2  from user_tables
  3  where table_name = 'JOHN'
  4  union
  5  select table_name
  6  from user_tables
  7  where table_name = 'ANDREW'
  8  order by table_name
  9  /
 
TABLE_NAME
------------------------------
ANDREW
JOHN
 
SQL>
 
I removed the ORDER BY. For the purposes of this example, I also added SET HEAD OFF and removed some blank lines from the output:
 
SQL> set head off
SQL> select *
  2  from user_tables
  3  where table_name = 'JOHN'
  4  union
  5  select *
  6  from user_tables
  7  where table_name = 'ANDREW'
  8  /
 
ANDREW
USERS
                               VALID            10
                    1        255          65536
                      1  2147483645
                           YES N
         1          1     N ENABLED
          NO               N N NO  DEFAULT DISABLED NO
NO                  DISABLED YES
                               DISABLED DISABLED
                   NO  NO
 
JOHN
USERS
                               VALID            10
                    1        255          65536
                      1  2147483645
                           YES N
         1          1     N ENABLED
          NO               N N NO  DEFAULT DISABLED NO
NO                  DISABLED YES
                               DISABLED DISABLED
                   NO  NO
 
SQL>
 
I looked at the USER_TABLES view, saw that TABLE_NAME was the first column and used ORDER BY 1 instead:
 
SQL> desc user_tables
Name                       Null?    Type
-------------------------- -------- ------------------
TABLE_NAME                 NOT NULL VARCHAR2(30)
TABLESPACE_NAME                     VARCHAR2(30)
CLUSTER_NAME                        VARCHAR2(30)
IOT_NAME                            VARCHAR2(30)
STATUS                              VARCHAR2(8)
PCT_FREE                            NUMBER
PCT_USED                            NUMBER
INI_TRANS                           NUMBER
MAX_TRANS                           NUMBER
INITIAL_EXTENT                      NUMBER
NEXT_EXTENT                         NUMBER
MIN_EXTENTS                         NUMBER
MAX_EXTENTS                         NUMBER
PCT_INCREASE                        NUMBER
FREELISTS                           NUMBER
FREELIST_GROUPS                     NUMBER
LOGGING                             VARCHAR2(3)
BACKED_UP                           VARCHAR2(1)
NUM_ROWS                            NUMBER
BLOCKS                              NUMBER
EMPTY_BLOCKS                        NUMBER
AVG_SPACE                           NUMBER
CHAIN_CNT                           NUMBER
AVG_ROW_LEN                         NUMBER
AVG_SPACE_FREELIST_BLOCKS           NUMBER
NUM_FREELIST_BLOCKS                 NUMBER
DEGREE                              VARCHAR2(10)
INSTANCES                           VARCHAR2(10)
CACHE                               VARCHAR2(5)
TABLE_LOCK                          VARCHAR2(8)
SAMPLE_SIZE                         NUMBER
LAST_ANALYZED                       DATE
PARTITIONED                         VARCHAR2(3)
IOT_TYPE                            VARCHAR2(12)
TEMPORARY                           VARCHAR2(1)
SECONDARY                           VARCHAR2(1)
NESTED                              VARCHAR2(3)
BUFFER_POOL                         VARCHAR2(7)
ROW_MOVEMENT                        VARCHAR2(8)
GLOBAL_STATS                        VARCHAR2(3)
USER_STATS                          VARCHAR2(3)
DURATION                            VARCHAR2(15)
SKIP_CORRUPT                        VARCHAR2(8)
MONITORING                          VARCHAR2(3)
CLUSTER_OWNER                       VARCHAR2(30)
DEPENDENCIES                        VARCHAR2(8)
COMPRESSION                         VARCHAR2(8)
COMPRESS_FOR                        VARCHAR2(18)
DROPPED                             VARCHAR2(3)
READ_ONLY                           VARCHAR2(3)
 
SQL> select *
  2  from user_tables
  3  where table_name = 'JOHN'
  4  union
  5  select *
  6  from user_tables
  7  where table_name = 'ANDREW'
  8  order by 1
  9  /
 
ANDREW
USERS
                               VALID            10
                    1        255          65536
                      1  2147483645
                           YES N
         1          1     N ENABLED
          NO               N N NO  DEFAULT DISABLED NO
NO                  DISABLED YES
                               DISABLED DISABLED
                   NO  NO
 
JOHN
USERS
                               VALID            10
                    1        255          65536
                      1  2147483645
                           YES N
         1          1     N ENABLED
          NO               N N NO  DEFAULT DISABLED NO
NO                  DISABLED YES
                               DISABLED DISABLED
                   NO  NO
 
SQL>

I put the UNION inside brackets and put the ORDER BY outside the brackets: 

SQL> select * from
  2  (select *
  3   from user_tables
  4   where table_name = 'JOHN'
  5   union
  6   select *
  7   from user_tables
  8   where table_name = 'ANDREW')
  9  order by table_name
10  /
 
ANDREW
USERS
                               VALID            10
                    1        255          65536
                      1  2147483645
                           YES N
         1          1     N ENABLED
          NO               N N NO  DEFAULT DISABLED NO
NO                  DISABLED YES
                               DISABLED DISABLED
                   NO  NO
 
JOHN
USERS
                               VALID            10
                    1        255          65536
                      1  2147483645
                           YES N
         1          1     N ENABLED
          NO               N N NO  DEFAULT DISABLED NO
NO                  DISABLED YES
                               DISABLED DISABLED
                   NO  NO
 
SQL>

How to Add Conditions After a Where Clause

$
0
0
I went on an Oracle 9i DBA Performance and Tuning course in 2005 and was looking through the course notes recently to find something to blog about. They suggested that if you had conditions after a WHERE clause joined by AND, you should put the test which was most likely to fail first. This would then save Oracle the bother of evaluating the subsequent condition(s). This seemed reasonable so I decided to try it out.

I copied the contents of DBA_TABLES into a table of my own called T1 and duplicated its contents repeatedly until it had over three million rows. Then I checked that every row had TABLE_LOCK set to ENABLED and that no rows had an owner called BLAH.

SQL> create table t1 as select * from dba_tables
  2  /
 
Table created.
 
SQL> begin
  2  for a in 1..11 loop
  3  insert into t1 select * from t1;
  4  end loop;
  5  end;
  6  /
 
PL/SQL procedure successfully completed.
 
SQL> commit
  2  /
 
Commit complete.
 
SQL> select count(*) from t1
  2  /
 
  COUNT(*)
----------
   3217408
 
SQL> select count(*) from t1 where table_lock = 'ENABLED'
  2  /
 
  COUNT(*)
----------
   3217408
 
SQL> select count(*) from t1 where owner = 'BLAH'
  2  /
 
  COUNT(*)
----------
         0
 
SQL>

Then I ran the first SELECT statement as follows. If the course notes were correct, the first condition should reject every row and the second condition should never be evaluated:

SQL> alter system flush shared_pool
  2  /
 
System altered.
 
SQL> conn /
Connected.
SQL> select count(*) from t1
  2  where owner = 'BLAH'
  3  and table_lock = 'ENABLED'
  4  /
 
  COUNT(*)
----------
         0
 
SQL> select a.value/100 "CPU Used"
  2  from v$mystat a, v$sysstat b
  3  where a.statistic# = b.statistic#
  4  and name = 'CPU used by this session'
  5  /
 
  CPU Used
----------
     13.83
 
SQL>

Then I ran the second SELECT statement shown below. It was identical to the first but the conditions were swapped round. If the course notes were correct, the first condition should accept each row, forcing Oracle to evaluate the second condition every time. Notice how the CPU Used figure increased. I repeated this test five times and got similar results each time. So far so good:

SQL> alter system flush shared_pool
  2  /
 
System altered.
 
SQL> conn /
Connected.
SQL> select count(*) from t1
  2  where table_lock = 'ENABLED'
  3  and owner = 'BLAH'
  4  /
 
  COUNT(*)
----------
         0
 
SQL> select a.value/100 "CPU Used"
  2  from v$mystat a, v$sysstat b
  3  where a.statistic# = b.statistic#
  4  and name = 'CPU used by this session'
  5  /
 
  CPU Used
----------
     14.27
 
SQL>

The course notes also suggested that if you had conditions after a WHERE clause joined by OR, you should put the test which was most likely to succeed first. This would then save Oracle the bother of evaluating the subsequent condition(s). I decided to try this out too, using the table T1, which I created above.

I ran the third SELECT statement like this. If the course notes were correct, the first condition should accept every row and the second condition should never be evaluated: 

SQL> alter system flush shared_pool
  2  / 

System altered.
 
SQL> conn /
Connected.
SQL> select count(*) from t1
  2  where table_lock = 'ENABLED'
  3  or owner = 'BLAH'
  4  /

  COUNT(*)
----------
   3217408

SQL> select a.value/100 "CPU Used"
  2  from v$mystat a, v$sysstat b
  3  where a.statistic# = b.statistic#
  4  and name = 'CPU used by this session'
  5  / 

  CPU Used
----------
     14.17 

SQL>

Finally, I ran the fourth SELECT statement. It was identical to the third but the conditions were swapped round. If the course notes were correct, the first condition should reject each row, forcing Oracle to evaluate the second condition every time. Notice how the CPU Used figure increased. I repeated this test five times as well and got similar results each time. I think this demonstrated that the course notes were correct:

SQL> alter system flush shared_pool
  2  /
 
System altered.
 
SQL> conn /
Connected.
SQL> select count(*) from t1
  2  where owner = 'BLAH'
  3  or table_lock = 'ENABLED'
  4  /
 
  COUNT(*)
----------
   3217408
 
SQL> select a.value/100 "CPU Used"
  2  from v$mystat a, v$sysstat b
  3  where a.statistic# = b.statistic#
  4  and name = 'CPU used by this session'
  5  /
 
  CPU Used
----------
     14.79
 
SQL>

EXP-00008 and ORA-00904

$
0
0
A colleague tried to use an Oracle 10 Windows Vista client to export a schema from an Oracle 11.1.0.6.0 database. The export failed and he came to me for help. I have reproduced the error below in a Command Prompt session:

C:\Users\j0294094>exp parfile=paramfile
 
Export: Release 10.2.0.4.0 - Production on Thu Dec 27 16:06:55 2012
 
Copyright (c) 1982, 2007, Oracle.  All rights reserved.
 
Connected to: Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - 64bit
Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
Export done in WE8MSWIN1252 character set and AL16UTF16 NCHAR character set
server uses WE8ISO8859P15 character set (possible charset conversion)
. exporting pre-schema procedural objects and actions
. exporting foreign function library names for user CBS
. exporting PUBLIC type synonyms
. exporting private type synonyms
. exporting object type definitions for user CBS
About to export CBS's objects ...
. exporting database links
. exporting sequence numbers
. exporting cluster definitions
. about to export CBS's tables via Conventional Path ...
Etc
Etc
. exporting synonyms
. exporting views
. exporting stored procedures
. exporting operators
EXP-00008: ORACLE error 904 encountered
ORA-00904: "OLEVEL": invalid identifier
EXP-00000: Export terminated unsuccessfully
 
C:\Users\j0294094>

I believe he had this problem because the export utility was on a lower version than the database. One possible solution is to run the export on the UNIX server hosting the database, where the client and the database versions are the same. As you can see below, this runs successfully. However, I’m not sure if this is the best way forwards as Oracle no longer supports export for general use. I’m going to suggest to the developer that he considers using datapump instead:

Solaris > exp parfile=paramfile
 
Export: Release 11.1.0.6.0 - Production on Thu Dec 27 16:34:57 2012
 
Copyright (c) 1982, 2007, Oracle.  All rights reserved.
 
 
Connected to: Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
Export done in US7ASCII character set and AL16UTF16 NCHAR character set
server uses WE8ISO8859P15 character set (possible charset conversion)
. exporting pre-schema procedural objects and actions
. exporting foreign function library names for user CBS
. exporting PUBLIC type synonyms
. exporting private type synonyms
. exporting object type definitions for user CBS
About to export CBS's objects ...
. exporting database links
. exporting sequence numbers
. exporting cluster definitions
. about to export CBS's tables via Conventional Path ...
Etc
Etc
. exporting synonyms
. exporting views
. exporting stored procedures
. exporting operators
. exporting referential integrity constraints
. exporting triggers
. exporting indextypes
. exporting bitmap, functional and extensible indexes
. exporting posttables actions
. exporting materialized views
. exporting snapshot logs
. exporting job queues
. exporting refresh groups and children
. exporting dimensions
. exporting post-schema procedural objects and actions
. exporting statistics
Export terminated successfully without warnings.
Solaris >

Why is my Package or Procedure Invalid?

$
0
0
I had a problem earlier this week with a package which kept going INVALID. Once I had worked out the reason, I decided to do a worked example of it on Oracle 11.2.0.2.7. I created users called SOMEBODY, JLS and CBS. JLS has nothing to do with the pop group of the same name and CBS has nothing to do with the news network. You will notice that they have some unusual permissions. This is to save me having to connect as SYS later in the example. It has nothing to do with the problem:

conn / as sysdba
Connected.
SQL> create user somebody
  2  identified by somebody
  3  default tablespace users
  4  quota unlimited on users
  5  /

User created.
 
SQL> grant create session, create table to somebody
  2  /
 
Grant succeeded.
 
SQL> create user jls
  2  identified by jls
  3  default tablespace users
  4  quota unlimited on users
  5  /
 
User created.
 
SQL> grant
  2  create session,
  3  create procedure,
  4  update any table,
  5  select any table,
  6  select any dictionary to jls
  7  /
 
Grant succeeded.
 
SQL> create user cbs
  2  identified by cbs
  3  default tablespace users
  4  quota unlimited on users
  5  /
 
User created.
 
SQL> grant
  2  create session,
  3  create table,
  4  select any dictionary,
  5  create public synonym to cbs
  6  /
 
Grant succeeded.

SQL>

SOMEBODY created a table called SITE:
 
SQL> conn somebody/somebody
Connected.
SQL> create table site (col1 varchar2(15))
  2  /
 
Table created.

SQL>

JLS created a procedure to update SOMEBODY's SITE table using its fully qualified name:
 
SQL> conn jls/jls
Connected.
SQL> create or replace procedure update_site as
  2  begin
  3  update somebody.site set col1 = '10 High Street';
  4  end;
  5  /
 
Procedure created.

SQL>

DBA_DEPENDENCIES showed that JLS's UPDATE_SITE procedure referenced SOMEBODY's SITE table:
 
SQL> select referenced_owner, referenced_name
  2  from dba_dependencies
  3  where owner = 'JLS'
  4  and name = 'UPDATE_SITE'
  5  /
 
REFERENCED_OWNER     REFERENCED_NAME
-------------------- ------------------------------
SYS                  SYS_STUB_FOR_PURITY_ANALYSIS
SOMEBODY             SITE

SQL>

CBS created its own SITE table but this did not affect the validity of JLS's UPDATE_SITE procedure:
SQL> conn cbs/cbs
Connected.
SQL> create table site (col2 number)
  2  /
 
Table created.
 
SQL> select owner, object_name, object_type
  2  from dba_objects
  3  where status = 'INVALID'
  4  /
 
no rows selected
 
SQL> drop table site
  2  /
 
Table dropped.

SQL>

A public synonym called SITE was created for SOMEBODY's SITE table:
 
SQL> create or replace public synonym site for somebody.site
  2  /
 
Synonym created.

SQL>

CBS recreated its SITE table. JLS's UPDATE_SITE procedure remained VALID:
 
SQL> create table site (col2 number)
  2  /
 
Table created.
 
SQL> select owner, object_name, object_type
  2  from dba_objects
  3  where status = 'INVALID'
  4  /
 
no rows selected
 
SQL> drop table site
  2  /
 
Table dropped.

SQL>

JLS changed its UPDATE_SITE procedure to access SOMEBODY's SITE table via the public synonym:
 
SQL> conn jls/jls
Connected.
SQL> create or replace procedure update_site as
  2  begin
  3  update site set col1 = '10 High Street';
  4  end;
  5  /
 
Procedure created.

SQL>

DBA_DEPENDENCIES showed that JLS's UPDATE_SITE procedure referenced a public synonym called SITE:
 
SQL> select referenced_owner, referenced_name
  2  from dba_dependencies
  3  where owner = 'JLS'
  4  and name = 'UPDATE_SITE'
  5  /
 
REFERENCED_OWNER     REFERENCED_NAME
-------------------- ------------------------------
SYS                  SYS_STUB_FOR_PURITY_ANALYSIS
PUBLIC               SITE

SQL>

CBS checked that there were no INVALID objects in the database and recreated its SITE table. This made JLS's UPDATE_SITE procedure INVALID.
    
SQL> conn cbs/cbs
Connected.
SQL> select owner, object_name, object_type
  2  from dba_objects
  3  where status = 'INVALID'
  4  /
 
no rows selected
 
SQL> create table site (col2 number)
  2  /
 
Table created.
 
SQL> select owner, object_name, object_type
  2  from dba_objects
  3  where status = 'INVALID'
  4  /
 
OWNER      OBJECT_NAME     OBJECT_TYPE
---------- --------------- ---------------
JLS        UPDATE_SITE     PROCEDURE

SQL>

The procedure then had to be recompiled: 

SQL> conn jls/jls
Connected.
SQL> alter procedure update_site compile
  2  /
 
Procedure altered.
 
SQL>

So, there you have it. If a compiled procedure or package accesses a table via a public synonym, then another user creates a table with the same name as the public synonym, the compiled procedure or package will be invalidated.

Incidentally, when this happened for real, a package with over 3000 lines of code became INVALID. When a UNIX script then tried to run it, Oracle did not attempt to recompile the package automatically, it just failed with a tnsnames related error, possibly because the package accessed data over a database link. It was later recompiled without errors and the UNIX script ran successfully.

I have already looked at a very simple example relating to automatic procedure recompilation. I will be looking at it in more detail in the near future as it is clearly not as straightforward as I thought at first.

Can You Set a Datafile's Maxbytes / Maxsize to Less Than its Actual Size?

$
0
0
I was dealing with a message like this for one of our developers:

ORA-01653: unable to extend table ...

He was using a tablespace which had one datafile with AUTOEXTEND ON and this datafile had reached its MAXBYTES / MAXSIZE. I wondered what would happen if you tried to set a datafile's MAXBYTES / MAXSIZE to a value less than its actual size. I tried this first on Oracle 9. I started with a datafile which had an actual size of 20 megabytes and a MAXBYTES / MAXSIZE of 50 megabytes. I found that I was able to set its MAXBYTES / MAXSIZE to 1 megabyte and that the change was recorded in DBA_DATA_FILES:

SQL> select file_id, bytes, maxbytes, autoextensible
  2  from dba_data_files
  3  where tablespace_name = 'USER_DATA'
  4  /
 
   FILE_ID      BYTES   MAXBYTES AUT
---------- ---------- ---------- ---
        22   20971520   52428800 YES
 
SQL> alter database datafile 22
  2  autoextend on
  3  maxsize 1m
  4  /
 
Database altered.
 
SQL> select file_id, bytes, maxbytes, autoextensible
  2  from dba_data_files
  3  where tablespace_name = 'USER_DATA'
  4  /
 
   FILE_ID      BYTES   MAXBYTES AUT
---------- ---------- ---------- ---
        22   20971520    1048576 YES
 
SQL>

However, when I tried to do a similar thing in Oracle 11, the MAXBYTES value shown in DBA_DATA_FILES did not go below the actual size of the datafile:

SQL> select file_id, bytes, maxbytes
  2  from dba_data_files
  3  where tablespace_name = 'USERS'
  4  /
 
   FILE_ID      BYTES   MAXBYTES
---------- ---------- ----------
         4   20971520   52428800
 
SQL> alter database datafile 4
  2  autoextend on maxsize 10m
  3  /
 
Database altered.
 
SQL> select file_id, bytes, maxbytes
  2  from dba_data_files
  3  where tablespace_name = 'USERS'
  4  /
 
   FILE_ID      BYTES   MAXBYTES
---------- ---------- ----------
         4   20971520   20971520
 
SQL>

SQL*Loader and ORA-01480

$
0
0
This was tested on Oracle 11.2.0.2.7 on Solaris. I created a SQL*Loader control file with a table name which was more than 30 characters long: 

ORCL > cat andrew.ctl
load data
infile 'andrew.dat'
into table andrew7890123456789012345678901
fields terminated by ','
(col1, col2, col3)
ORCL >

Then when I tried to use SQL*Loader, I got an ORA-01480: 

ORCL > sqlldr / control=andrew.ctl
 
SQL*Loader: Release 11.2.0.2.0 - Production on Thu Feb 7 16:06:23 2013
 
Copyright (c) 1982, 2009, Oracle and/or its affiliates.  All rights reserved.
 
SQL*Loader-704: Internal error: ulmtsyn: OCIStmtExecute(tabhp) [1480]
ORA-01480: trailing null missing from STR bind value
ORCL >

I reduced the length of the table name to 30 characters and the error went away.

In Spanish / en español

Create View ... With Check Option

$
0
0
This post was tested on Oracle 11.2. It shows how to use the WITH CHECK OPTION clause of the CREATE VIEW command. The following two steps create a table and a view on a subset of that table i.e. rows where first_name begins with "A": 

SQL> create table t1
  2  (first_name varchar2(10))
  3  /
 
Table created.
 
SQL> create view v1
  2  as select first_name from t1
  3  where substr(first_name,1,1) = 'A'
  4  /
 
View created.
 
SQL>


Oracle allows you to use a view to insert rows which that view would then be unable to select. You can see this below where the view is used to insert a row for "BRIAN". This row can be selected from the table, which shows that it was successfully inserted. However, you cannot select it using the view because first_name does not begin with "A": 

SQL> insert into v1 select 'ANDREW' from dual
  2  /
 
1 row created.
 
SQL> insert into v1 select 'BRIAN' from dual
  2  /
 
1 row created.
 
SQL> select * from t1
  2  /
 
FIRST_NAME
----------
ANDREW
BRIAN
 
SQL> select * from v1
  2  /
 
FIRST_NAME
----------
ANDREW
 
SQL> 


This may not be what you want. For example, you would not expect an administrator in the IT department to be able to insert rows for the Sales department. Let's delete the rows from the table and try something different:

SQL> delete t1
  2  /

2 rows deleted.

SQL>

This time the WITH CHECK OPTION clause is added at the end of the CREATE VIEW statement:

SQL> create or replace view v1
  2  as select first_name from t1
  3  where substr(first_name,1,1) = 'A'
  4  with check option
  5  /

View created.

SQL>

This creates a constraint on the view, not the table. It has a system generated name:

SQL> select constraint_name
  2  from dba_constraints
  3  where table_name = 'V1'
  4  /
 
CONSTRAINT_NAME
------------------------------
SYS_C00121817
 
SQL>

With the constraint in place, you can still use the view to insert a first_name which begins with "A":


SQL> insert into v1 select 'ARTHUR' from dual
  2  /
 
1 row created.
 
SQL>

But if you try to use it to insert a first_name beginning with "B", the value is rejected:


SQL> insert into v1 select 'BARRY' from dual
  2  /
insert into v1 select 'BARRY' from dual
            *
ERROR at line 1:
ORA-01402: view WITH CHECK OPTION where-clause violation
 
SQL>

... and only the valid value remains in the table:


SQL> select * from t1
  2  /
 
FIRST_NAME
----------
ARTHUR
 
SQL> select * from v1
  2  /
 
FIRST_NAME
----------
ARTHUR
 
SQL>

If you do not like the system generated name, you can choose a name for the constraint yourself like this:


SQL> create or replace view v1
  2  as select first_name from t1
  3  where substr(first_name,1,1) = 'A'
  4  with check option constraint c1
  5  /

View created.

SQL> select constraint_name
  2  from dba_constraints
  3  where table_name = 'V1'
  4  /

CONSTRAINT_NAME
------------------------------
C1

SQL>
Viewing all 330 articles
Browse latest View live


Latest Images