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

PL/SQL CASE Statement

$
0
0
This is an alternative to if… then… else… elsif… end if when you want to use conditional statements in PL/SQL. Here is a simple example, which checks whether a given number is 1 or 2. You start with the word case. Then you add one or more conditions followed by the action to take if that condition is satisfied. Each condition is preceded by the word when. You finish with the words end case:

SQL> declare
  2    one_or_two number := 1;
  3  begin
  4    case
  5    when one_or_two = 1 then
  6      dbms_output.put_line('One');
  7    when one_or_two = 2 then
  8      dbms_output.put_line('Two');
  9    end case;
 10  end;
 11  /
One
 
PL/SQL procedure successfully completed.
 
SQL>

The next example puts the variable name after the word case. This saves you having to include it in every when line:

SQL> declare
  2    one_or_two number := 2;
  3  begin
  4    case one_or_two
  5    when 1 then dbms_output.put_line('One');
  6    when 2 then dbms_output.put_line('Two');
  7    end case;
  8  end;
  9  /
Two
 
PL/SQL procedure successfully completed.
 
SQL>

If none of the when clauses are satisfied, you get an ORA-06592:

SQL> declare
  2    one_or_two number := 3;
  3  begin
  4    case
  5    when one_or_two = 1 then
  6      dbms_output.put_line('One');
  7    when one_or_two = 2 then
  8      dbms_output.put_line('Two');
  9    end case;
 10  end;
 11  /
declare
*
ERROR at line 1:
ORA-06592: CASE not found while executing CASE
statement
ORA-06512: at line 4
 
SQL>

One way to avoid this is to add an else clause before the end case:

SQL> declare
  2    one_or_two number := 3;
  3  begin
  4    case one_or_two
  5    when 1 then dbms_output.put_line('One');
  6    when 2 then dbms_output.put_line('Two');
  7    else dbms_output.put_line('Try again');
  8    end case;
  9  end;
 10  /
Try again
 
PL/SQL procedure successfully completed.
 
SQL>

An alternative is to add an exception section and check for case_not_found:

SQL> declare
  2    one_or_two number := 3;
  3  begin
  4    case
  5    when one_or_two = 1 then
  6      dbms_output.put_line('One');
  7    when one_or_two = 2 then
  8      dbms_output.put_line('Two');
  9    end case;
 10  exception
 11    when case_not_found then
 12      dbms_output.put_line('Try again');
 13  end;
 14  /
Try again
 
PL/SQL procedure successfully completed.
 
SQL>

Once a condition has been satisfied, Oracle jumps to the end case statement, ignoring the remaining conditions:

SQL> declare
  2    one_or_two number := 1;
  3  begin
  4    case
  5    when one_or_two = 1 then
  6      dbms_output.put_line('One');
  7    when one_or_two < 2 then
  8      dbms_output.put_line('Less than two');
  9    end case;
 10  exception
 11    when case_not_found then
 12      dbms_output.put_line('Try again');
 13  end;
 14  /
One
 
PL/SQL procedure successfully completed.
 
SQL>

You can even use a Boolean expression after the word case, as you can see in the two examples below:

SQL> begin
  2  case 1 = 1
  3  when true then
  4    dbms_output.put_line('1 = 1');
  5  when false then
  6    dbms_output.put_line('1 != 1');
  7  end case;
  8  end;
  9  /
1 = 1
 
PL/SQL procedure successfully completed.
 
SQL> begin
  2  case 1 = 2
  3  when true then
  4    dbms_output.put_line('1 = 2');
  5  when false then
  6    dbms_output.put_line('1 != 2');
  7  end case;
  8  end;
  9  /
1 != 2
 
PL/SQL procedure successfully completed.
 
SQL>

These examples were all tested in an Oracle 11 database.

Password Expire

$
0
0
If a user forgets his password, he may ask you to reset it for him. You will then know his new password, which you may see as a security issue. By including the password expire clause in the alter user command, you can force the user to change his password the next time he logs in. After this, you will no longer know his password. The examples which follow show a DBA changing a password in red and a user logging in afterwards in green.
 
The first example shows a DBA using an Oracle 11 version of SQL*Plus to change a password in an Oracle 11 database:

TEST11 > sqlplus / as sysdba
 
SQL*Plus: Release 11.1.0.6.0 - Production on Wed Aug 26 11:03:51 2015
 
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
 
SQL> alter user a identified by b
  2  password expire
  3  /
 
User altered.
 
SQL>

The user then logs in with the same Oracle 11 version of SQL*Plus and is prompted to change his password. After doing this, he reconnects to the database. This is not necessary, it is just to show that the password change has taken effect:

TEST11 > sqlplus a/b
 
SQL*Plus: Release 11.1.0.6.0 - Production on Wed Aug 26 11:11:51 2015
 
Copyright (c) 1982, 2007, Oracle.  All rights reserved.
 
ERROR:
ORA-28001: the password has expired
 
Changing password for a
New password:
Retype new password:
Password changed
 
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
 
SQL> conn a/c
Connected.
SQL>

The DBA then resets and expires the password again using the same Oracle 11 version of SQL*Plus:

TEST11 > sqlplus / as sysdba
 
SQL*Plus: Release 11.1.0.6.0 - Production on Wed Aug 26 11:56:10 2015
 
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
 
SQL> alter user a identified by b
  2  password expire
  3  /
 
User altered.
 
SQL>

The user logs in using an Oracle 10 version of SQL*Plus this time. He is prompted to change his password but is unable to do so:

TEST10 > sqlplus a/b@test11
 
SQL*Plus: Release 10.2.0.3.0 - Production on Wed Aug 26 11:59:46 2015
 
Copyright (c) 1982, 2006, Oracle.  All Rights Reserved.
 
ERROR:
ORA-28001: the password has expired
 
Changing password for a
New password:
Retype new password:
ERROR:
ORA-01017: invalid username/password; logon denied
 
Password unchanged
Enter user-name: 

So, if you want to expire a password in an Oracle 11 database, you need to check that the person who will be logging in to that user afterwards is using an Oracle 11 version of SQL*Plus, not an Oracle 10 one.

ORA-01440

$
0
0
I was asked to run some SQL today and it generated an ORA-01440. I did not remember having seen this error before so I decided to check it out in an Oracle 11.2 database. First I created a table and added a row of data to it:

SQL> create table tab1(col1 number)
  2  /
 
Table created.
 
SQL> insert into tab1 values(1)
 2  /
 
1 row created.
 
SQL>

Then I tried to change col1 to number(5), Oracle gave me an ORA-01440. The reason for this should be obvious:

SQL> alter table tab1 modify (col1 number(5))
  2  /
alter table tab1 modify (col1 number(5))
                         *
ERROR at line 1:
ORA-01440: column to be modified must be empty to decrease precision or scale
 
SQL>
 
I removed the data from the table and when I tried to modify it again, I did not get an error:

SQL> delete tab1
  2  /
 
1 row deleted.
 
SQL> alter table tab1 modify (col1 number(5))
  2  /
 
Table altered.
 
