Thursday, January 14, 2010

11g Vitual Columns howto and performance

Let's do some basic testing on Virtual columns.


1 create table employees
2 (empno number,
3 firstname varchar2(100),
4 lastname varchar2(100),
5 email varchar2(100),
6 loweremail as (lower(email)),
7 emp_full_name as (firstname || ' ' || lastname)
8* )
SQL> /

Table created.

Elapsed: 00:00:00.15



And try to insert some values....

insert into employees values (1,'ALLEN', 'BECK','AllenBeck@aol.com')
*
ERROR at line 1:
ORA-00947: not enough values


Elapsed: 00:00:00.00

Bamm...it failed. So it needs values for virtual columns too ??


insert into employees values (2,'Blah', 'Jlah', 'blah.jlah@msn.com', 'blah.jlah@msn.com','Blah Jlah')
*
ERROR at line 1:
ORA-54013: INSERT operation disallowed on virtual columns


Elapsed: 00:00:00.01



Not really. Then ?? Why did it fail ? Let's try doing something different...


SQL> insert into employees (empno,firstname, lastname, email) values (1,'ALLEN', 'BECK','AllenBeck@aol.com');

1 row created.

Elapsed: 00:00:00.00



So we know using virtual columns puts some limitations on how we do inserts.


Let's retrieve this data back..


1* select * from employees
SQL> /

EMPNO FIRSTNAME LASTNAME EMAIL LOWEREMAIL EMP_FULL_NAME
---------- ---------- ------------------------- -------------------- -------------------- ------------------------------
1 ALLEN BECK AllenBeck@aol.com allenbeck@aol.com ALLEN BECK

Elapsed: 00:00:00.00
SQL>

How to make sure the column is Virtual ?


1 select table_name, column_name, data_type, hidden_column
2 from dba_tab_cols
3 where table_name = 'EMPLOYEES'
4* and virtual_column = 'YES'
SQL> /

TABLE_NAME COLUMN_NAME DATA_TYPE HID
--------------- ------------------------------ --------------- ---
EMPLOYEES EMP_FULL_NAME VARCHAR2 NO
EMPLOYEES LOWEREMAIL VARCHAR2 NO

SQL>


Now, let's look at some performance metrics between this table and a table with no virtual columns.

SQL> create table employees_nonv
2 (empno number,
3 firstname varchar2(100),
4 lastname varchar2(100),
5 email varchar2(100)
6 );

Table created.

SQL> create table employees_v
2 (empno number,
3 firstname varchar2(100),
4 lastname varchar2(100),
5 email varchar2(100),
6 loweremail as (lower(email)),
7 emp_full_name as (firstname || ' ' || lastname)
8 );

Table created.

SQL>

Let's insert 100000 records in a table with no virtual columns.

set timing on

declare
i number:=0;
begin
for i in 1..100000
loop
insert into employees_nonv values (i,'Fredreck', 'Herbert','Fredreck.Herbert@myemailaddress.com');
end loop;
commit;
end;
/

PL/SQL procedure successfully completed.

Elapsed: 00:00:29.74
SQL>


Now let's insert 100000 records in a table with virtual columns.

declare
i number:=0;
begin
for i in 1..100000
loop
insert into employees_v (empno, firstname, lastname, email) values (i,'Fredreck', 'Herbert','Fredreck.Herbert@myemailaddress.com');
end loop;
commit;
end;
/

PL/SQL procedure successfully completed.

Elapsed: 00:00:29.79
SQL>

Pretty much the same results. Now, let's try to select from them.


Let's do a test by fetching a column from the table without virtual columns.

declare
i number:=0;
j number;
begin
for i in 1..100000
loop
select empno into j
from employees_nonv
where empno = i;
end loop;
end;
/
PL/SQL procedure successfully completed.

Let's do the test by fetching a single column from the table with virtual columns.

Elapsed: 00:00:22.26
SQL> SQL>

declare
i number:=0;
j number;
begin
for i in 1..100000
loop
select empno into j
from employees_v
where empno = i;
end loop;
end;
/

PL/SQL procedure successfully completed.

Elapsed: 00:00:22.47

Another one...

