Showing posts with label dba_tables. Show all posts
Showing posts with label dba_tables. Show all posts

Wednesday, January 22, 2014

ORA-00069

A colleague asked what the table_lock column in dba_tables was for. I did not know so I decided to investigate.First I looked at the distribution of table_lock values in one of our test databases:

SQL> select table_lock, count(*)
  2  from dba_tables
  3  group by table_lock
  4  /

TABLE_LOCK  COUNT(*)
---------- ----------
ENABLED          825

SQL>

 
This suggested to me that the default setting is enabled so I tried this out by creating a test table:

SQL> create table andrew (col1 number)
  2  /

Table created.

SQL> 


As expected, the table_lock column was set to enabled for this new table:

SQL> select table_lock from dba_tables
  2  where table_name = 'ANDREW'
  3  /

TABLE_LOCK
----------
ENABLED

SQL> 


You can alter this as follows:

SQL> alter table andrew disable table lock
  2  /

Table altered.

SQL>


And this sets the table_lock value to disabled:

SQL> select table_lock from dba_tables
  2  where table_name = 'ANDREW'
  3  /

TABLE_LOCK
----------
DISABLED

SQL> 


Setting table_lock to disabled stops you locking that table:

SQL> lock table andrew in share mode
  2  /
lock table andrew in share mode
*
ERROR at line 1:
ORA-00069: cannot acquire lock -- table locks
disabled for ANDREW

SQL> 


It also stops you running other DDL against it:

SQL> rename andrew to fred
  2  /
rename andrew to fred
*
ERROR at line 1:
ORA-00069: cannot acquire lock -- table locks
disabled for ANDREW

SQL>


And setting it back to enabled allows you to lock the table:


SQL> alter table andrew enable table lock
  2  /

Table altered.


SQL>

And run other DDL on the table:

SQL> rename andrew to fred
  2  /

Table renamed.

SQL>


So the main purpose of the table_lock column is to show whether you are allowed to lock a table or not.

Thursday, January 24, 2013

How to Add Conditions After a Where Clause

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>

Sunday, September 16, 2012

Subquery with Order By?

This was tested on Oracle 11.1.0.6.0 running on Windows XP. Looking through some course notes from 1990 (as you do), I read that you cannot include an order by in a subquery. I'm not sure why you would ever want to do such a thing but I decided to see what happened if you did:

SQL> l
  1  select count(*)
  2  from dba_tables
  3  where table_name not in
  4  (select table_name
  5   from dba_indexes
  6*  order by 1)
SQL> /
 order by 1)
 *
ERROR at line 6:
ORA-00907: missing right parenthesis

SQL> l
  1  select count(*)
  2  from dba_tables
  3  where table_name not in
  4  (select table_name
  5   from dba_indexes
  6*  order by table_name)
SQL> /
 order by table_name)
 *
ERROR at line 6:
ORA-00907: missing right parenthesis

SQL>

It gave me an ORA-00907, as you can see above, which was not especially helpful. I removed the order by and the query ran successfully:

SQL> l
  1  select count(*)
  2  from dba_tables
  3  where table_name not in
  4  (select table_name
  5*  from dba_indexes)
SQL> /

  COUNT(*)
----------
       444

SQL>

However, I have just thought of a special kind of subquery, sometimes called an in-line view, which is allowed to have an order by. Here is an example, suggested by Laurent in the 1st comment below:

SQL> l
  1  select * from
  2  (select sid, a.value/100 CPU_Seconds
  3   from v$sesstat a, v$sysstat b
  4   where a.statistic# = b.statistic#
  5   and name = 'CPU used by this session'
  6   order by a.value desc)
  7* where rownum < 6
SQL> /
       SID CPU_SECONDS
---------- -----------
        71      401.64
       140      361.04
        10      306.59
         9         306
        43      280.75
SQL>

Tuesday, April 05, 2011

Calculating Averages (Part 2)

Go to part 1

(Tested on Oracle 9 but probably not on the database used for part 1.)

In Calculating Averages (Part 1) I ran the following SQL:

select avg(num_rows) from dba_tables
where num_rows is not null;

Since then I have read that the where clause was unnecessary as Oracle does not include null values when it works out averages. I decided to see if this was true. First I checked that there were both null and not null values in the num_rows column in dba_tables:

SQL> select count(*) from dba_tables
  2  where num_rows is null;

  COUNT(*)
----------
       459

SQL> select count(*) from dba_tables
  2  where num_rows is not null;

  COUNT(*)
----------
       366

SQL>

Incidentally, you can combine the two statements above into one by using decode:

SQL> select
  2  decode(num_rows,null,'NULL','NOT NULL') row_count,
  3  count(*)
  4  from dba_tables
  5  group by decode(num_rows,null,'NULL','NOT NULL');

ROW_COUNT   COUNT(*)
--------- ----------
NOT NULL         366
NULL             459

SQL>

Then I calculated the average as before:

SQL> select avg(num_rows) from dba_tables
  2  where num_rows is not null;

AVG(NUM_ROWS)
-------------
   783867.724

SQL>

Finally, I omitted the where clause and the result was the same. This proves that avg ignores null values:

SQL> select avg(num_rows) from dba_tables;

AVG(NUM_ROWS)
-------------
   783867.724

SQL>

Wednesday, January 26, 2011

Calculating Averages (Part 1)

You can calculate average values with the AVG function as shown below. The second statement double checks the answer:

SQL> select avg(num_rows)
  2  from dba_tables
  3  where num_rows is not null
  4  /

AVG(NUM_ROWS)
-------------
   153051.912

SQL> select sum(num_rows) / count(*)
  2  from dba_tables
  3  where num_rows is not null
  4  /

SUM(NUM_ROWS)/COUNT(*)
----------------------
            153051.912

SQL>

Footnote: Since writing this post I have noticed that it is slightly misleading.
Go to part 2 for details.