SQL>

Instead of removing the data, I could have set the value(s) to null:
 
SQL> drop table tab1
  2  /
 
Table dropped.
 
SQL> create table tab1(col1 number)
  2  /
 
Table created.
 
SQL> insert into tab1 values(1)
  2  /
 
1 row created.
 
SQL> alter table tab1 modify (col1 number(5))
  2  /
alter table tab1 modify (col1 number(5))
                         *
ERROR at line 1:
ORA-01440: column to be modified must be empty to decrease precision or scale
 
SQL> update tab1 set col1 = null
  2  /
 
1 row updated.
 
SQL> alter table tab1 modify (col1 number(5))
  2  /
 
Table altered.
 
SQL>

Whichever method you choose, you should consider saving the data beforehand and reinstating it afterwards.

ON COMMIT Materialized View Causes ORA-02050 and ORA-02051

$
0
0
This example replicates a problem I discovered today. I tested it in an Oracle 11.2 database. First I created an empty table called tab1:

SQL> create table tab1
  2  (col1 varchar2(30),
  3   constraint con1 primary key (col1))
  4  /
 
Table created.
 
SQL>

Then I set up a materialized view on the table. Note that it is refreshed on commit:

SQL> create materialized view log on tab1
  2  /
 
Materialized view log created.
 
SQL> create materialized view mv1
  2  refresh fast on commit
  3  as select * from tab1
  4  /
 
Materialized view created.
 
SQL>

I added some rows to the table FROM THE LOCAL DATABASE:

SQL> insert into tab1
  2  select table_name
  3  from dba_tables
  4  where owner = 'SYS'
  5  and table_name like 'A%'
  6  /
 
97 rows created.
 
SQL>

I checked that the rows did not appear in the materialized view:

SQL> select count(*) from mv1
  2  /
 
  COUNT(*)
----------
         0
 
SQL>

Then I did a commit to refresh the materialized view:
 
SQL> commit
  2  /
 
Commit complete.
 
SQL>

… and checked that I could see the rows in the table via the materialized view:

SQL> select count(*) from mv1
  2  /
 
  COUNT(*)
----------
        97
 
SQL>

I inserted some more rows into the table FROM A REMOTE DATABASE:

SQL> insert into tab1
  2  select table_name
  3  from dba_tables@remote_database
  4  where owner = 'SYS'
  5  and table_name like 'S%'
  6  /
 
142 rows created.
 
SQL>
 
I checked that I could not see these new rows via the materialized view:
 
SQL> select count(*) from mv1
  2  /
 
  COUNT(*)
----------
        97
 
SQL>
 
If you have an on commit materialized view, you cannot insert rows into any of its master tables from a remote database. (I read this on My Oracle Support.) So when I tried to refresh the materialized view, I got an ORA-02050 and an ORA-02051:
 
SQL> commit
  2  /
commit
*
ERROR at line 1:
ORA-02050: transaction 6.25.157910 rolled back, some
remote DBs may be in-doubt
ORA-02051: another session or branch in same
transaction failed or finalized
 
SQL>
 
… and the number of rows in the materialized view did not change:
 
SQL> select count(*) from mv1
  2  /
 
  COUNT(*)
----------
        97
 
SQL> 

I also noticed the following messages in the alert log: 

Wed Sep 30 17:41:31 2015
Following on-commit snapshots not refreshed :
ANDREW.MV1
Error 2051 trapped in 2PC on transaction 6.25.157910. Cleaning up.
Error stack returned to user:
Wed Sep 30 17:41:31 2015
DISTRIB TRAN BUSDPT1.WORLD.2e3bf2d1.6.25.157910
ORA-02050: transaction 6.25.157910 rolled back, some remote DBs may be in-doubt
  is local tran 6.25.157910 (hex=06.19.268d6)
ORA-02051: another session or branch in same transaction failed or finalized
  insert pending collecting tran, scn=1759205570084 (hex=199.98d2b624)
Wed Sep 30 17:41:31 2015
DISTRIB TRAN BUSDPT1.WORLD.2e3bf2d1.6.25.157910
  is local tran 6.25.157910 (hex=06.19.268d6))
  delete pending collecting tran, scn=1759205570084 (hex=199.98d2b624) 

To isolate the exact cause, I created another table called tab2:

SQL> create table tab2
  2  (col2 varchar2(30),
  3   constraint con2 primary key (col2))
  4  /
 
Table created.
 
SQL>
 
Then I set up an on demand materialized view on the table:
 
SQL> create materialized view log on tab2
  2  /
 
Materialized view log created.
 
SQL> create materialized view mv2
  2  refresh fast on demand
  3  as select * from tab2
  4  /
 
Materialized view created.
 
SQL>
 
I inserted the same rows into the table FROM THE LOCAL DATABASE:
 
SQL> insert into tab2
  2  select table_name
  3  from dba_tables
  4  where owner = 'SYS'
  5  and table_name like 'A%'
  6  /
 
97 rows created.
 
SQL>
 
… and checked that I could not see them via the materialized view:
 
SQL> select count(*) from mv2
  2  /
 
  COUNT(*)
----------
         0
 
SQL>
 
I did a commit:
 
SQL> commit
  2  /
 
Commit complete.
 
SQL>
 
… but this had no effect on the materialized view as it was refreshed on demand:
 
SQL> select count(*) from mv2
  2  /
 
  COUNT(*)
----------
         0
 
SQL>
 
… so I did an on demand refresh:
 
SQL> exec dbms_mview.refresh('mv2');
 
PL/SQL procedure successfully completed.
 
SQL>
 
… and checked that I could see the rows I had added to the table:
 
SQL> select count(*) from mv2
  2  /
 
  COUNT(*)
----------
        97
 
SQL>
 
I added the same rows FROM THE REMOTE DATABASE:
 
SQL> insert into tab2
  2  select table_name
  3  from dba_tables@remote_database
  4  where owner = 'SYS'
  5  and table_name like 'S%'
  6  /
 
142 rows created.
 
SQL>
 
… but when I did a commit, this time it did not fail:
 
SQL> commit
  2  /
 
Commit complete.
 
SQL>
 
Of course, as the materialized view had to be refreshed on demand, the new rows did not appear in it:
 
SQL> select count(*) from mv2
  2  /
 
  COUNT(*)
----------
        97
 
SQL>
 
… so I did another on demand refresh:
 
SQL> exec dbms_mview.refresh('mv2');
 
PL/SQL procedure successfully completed.
 
SQL>
 
… and then I could see them:
 
SQL> select count(*) from mv2
  2  /
 
  COUNT(*)
----------
       239
 
SQL>

Setting NUMWIDTH in PL/SQL Developer

$
0
0
This post is based on a problem I was asked to look at recently.
 
A colleague was using PL/SQL Developer to compare two tables in an Oracle 11 database. He was matching the rows on a key column then checking the corresponding values from a different column and reporting them if they did not match. His query returned almost 2000 rows but they appeared to have equal, not different values. Five people looked at the problem and could not see the cause. They then gave it to me, suggested it might be an Oracle bug and asked me to look for a patch to fix it.
 