declare
i number:=0;
email varchar2(100);
begin
for i in 1..100000
loop
select email into email
from employees_nonv
where empno = i;
end loop;
end;
/
PL/SQL procedure successfully completed.

Elapsed: 00:00:24.06


declare
i number:=0;
loweremail varchar2(100);
begin
for i in 1..100000
loop
select loweremail into loweremail
from employees_v
where empno = i;
end loop;
end;
/
PL/SQL procedure successfully completed.

Elapsed: 00:00:24.36


Let's do another test by fetching multiple columns from the tables without virtual columns.

declare
i number:=0;
firstname varchar2(100);
lastname varchar2(100);
full_name varchar2(100);
email varchar2(100);
begin
for i in 1..100000
loop
select firstname, lastname, firstname || ' ' || lastname full_name, email into firstname, lastname, full_name, email
from employees_nonv
where empno = i;
end loop;
end;
/
PL/SQL procedure successfully completed.

Elapsed: 00:00:24.73

Now, let's try to select multiple columns from the tables with virtual columns.

declare
i number:=0;
firstname varchar2(100);
lastname varchar2(100);
full_name varchar2(100);
email varchar2(100);
begin
for i in 1..100000
loop
select firstname, lastname, full_name, loweremail into firstname, lastname, full_name, email
from employees_v
where empno = i;
end loop;
end;
/

PL/SQL procedure successfully completed.

Elapsed: 00:00:24.79

So, it is clear there is no apparent degradation for using Virtual columns. If there is an index created on a virtual which is a Function based index on the underlying column, may have some performance issues. That is true for any Function based index created. But generally if it's a pseudo column which is used by a PK or other indexed column, there shouldn't be any issues.

Tuesday, January 12, 2010

Is ROW MOVEMENT expensive ?

Create NON Partition table

SQL> create table non_part (x number, y number);

Table created.

SQL>

Create Primary Key on this table.

SQL> alter table non_part add primary key(x);

Table altered.

SQL>

Let's create Partitioned table


SQL> CREATE TABLE part
2 ( x NUMBER,
3 y number)
4 PARTITION BY RANGE (y)
5 ( PARTITION part_y_1 VALUES LESS THAN (1),
6 PARTITION part_y_2 VALUES LESS THAN (2),
7 PARTITION part_y_3 VALUES LESS THAN (MAXVALUE)
8 );



Table created.

Elapsed: 00:00:00.09
SQL>

alter table part add primary key (x)
SQL> /

Table altered.

Elapsed: 00:00:00.89

Let's populate the tables now....


declare
i number:=0;
begin
for i in 1..100000
loop
insert into non_part values (i,1);
end loop;
commit;
end;
/

PL/SQL procedure successfully completed.

SQL>

declare
i number:=0;
begin
for i in 1..100000
loop
insert into part values (i,1);
end loop;
commit;
end;
/

PL/SQL procedure successfully completed.

SQL>



SQL> select count(1) from non_part;

COUNT(1)
----------
100000

SQL>


SQL> select count(1) from part partition(part_y_1);

COUNT(1)
----------
0

1* select count(1) from part partition(part_y_2)
SQL> /

COUNT(1)
----------
100000

1* select count(1) from part partition(part_y_3)
SQL> /

COUNT(1)
----------
0


SQL> update non_part
2 set y = 3;

100000 rows updated.

Elapsed: 00:01:15.12

So, it took 1 minute and 15 seconds to update 100000 rows.


SQL> update part
2 set y=3;

100000 rows updated.

Elapsed: 00:04:50.19


SQL> select count(1) from part partition(part_y_3);

COUNT(1)
----------
100000



So, the conclusion is that it took almost 4 times as much time to update 100k rows in a partition table with row movement and we made sure all 100k rows were moved.

So each row that took .69 millisecond would take 2.7 millisecond. Generally, in an OLTP system this isn't good but your application should be purely subjective.

Thursday, January 7, 2010

ROWID datatype

Now there is a ROWID datatype and you don't have to put that in an char or varchar2 datatype.


1 declare
2 rowids rowid;
3 begin
4 select rowid into rowids
5 from dual;
6 dbms_output.put_line('rowid is '|| rowids);
7* end;
8 /
rowid is AAAABzAABAAAAEmAAA

PL/SQL procedure successfully completed.

SQL>