I have reproduced the issue in the screen print below although in my example, the two tables only have two columns and one row each. COL1 is set to PI in both tables. I have given this column an alias of NAME. The query joins the two tables using this value as a key. It then compares the COL2 values and reports them if they do not match. I have given TAB1.COL2 an alias of VALUE1 and TAB2.COL2 an alias of VALUE2. According to the screen print, COL2 has the same value in both tables. This being the case, the query should not have displayed these values. As usual, click on the image to enlarge it if necessary:


I’m not an expert with PL/SQL Developer so I decided to see if the same thing happened in SQL*Plus and it did:
 
SQL> select a.col1 name, a.col2 value1, b.col2 value2
  2  from tab1 a, tab2 b
  3  where a.col1 = b.col1
  4  and a.col2 != b.col2
  5  /
 
NAME     VALUE1     VALUE2
---- ---------- ----------
PI   3.14159265 3.14159265
 
SQL>
 
I noticed that the value of COL2 filled the output area in both cases so I checked the value of NUMWIDTH:
 
SQL> show numwidth
numwidth 10
SQL>
 
I gave it a higher value. At this point I cheated a bit because I had set up the test data so I knew how big to make it:
 
SQL> set numwidth 22
SQL>
 
I ran the query again and this time it showed that the values had been different all the time:
 
SQL> select a.col1 name, a.col2 value1, b.col2 value2
  2  from tab1 a, tab2 b
  3  where a.col1 = b.col1
  4  and a.col2 != b.col2
  5  /
 
NAME                 VALUE1                 VALUE2
---- ---------------------- ----------------------
PI         3.14159265358979  3.1415926535897932385
 
SQL>
 
I wondered how I might do the same thing in PL/SQL Developer so I did a bit of research. Then I clicked on Tools / Preferences / SQL Window and put a tick against Number fields to_char. This made the little box next to it show up as green in the screen print below:


I clicked on Apply and OK in the usual way and when I ran the query again, the difference between the column values was obvious:


 

ORA-01166

$
0
0
We have some jobs which copy the datafiles from 1 database to another then recreate the control file to give the output database a new name. Some of these jobs are intelligent i.e. they query the input database to dynamically create the SQL to do the rename. This particular job is not. Part of it is shown below and you can see that the MAXDATAFILES parameter was set to 120:

STARTUP NOMOUNT
CREATE CONTROLFILE REUSE SET DATABASE "MRMDPT1" RESETLOGS  NOARCHIVELOG
--  SET STANDBY TO MAXIMIZE PERFORMANCE
    MAXLOGFILES 10
    MAXLOGMEMBERS 3
    MAXDATAFILES 120
    MAXINSTANCES 1
    MAXLOGHISTORY 452
 
Somebody added an extra datafile to the input database so it then had 121:
 
SQL> select count(*) from dba_data_files
  2  /
 
  COUNT(*)
----------
       121
 
SQL>
 
However, he forgot to update the script so the rename failed as follows:
 
ORA-01503: CREATE CONTROLFILE failed
ORA-01166: file number 121 is larger than MAXDATAFILES (120)
ORA-01110: data file 121: '/cisdpt/mrmdpt1/mrm_tables3/sysauxMRMPROD_2.dbf'
 
I updated the script like this, changing MAXDATAFILES to 150:
 
STARTUP NOMOUNT
CREATE CONTROLFILE REUSE SET DATABASE "MRMDPT1" RESETLOGS  NOARCHIVELOG
--  SET STANDBY TO MAXIMIZE PERFORMANCE
    MAXLOGFILES 10
    MAXLOGMEMBERS 3
    MAXDATAFILES 150
    MAXINSTANCES 1
    MAXLOGHISTORY 452
 
... and when I ran the job again, it worked:
 
SQL*Plus: Release 10.2.0.3.0 - Production on Thu Oct 8 12:16:47 2015
 
Copyright (c) 1982, 2006, Oracle.  All Rights Reserved.
 
Connected to an idle instance.
 
SQL> ORACLE instance started.
 
Total System Global Area  524288000 bytes
Fixed Size                  2031416 bytes
Variable Size             171966664 bytes
Database Buffers          343932928 bytes
Redo Buffers                6356992 bytes
SQL>   2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19   20   21   22   23   24   25   26   27   28   29   30   31   32   33   34   35   36   37   38   39   40   41   42   43   44   45   46   47   48   49   50   51   52   53   54   55   56   57   58   59   60   61   62   63   64   65   66   67   68   69   70   71   72   73   74   75   76   77   78   79   80   81   82   83   84   85   86   87   88   89   90   91   92   93   94   95   96   97   98   99  100  101  102  103  104  105  106  107  108  109  110  111  112  113  114  115  116  117  118  119  120  121  122  123  124  125  126  127  128  129  130  131  132  133  134  135  136  137  138  139  140  141  142  143  144
Control file created.
 
SQL>

ALTER USER Hangs on row cache lock

$
0
0
I created the following UNIX shell script called loop1.ksh:

Oracle 11.1 > cat loop1.ksh
#!/bin/bash
export ORACLE_SID=PQEDPT1
export ORAENV_ASK=NO
. oraenv
sqlplus / as sysdba << eof
grant create session to andrew
identified by reid1
/
exit
eof
echo "User created"
./loop2.ksh > loop2.log1 &
./loop2.ksh > loop2.log2 &
./loop2.ksh > loop2.log3 &
./loop2.ksh > loop2.log4 &
./loop2.ksh > loop2.log5 &
./loop2.ksh > loop2.log6 &
./loop2.ksh > loop2.log7 &
./loop2.ksh > loop2.log8 &
./loop2.ksh > loop2.log9 &
./loop2.ksh > loop2.log10 &
Oracle 11.1 >

I ran it against an Oracle 11.1 database. It created a user called andrew with a password of reid1. It then ran another shell script called loop2.ksh 10 times. The ampersand at the end of each line made these 10 jobs run simultaneously in the background. Each run wrote its output to a different log file i.e. loop2.log1, loop2.log2 etc.

Here is the shell script loop2.ksh:

Oracle 11.1 > cat loop2.ksh
#!/bin/bash
export ORACLE_SID=PQEDPT1
export ORAENV_ASK=NO
. oraenv
for (( i=1; i <=100; i++ ))
do
echo 'i = '$i
sqlplus andrew/reid1 << eof
exit
eof
done
Oracle 11.1 >

It connected to and disconnected from the same database 100 times as user andrew. So I had 10 background jobs, each connecting to and disconnecting from the same database 100 times. Here is part of one of the output files:

DUM11.2 /export/home/oracle/andrew/hanging > cat loop2.log2|more
The Oracle base for ORACLE_HOME=/oracle/app/oracle/product/11.1.0 is /oracle/app/oracle/product
i = 1

SQL*Plus: Release 11.1.0.6.0 - Production on Tue Oct 13 18:26:40 2015

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

SQL> Disconnected from Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
i = 2

SQL*Plus: Release 11.1.0.6.0 - Production on Tue Oct 13 18:26:41 2015

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

SQL> Disconnected from Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
i = 3

SQL*Plus: Release 11.1.0.6.0 - Production on Tue Oct 13 18:26:41 2015

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

SQL> Disconnected from Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
i = 4

SQL*Plus: Release 11.1.0.6.0 - Production on Tue Oct 13 18:26:41 2015

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

SQL> Disconnected from Oracle Database 11g Enterprise Edition Release 11.1.0.6.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
i = 5

SQL*Plus: Release 11.1.0.6.0 - Production on Tue Oct 13 18:26:42 2015

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

While this was going on, I tried to change the password for user andrew in a separate SQL*Plus session. This took a long time. When it had finished, I checked the wait events and saw that it had been waiting on a row cache lock:

SQL> conn / as sysdba
Connected.
SQL> set timing on
SQL> alter user andrew identified by reid2
  2  /

User altered.

Elapsed: 00:00:24.94
SQL> set timing off
SQL> select event, time_waited/100
  2  from v$session_event
  3  where sid = (select distinct sid from v$mystat)
  4  and wait_class != 'Idle'
  5  /

EVENT                               TIME_WAITED/100
----------------------------------- ---------------
log file sync                                     0
row cache lock                                24.34
SQL*Net message to client                         0

SQL>

So if your ALTER USER statement hangs in this way, it could be due to other sessions trying to connect to the database again and again.

Say Hello

$
0
0
I will be going to a seminar about Oracle 12 next week at Oracle City Office, 1 South Place, London. It is called:
Oracle Database 12c– Latest Features, Upgrades & Migrations Seminar and will take place on 21st October. If you go there and recognise me, please say hello!

LOGICAL_READS_PER_SESSION

$
0
0
One of the resources you can limit in a profile is called logical_reads_per_session. According to the Oracle documentation, it is used to:
Specify the permitted number of data blocks read in a session, including blocks read from memory and disk. I decided to try it out in an Oracle 11.1 database.
 
I created a profile called for_andrew, which would limit logical_reads_per_session to 10000:

SQL> conn / as sysdba
Connected.
SQL> create profile for_andrew
  2  limit logical_reads_per_session 10000
  3  /
 
Profile created.
 
SQL>

I set resource_limit to true so that Oracle would enforce the logical_reads_per_session limit:

SQL> alter system set resource_limit = true
  2  /
 
System altered.
 
SQL>

I created a user with the new profile:

SQL> create user andrew
  2  identified by reid
  3  profile for_andrew
  4  /
 
User created.
 
SQL> grant create session,
  2  select any dictionary to andrew
  3  /
 
Grant succeeded.
 
SQL>

I logged in with this user, ran some SQL against dba_tables and counted the session logical reads this had consumed. For the purposes of this simple demonstration, I ignored any overhead which the 2nd piece of SQL may have incurred:

SQL> conn andrew/reid
Connected.
SQL> select max(last_analyzed) from dba_tables
  2  /
 
MAX(LAST_
---------
18-OCT-15
 
SQL> select value from v$sesstat a, v$statname b
  2  where sid =
  3  (select distinct sid from v$mystat)
  4  and a.statistic# = b.statistic#
  5  and name = 'session logical reads'
  6  /
 
       VALUE
------------
        6936
 
SQL>

I logged in again ran some SQL against dba_indexes and counted the session logical reads this had consumed. Again, I ignored any overhead which may have been incurred by checking the session logical reads figure in v$sesstat:

SQL> conn andrew/reid
Connected.
SQL> select max(last_analyzed) from dba_indexes
  2  /
 
MAX(LAST_
---------
18-OCT-15
 
SQL> select value from v$sesstat a, v$statname b
  2  where sid =
  3  (select distinct sid from v$mystat)
  4  and a.statistic# = b.statistic#
  5  and name = 'session logical reads'
  6  /
 
       VALUE
------------
        7341
 
SQL>

The 2 statements together used over 14000 session logical reads. I guessed that if I tried to run them in the same session, this would exceed the limit set by the logical_reads_per_session parameter in the user’s profile. I logged in again and tried to do this. As expected, the 1st SQL worked but the 2nd failed with an ORA-02394:

SQL> conn andrew/reid
Connected.
SQL> select max(last_analyzed) from dba_tables
  2  /
 
MAX(LAST_
---------
18-OCT-15
 
SQL> select max(last_analyzed) from dba_indexes
  2  /
select max(last_analyzed) from dba_indexes
                               *
ERROR at line 1:
ORA-02394: exceeded session limit on IO usage, you are
being logged off
 
SQL>

db_writer_processes

$
0
0
I wrote the first part of this example in 2012.
 
The database writer copies data blocks from the buffer cache onto disk. The db_writer_processes initialization parameter determines how many processes will do this task. Its default value is 1 or cpu_count / 8, whichever is greater. I found an Oracle 9 database on a Tru64 server with cpu_count set to 1:
 
SQL> l
  1  select value from v$parameter
  2* where name = 'cpu_count'
SQL> /
 
VALUE
------------------------------
1
 
SQL>
 
The database used the default value for db_writer_processes, which Oracle had calculated as 1:
 
SQL> select value, isdefault
  2  from v$parameter
  3  where name = 'db_writer_processes'
  4  /
 
VALUE                          ISDEFAULT
------------------------------ ---------
1                              TRUE
 
SQL>
 
(However, since I first wrote this, I have found documentation suggesting that db_writer_processes always defaulted to 1 in Oracle 9 and took no notice of cpu_count.)
 
I looked for this process in the operating system as follows (I had to squash the ps output a bit to make it fit the screen):
 
UNIX > ps -ef|grep dbw|grep TEST9
oracle 250424 1 0.0 02:07:18 ?? 0:00.87 ora_dbw0_TEST9
UNIX >
 
I found an Oracle 11 database on a Solaris server with cpu_count set to 16:
 
SQL> select value from v$parameter
  2  where name = 'cpu_count'
  3  /
 
VALUE
------------------------------
16
 
SQL>
 
It also used the default value for db_writer_processes, which Oracle had calculated as 2:
 
SQL> select value, isdefault
  2  from v$parameter
  3  where name = 'db_writer_processes'
  4  /
 
VALUE                          ISDEFAULT
------------------------------ ---------
2                              TRUE
 
SQL>
 
When I found these processes in the operating system, I saw that Oracle had given them consecutive numbers i.e. dbw0 and dbw1 (the ps output was squashed again to fit the screen):
 
UNIX > ps -ef|grep dbw|grep PROD11
oracle 12230 1 0 19:42:36 ? 1:24 ora_dbw0_PROD11
oracle 12232 1 0 19:42:36 ? 1:21 ora_dbw1_PROD11
UNIX >
 
If you don’t have enough database writer processes, you can apparently have problems with free buffer waits. I searched all our production databases and could only find two with any of these at all. This was one of them:
 
  1  select time_waited from v$system_event
  2* where event = 'free buffer waits'
SQL> /
 
TIME_WAITED
-----------
71
 
SQL>
 
As this figure is given in hundredths of a second, I decided to do nothing about it.
 
I created an Oracle 11.2.0.4 database in 2015 and set db_writer_processes to 36:
 
SQL> alter system set db_writer_processes = 36
  2  scope = spfile
  3  /
 
System altered.
 
SQL>
 
Then I bounced the database (this is not shown). When I looked for the database writer processes in the operating system, I saw that Oracle had called them dbw0 through dbw9 then dbwa through dbwz:
 
UNIX > ps -ef|grep dbw|grep BECHEDV1
oracle 50594 50565 0 17:27:03 ? 0:00 ora_dbwh_BECHEDV1
oracle 50583 50565 0 17:27:02 ? 0:00 ora_dbw6_BECHEDV1
oracle 50578 50565 0 17:27:02 ? 0:00 ora_dbw1_BECHEDV1
oracle 50611 50565 0 17:27:03 ? 0:00 ora_dbww_BECHEDV1
oracle 50589 50565 0 17:27:02 ? 0:00 ora_dbwc_BECHEDV1
oracle 50593 50565 0 17:27:03 ? 0:00 ora_dbwg_BECHEDV1
oracle 50600 50565 0 17:27:03 ? 0:00 ora_dbwn_BECHEDV1
oracle 50615 50565 0 17:27:03 ? 0:00 ora_dbwz_BECHEDV1
oracle 50612 50565 0 17:27:03 ? 0:00 ora_dbwx_BECHEDV1
oracle 50582 50565 0 17:27:02 ? 0:00 ora_dbw5_BECHEDV1
oracle 50580 50565 0 17:27:02 ? 0:00 ora_dbw3_BECHEDV1
oracle 50608 50565 0 17:27:03 ? 0:00 ora_dbwt_BECHEDV1
oracle 50588 50565 0 17:27:02 ? 0:00 ora_dbwb_BECHEDV1
oracle 50610 50565 0 17:27:03 ? 0:00 ora_dbwv_BECHEDV1
oracle 50579 50565 0 17:27:02 ? 0:00 ora_dbw2_BECHEDV1
oracle 50602 50565 0 17:27:03 ? 0:00 ora_dbwp_BECHEDV1
oracle 50595 50565 0 17:27:03 ? 0:00 ora_dbwi_BECHEDV1
oracle 50614 50565 0 17:27:03 ? 0:00 ora_dbwy_BECHEDV1
oracle 50598 50565 0 17:27:03 ? 0:00 ora_dbwl_BECHEDV1
oracle 50609 50565 0 17:27:03 ? 0:00 ora_dbwu_BECHEDV1
oracle 50590 50565 0 17:27:03 ? 0:00 ora_dbwd_BECHEDV1
oracle 50581 50565 0 17:27:02 ? 0:00 ora_dbw4_BECHEDV1
oracle 50586 50565 0 17:27:02 ? 0:00 ora_dbw9_BECHEDV1
oracle 50596 50565 0 17:27:03 ? 0:00 ora_dbwj_BECHEDV1
oracle 50584 50565 0 17:27:02 ? 0:00 ora_dbw7_BECHEDV1
oracle 50599 50565 0 17:27:03 ? 0:00 ora_dbwm_BECHEDV1
oracle 50604 50565 0 17:27:03 ? 0:00 ora_dbwr_BECHEDV1
oracle 50587 50565 0 17:27:02 ? 0:00 ora_dbwa_BECHEDV1
oracle 50585 50565 0 17:27:02 ? 0:00 ora_dbw8_BECHEDV1
oracle 50591 50565 0 17:27:03 ? 0:00 ora_dbwe_BECHEDV1
oracle 50603 50565 0 17:27:03 ? 0:00 ora_dbwq_BECHEDV1
oracle 50601 50565 0 17:27:03 ? 0:00 ora_dbwo_BECHEDV1
oracle 50606 50565 0 17:27:03 ? 0:00 ora_dbws_BECHEDV1
oracle 50597 50565 0 17:27:03 ? 0:00 ora_dbwk_BECHEDV1
oracle 50592 50565 0 17:27:03 ? 0:00 ora_dbwf_BECHEDV1
oracle 50577 50565 0 17:27:02 ? 0:00 ora_dbw0_BECHEDV1
UNIX >
 
I read somewhere that the maximum value for db_writer_processes is 36 but Oracle allowed me to change it to 37:
 
SQL> alter system set db_writer_processes = 37
  2  scope = spfile
  3  /
 
System altered.
 
SQL>
 
Then I bounced the database again:
 
SQL> shutdown
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORACLE instance started.
 
Total System Global Area  521936896 bytes
Fixed Size                  2252448 bytes
Variable Size             306184544 bytes
Database Buffers          205520896 bytes
Redo Buffers                7979008 bytes
Database mounted.
Database opened.
SQL> exit
Disconnected from Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
UNIX >
 
However, when I checked in the operating system, there were still only 36 database writer processes:
 
UNIX >  ps -ef|grep dbw|grep BECHEDV1|wc -l
      36
UNIX >
 
… and there was a message in the alert log telling me that db_writer_processes had been adjusted:
 
NOTE: db_writer_processes has been changed from 37 to  36ue to NUMA requirements.

O7_DICTIONARY_ACCESSIBILITY

$
0
0
Long ago, in Oracle 7 I believe, a user with the SELECT ANY TABLE privilege could access tables and views owned by SYS. Also, a user with the EXECUTE ANY  PROCEDURE privilege could run code owned by SYS. Nowadays, this behaviour is controlled by the O7_DICTIONARY_ACCESSIBILITY initialisation parameter. The default value for this is FALSE, as you can see in the query below, which I ran in an Oracle 11.1 database:
 
SQL> conn / as sysdba
Connected.
SQL> l
  1  select value, isdefault
  2  from v$parameter
  3* where name = 'O7_DICTIONARY_ACCESSIBILITY'
SQL> /
 
VALUE                ISDEFAULT
-------------------- ---------
FALSE                TRUE
 
SQL>
 
I created a user in this database and granted it the SELECT ANY TABLE and EXECUTE ANY PROCEDURE privileges:
 
SQL> create user andrew
  2  identified by reid
  3  /
 
User created.
 
SQL> grant create session,
  2  select any table,
  3  execute any procedure
  4  to andrew
  5  /
 
Grant succeeded.
 
SQL>
 
I logged in with this user and found that it could neither SELECT from DBA_TABLES nor EXEC SYS.DBMS_LOCK.SLEEP:
 
SQL> conn andrew/reid
Connected.
SQL> select count(*) from dba_tables
  2  /
select count(*) from dba_tables
                     *
ERROR at line 1:
ORA-00942: table or view does not exist
 
SQL> exec sys.dbms_lock.sleep(10);
BEGIN sys.dbms_lock.sleep(10); END;
 
      *
ERROR at line 1:
ORA-06550: line 1, column 7:
PLS-00201: identifier 'SYS.DBMS_LOCK' must be declared
ORA-06550: line 1, column 7:
PL/SQL: Statement ignored
 
SQL>
 
I changed O7_DICTIONARY_ACCESSIBILITY to TRUE:
 
SQL> conn / as sysdba
Connected.
SQL> l
  1  alter system
  2  set o7_dictionary_accessibility = true
  3* scope = spfile
SQL> /
 
System altered.
 
SQL> startup force
ORACLE instance started.
 
Total System Global Area  158703616 bytes
Fixed Size                  2086736 bytes
Variable Size             104859824 bytes
Database Buffers           46137344 bytes
Redo Buffers                5619712 bytes
Database mounted.
Database opened.
SQL>
 
Then when I logged in with my user, it could SELECT from DBA_TABLES and EXEC SYS.DBMS_LOCK.SLEEP:
 
SQL> conn andrew/reid
Connected.
SQL> select count(*) from dba_tables
  2  /
 
  COUNT(*)
----------
      1021
 
SQL> exec sys.dbms_lock.sleep(10);
 
PL/SQL procedure successfully completed.
 
SQL>

AUTHID CURRENT_USER

$
0
0
If USERA creates a function or procedure and allows USERB to run it, USERB does so with USERA’s permissions. However, if USERA adds the AUTHID CURRENT_USER clause to the code, USERB runs it with its own permissions. You can see what I mean in the example below, which I tested in an Oracle 11.1 database:
 
I created a user called USERB and allowed it to login to the database:
 
SQL> create user userb
  2  identified by userb
  3  /
 
User created.
 
SQL> grant create session to userb
  2  /
 
Grant succeeded.
 
SQL>
 
I created a user called USERA and allowed it to connect to the database and create tables and compiled code:
 
SQL> create user usera
  2  identified by usera
  3  default tablespace users
  4  quota unlimited on users
  5  /
 
User created.
 
SQL> grant create session,
  2  create table,
  3  create procedure to usera
  4  /
 
Grant succeeded.
 
SQL>
 
USERA connected to the database and created a table called CARS with 2 rows:
 
SQL> conn usera/usera
Connected.
SQL> create table cars as
  2  select 'AUSTIN' manufacturer from dual
  3  /
 
Table created.
 
SQL> insert into cars values('MORRIS')
  2  /
 
1 row created.
 
SQL> select count(*) from cars
  2  /
 
  COUNT(*)
----------
         2
 
SQL>
 
USERA then created a function to accept a table name parameter, count the number of rows in that table and return the value to the caller. USERA then allowed USERB to execute this function:
 
SQL> create function count_rows
  2  (tab in varchar2) return pls_integer
  3  is
  4  row_count pls_integer;
  5  begin
  6  execute immediate 'select count(*) from '||tab
  7  into row_count;
  8  return row_count;
  9  end count_rows;
10  /
 
Function created.
 
SQL> grant execute on count_rows to userb
  2  /
 
Grant succeeded.
 
SQL>
 
USERA created a copy of this function with the AUTHID CURRENT_USER clause:
 
SQL> create function count_rows_acu
  2  (tab in varchar2) return pls_integer
  3  authid current_user
  4  is
  5  row_count pls_integer;
  6  begin
  7  execute immediate 'select count(*) from '||tab
  8  into row_count;
  9  return row_count;
10  end count_rows_acu;
11  /
 
Function created.
 
SQL> grant execute on count_rows_acu to userb
  2  /
 
Grant succeeded.
 
SQL>
 
USERB then logged in and tried to look at USERA’s CARS table but failed, as you would expect:
 
SQL> conn userb/userb
Connected.
SQL> select count(*) from usera.cars
  2  /
select count(*) from usera.cars
                           *
ERROR at line 1:
ORA-00942: table or view does not exist
 
SQL>
 
USERB then ran USERA’s first function to count the rows in the CARS table. This did not have the AUTHID CURRENT_USER clause so it ran with USERA’s permissions and there was no problem:
 
SQL> select usera.count_rows('cars') from dual
  2  /
 
USERA.COUNT_ROWS('CARS')
------------------------
                       2
 
SQL>
 
USERB then ran USERA’s second function to count the rows in the CARS table. This had the AUTHID CURRENT_USER clause so it ran with USERB’s permissions and failed:
 
SQL> select usera.count_rows_acu('cars') from dual
  2  /
select usera.count_rows_acu('cars') from dual
       *
ERROR at line 1:
ORA-00942: table or view does not exist
ORA-06512: at "USERA.COUNT_ROWS_ACU", line 7
 
SQL>

UK OUG Conference 2015

$
0
0
I went to the UK OUG Conference for the first time around 12 years ago, in 2003 if I remember correctly. I enjoyed all the presentations but the two which stuck in my mind were by Jonathan Lewis and Connor McDonald.

This year I will be chairing 8 sessions there, which means I will be introducing the speaker and making sure nothing goes wrong.

Two of these sessions are on 6th December 2015, which is known as Super Sunday:

http://www.supersunday15.ukoug.org/

The other six are in the main part of the conference, which goes from 7th to 9th December 2015:

http://www.tech15.ukoug.org/?utm_source=resourcepack&utm_medium=banner&utm_campaign=tech15

You really should attend if you are given the opportunity. If you see me there, don't forget to say hello.

SELECT ANY SEQUENCE

$
0
0
I used to think that a user with SELECT ANY TABLE and SELECT ANY DICTIONARY could see anything in a database. I found out today that these 2 privileges do not allow you to SELECT from another user’s sequence. You can see what I mean in the example below, which I tested in an Oracle 11.1 database. First I created a user to own a sequence:
 
SQL> conn / as sysdba
Connected.
SQL> create user user1
  2  identified by user1
  3  /
 
User created.
 
SQL> grant create session, create sequence
  2  to user1
  3  /
 
Grant succeeded.
 
SQL>
 
Then I created a second user to SELECT from the first user’s sequence:
 
SQL> create user user2
  2  identified by user2
  3  /
 
User created.
 
SQL> grant create session,
  2  select any table,
  3  select any dictionary to user2
  4  /
 
Grant succeeded.
 
SQL>
 
USER1 then created a sequence:
 
SQL> conn user1/user1
Connected.
SQL> create sequence sequence1
  2  /
 
Sequence created.
 
SQL> select sequence1.nextval from dual
  2  /
 
   NEXTVAL
----------
         1
 
SQL>
 
… but USER2 could not SELECT from it:
 
SQL> conn user2/user2
Connected.
SQL> select user1.sequence1.nextval from dual
  2  /
select user1.sequence1.nextval from dual
             *
ERROR at line 1:
ORA-01031: insufficient privileges
 
SQL>
 
One way round this is to GRANT the SELECT ANY SEQUENCE privilege like this:
 
SQL> conn / as sysdba
Connected.
SQL> grant select any sequence to user2
  2  /
 
Grant succeeded.
 
SQL> conn user2/user2
Connected.
SQL> select user1.sequence1.nextval from dual
  2  /
 
   NEXTVAL
----------
         2
 
SQL>

PRAGMA EXCEPTION_INIT

$
0
0
You can use one of these to link an exception name with an Oracle error number. Once you have done this, you can use the exception name in the exception block which follows the declaration. You can see what I mean in the example below, which I tested in an Oracle 11.2 database. First I set up a table so I could test the procedure I was going to create:
 
SQL> create table real_table
  2  as select * from dba_tables
  3  /
 
Table created.
 
SQL>
 
Then I created a procedure to count the rows in a table. On line 5, I created an exception called table_not_found. On line 6, I associated this with ORA-00942. On line 12, I tested for this exception and displayed an error message if appropriate:
 
SQL> create or replace procedure count_rows
  2  (tab in varchar2)
  3  is
  4   row_count number;
  5   table_not_found exception;
  6   pragma exception_init(table_not_found,-942);
  7  begin
  8   execute immediate 'select count(*) from '||tab
  9   into row_count;
 10   dbms_output.put_line(tab||' has '||row_count||' rows');
 11  exception
 12   when table_not_found then
 13   dbms_output.put_line('I cannot find '||tab);
 14  end;
 15  /
 
Procedure created.
 
SQL>

When I tested the procedure, it counted the rows in real_table and told me it could not find imaginary_table:

SQL> set serveroutput on
SQL> exec count_rows('real_table');
real_table has 3172 rows
 
PL/SQL procedure successfully completed.
 
SQL> exec count_rows('imaginary_table');
I cannot find imaginary_table
 
PL/SQL procedure successfully completed.

SQL>

GRANT SELECT Updates LAST_DDL_TIME

$
0
0
DDL stands for Data Definition Language. The CREATE TABLE, ALTER TABLE and DROP TABLE statements are examples of DDL. LAST_DDL_TIME is a column in the USER_OBJECTS view. It records the date and time of the most recent DDL statement applied to the object in question. Even granting SELECT access on a table will update its LAST_DDL_TIME. You can see this in the example below, which I tested in an Oracle 11.2 database.
 
First I created a table and checked that its LAST_DDL_TIME matched the creation time:
 
SQL> select to_char(sysdate,'hh24:mi:ss') time_now
  2  from dual
  3  /
 
TIME_NOW
--------
18:39:50
 
SQL> create table tab1(col1 number)
  2  /
 
Table created.
 
SQL> select to_char(last_ddl_time,'hh24:mi:ss') time_now
  2  from user_objects
  3  where object_name = 'TAB1'
  4  /
 
TIME_NOW
--------
18:39:50
 
SQL>
 
Then I waited 10 seconds, noted the time again, ran some DDL on the table and checked that this had updated the LAST_DDL_TIME:
 
SQL> exec sys.dbms_lock.sleep(10);
 
PL/SQL procedure successfully completed.
 
SQL> select to_char(sysdate,'hh24:mi:ss') time_now
  2  from dual
  3  /
 
TIME_NOW
--------
18:40:00
 
SQL> alter table tab1 add(col2 number)
  2  /
 
Table altered.
 
SQL> select to_char(last_ddl_time,'hh24:mi:ss') time_now
  2  from user_objects
  3  where object_name = 'TAB1'
  4  /
 
TIME_NOW
--------
18:40:00
 
SQL>
 
Finally, I waited a further 10 seconds, noted the time, did a GRANT SELECT on the table to another user and checked that the LAST_DDL_TIME had been updated again:
 
SQL> exec sys.dbms_lock.sleep(10);
 
PL/SQL procedure successfully completed.
 
SQL> select to_char(sysdate,'hh24:mi:ss') time_now
  2  from dual
  3  /
 
TIME_NOW
--------
18:40:10
 
SQL> grant select on tab1 to fred
  2  /
 
Grant succeeded.
 
SQL> select to_char(last_ddl_time,'hh24:mi:ss') time_now
  2  from user_objects
  3  where object_name = 'TAB1'
  4  /
 
TIME_NOW
--------
18:40:10

SQL>

Segment Creation Deferred not Available in Standard Edition

$
0
0
If you use Oracle Standard Edition to create a production database, you need to be sure to create any corresponding test databases in Oracle Standard Edition too. Otherwise you may find that some SQL might be tested successfully, only to fail when you implement it in production. You can see what I mean in the example below. First I created a table with segment creation deferred in the Oracle Enterprise Edition test database:
 
C:\Users\AJ0294094>sqlplus cmp
 
SQL*Plus: Release 11.2.0.1.0 Production on Mon Nov 16 12:35:04 2015
 
Copyright (c) 1982, 2010, Oracle.  All rights reserved.
 
Enter password:
 
Connected to:
Oracle Database 11g Enterprise Edition Release 11.2.0.1.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
 
SQL> create table tab1 (col1 number)
  2  segment creation deferred
  3  /
 
Table created.
 
SQL>
 
Later, when I tried to create the same table in the Oracle Standard Edition production database, Oracle returned an ORA-00439:
 
C:\Users\AJ0294094>sqlplus cmp
 
SQL*Plus: Release 11.2.0.1.0 Production on Mon Nov 16 12:37:13 2015
 
Copyright (c) 1982, 2010, Oracle.  All rights reserved.
 
Enter password:
 
Connected to:
Oracle Database 11g Release 11.2.0.1.0 - 64bit Production
 
SQL> create table tab1 (col1 number)
  2  segment creation deferred
  3  /
create table tab1 (col1 number)
*
ERROR at line 1:
ORA-00439: feature not enabled: Deferred Segment Creation

SQL>

A Problem with REVOKE

$
0
0
If you grant the DBA role to a user, Oracle also grants it the UNLIMITED TABLESPACE system privilege. If you then revoke the DBA role from this user, Oracle also revokes its UNLIMITED TABLESPACE system privilege. This isn’t too much of an issue.
 
However, if you grant the UNLIMITED TABLESPACE system privilege to a user by itself THEN grant it the DBA role, Oracle seems to have no idea where the UNLIMITED TABLESPACE system privilege came from. If you then revoke the DBA role from this user, Oracle still revokes the UNLIMITED TABLESPACE system privilege from it. This may not be what you intended to do. You can see what I mean in the example below, which I tested in an Oracle 11.2 database.
 
First I created a user:
 
SQL> create user a identified by b
  2  /
 
User created.
 
SQL>
 
I checked that it had no roles nor system privileges:
 
SQL> select granted_role from dba_role_privs
  2  where grantee = 'A'
  3  /
 
no rows selected
 
SQL> select privilege from dba_sys_privs
  2  where grantee = 'A'
  3  /
 
no rows selected
 
SQL>
 
I granted the DBA role to the user and checked that this also gave it the UNLIMITED TABLESPACE system privilege:
 
SQL> grant dba to a
  2  /
 
Grant succeeded.
 
SQL> select granted_role from dba_role_privs
  2  where grantee = 'A'
  3  /
 
GRANTED_ROLE
------------------------------
DBA
 
SQL> select privilege from dba_sys_privs
  2  where grantee = 'A'
  3  /
 
PRIVILEGE
----------------------------------------
UNLIMITED TABLESPACE
 
SQL>
 
I revoked the DBA role and checked that Oracle also revoked the UNLIMITED TABLESPACE system privilege:
 
SQL> revoke dba from a
  2  /
 
Revoke succeeded.
 
SQL> select granted_role from dba_role_privs
  2  where grantee = 'A'
  3  /
 
no rows selected
 
SQL> select privilege from dba_sys_privs
  2  where grantee = 'A'
  3  /
 
no rows selected
 
SQL>
 
I granted the UNLIMITED TABLESPACE system privilege to the user independently:
 
SQL> grant unlimited tablespace to a
  2  /
 
Grant succeeded.
 
SQL> select granted_role from dba_role_privs
  2  where grantee = 'A'
  3  /
 
no rows selected
 
SQL> select privilege from dba_sys_privs
  2  where grantee = 'A'
  3  /
 
PRIVILEGE
----------------------------------------
UNLIMITED TABLESPACE
 
SQL>
 
I granted the DBA role to the user:
 
SQL> grant dba to a
  2  /
 
Grant succeeded.
 
SQL> select granted_role from dba_role_privs
  2  where grantee = 'A'
  3  /
 
GRANTED_ROLE
------------------------------
DBA
 
SQL> select privilege from dba_sys_privs
  2  where grantee = 'A'
  3  /
 
PRIVILEGE
----------------------------------------
UNLIMITED TABLESPACE
 
SQL>
 
I revoked the DBA role from the user. Oracle revoked the UNLIMITED TABLESPACE system privilege at the same time despite the fact that I had granted it separately:
 
SQL> revoke dba from a
  2  /
 
Revoke succeeded.
 
SQL> select granted_role from dba_role_privs
  2  where grantee = 'A'
  3  /
 
no rows selected
 
SQL> select privilege from dba_sys_privs
  2  where grantee = 'A'
  3  /

no rows selected

SQL>

A Simple Example of an Index Organised Table Without Overflow

$
0
0
For a long time I have had a note on my task list to learn about index organized tables. I never got round to doing it because I thought I would never see one. However, I came across several in a 3rd party application recently. An index organized table is a kind of index and table combined. You can see how they work in the example below, which I tested in an Oracle 11.2 database:
 
First I created a sequence. You don’t need a sequence to create an index organized table. I just used it to ensure that the index always contained unique values:

SQL> create sequence seq1
  2  /
 
Sequence created.

SQL>

I read that an index organized table cannot contain a LONG column. When I tried to do this, Oracle returned an ORA-02160:

SQL> create table iot1
  2  (owner varchar2(30),
  3   object_name varchar2(30),
  4   seq_no number,
  5   column_not_allowed long,
  6   constraint iot1_pk
  7   primary key (owner, object_name, seq_no))
  8  organization index
  9  /
organization index
             *
ERROR at line 8:
ORA-02160: index-organized table can not contain
columns of type LONG
 
SQL>

Without the LONG column, the index organized table was created successfully. This was an index organized table without overflow. I will try to look at overflow in a future post:

SQL> create table iot1
  2  (owner varchar2(30),
  3   object_name varchar2(30),
  4   seq_no number,
  5   constraint iot1_pk
  6   primary key (owner, object_name, seq_no))
  7  organization index
  8  /
 
Table created.
 
SQL>

After creating the index organized table, it had an IOT_TYPE of IOT. The IOT_NAME column was empty as the index organized table did not have overflow:

SQL> select iot_type, nvl(iot_name,'NULL')
  2  from user_tables
  3  where table_name = 'IOT1'
  4  /
 
IOT_TYPE             NVL(IOT_NAME,'NULL')
-------------------- ------------------------------
IOT                  NULL

SQL>

I added data to the index organized table like this:

SQL> begin
  2   for i in 1..15 loop
  3    insert into iot1
  4    select owner, object_name, seq1.nextval
  5    from dba_objects;
  6   end loop;
  7  end;
  8  /
 
PL/SQL procedure successfully completed.
 
SQL> select count(*) from iot1
  2  /
 
  COUNT(*)
----------
    996060

SQL>

The index organized table had an entry in DBA_TABLES:

SQL> select count(*) from dba_tables
  2  where table_name = 'IOT1'
  3  /
 
  COUNT(*)
----------
         1

SQL>

... but it did not appear in DBA_SEGMENTS:

SQL> select count(*) from dba_segments
  2  where segment_name = 'IOT1'
  3  /
 
  COUNT(*)
----------
         0

SQL>

... and its TABLESPACE_NAME entry in DBA_TABLES was empty: 

SQL> select nvl(tablespace_name,'NULL')
  2  from dba_tables
  3  where table_name = 'IOT1'
  4  /
 
NVL(TABLESPACE_NAME,'NULL')
------------------------------
NULL

SQL>

The index associated with the index organized table had an entry in DBA_SEGMENTS so I checked how big it was:

SQL> select bytes from dba_segments
  2  where segment_name = 'IOT1_PK'
  3  /
 
     BYTES
----------
  83886080

SQL>

Then I deleted several rows from the index organized table:

SQL> delete from iot1
  2  where owner = 'SYS'
  3  /
 
460995 rows deleted.

SQL>

... and checked that this deleted some leaf rows from the index:

SQL> analyze index iot1_pk validate structure
  2  /
 
Index analyzed.
 
SQL> select name, lf_rows, del_lf_rows
  2  from index_stats
  3  /
 
NAME          LF_ROWS DEL_LF_ROWS
---------- ---------- -----------
IOT1_PK        848331      313266
 
SQL>

When this happens, you can usually just rebuild the index but when I tried to do this to the index for the index organized table, Oracle returned an ORA-28650:

SQL> alter index iot1_pk rebuild
  2  /
alter index iot1_pk rebuild
*
ERROR at line 1:
ORA-28650: Primary index on an IOT cannot be rebuilt
 
SQL>

So I moved the table instead:

SQL> alter table iot1 move online
  2  /
 
Table altered.

SQL>

... then I analyzed the index again:

SQL> analyze index iot1_pk validate structure
  2  /
 
Index analyzed.

SQL>

... and saw that the deleted leaf rows had disappeared:
 
SQL> select name, lf_rows, del_lf_rows
  2  from index_stats
  3  /
 
NAME          LF_ROWS DEL_LF_ROWS
---------- ---------- -----------
IOT1_PK        535065           0

SQL>

... and the index was much smaller:

SQL> select bytes from dba_segments
  2  where segment_name = 'IOT1_PK'
  3  /
 
     BYTES
----------
  25165824

SQL>

Oracle Live SQL

$
0
0
I had the opportunity to attend the UKOUG Conference recently. While I was there, I went to a Roundtable run by Shakeeb Rahman from Oracle. He told us about an Oracle tool which allows you to run SQL online without installing Oracle yourself. All you need is an account on My Oracle Support. I logged onto it here. It’s immediately obvious how it works so, once I had connected, I decided to see which version of Oracle I was using (Oracle 12.1.0.2). As usual, click on the image to enlarge it:


I do not have access to an Oracle 12 database right now so this tool will help me to learn some new features. I will post the results in due course.
Viewing all 330 articles
Browse latest View live