Showing posts with label PL/SQL. Show all posts
Showing posts with label PL/SQL. Show all posts

Friday, June 6, 2008

PL\SQL : Copy tables between databases

As you noticed, my posts run around my daily problems, and this isn't an exception. Past week I needed to copy large amount of data between production and development database to perform some tests. After some research I've found SQL*Plus COPY command, that fits like a glove in my needs.
So here's the syntax:


COPY FROM database TO database action
destination_table (column_name, column_name...) USING query

where action stands for:

  • create - if destination table doesn't exist yet;
  • replace - if destination table exists and we wish to drop and create it again;
  • append - Inserts data if table exist, otherwise it will be created;
  • insert - Insert data into an existing table.

An example could be :

SQL>copy from user/pass@DB1 
to user2/pass2@DB2
append new_emp
using select * from emp;
Enjoy!

Monday, March 3, 2008

ORA-01445: cannot select ROWID from a join view wihtout a key-preserved table.

Today, I was building an huge query, after adding another branche, the query returned me this strange error :

ORA-01445: cannot select ROWID from a join view wihtout a key-preserved table.
My query was something like this :
select * from tableA a join tableB b on (a.column = b.column)
No row_ids...

After struggling with it for a while, I search metalink, and after all it was Oracle thats causing my problems.... Oracle(9) has a limit of 1050 columns in any query that uses ANSI joins. So I've changed my query to :
select * from tableA a join (select column from tableB) b on (a.column = b.column)
This way I've reduced my query colums and my problem was solved.

Thursday, February 7, 2008

Oracle Sample Code Incubator

New Oracle wiki, you can try it here. Let's share our common, or not so common, daily routines.

Monday, December 31, 2007

PL/SQL: Returning multiple rows into a comma separated string

I've spent my last days strugling to optimize a query, during one of my multiple desperate tries, I came across a crazy idea of dinamically build my query. This try have no results, but I read this example of data retrieval, and I didn't resist to write about it. So here's the code :
with data
as
(
select myvalues, row_number() over (order by myvalues) rn, count(*) over () cnt
from
(
select email_addr myvalues from customers where zip = 72204
)
)
select ltrim(sys_connect_by_path(myvalues, ','),',') catvalues
from data
where rn = cnt
start with rn = 1
connect by prior rn = rn-1;


This sample returns a comma separated string with all email addresses whith zip code 72204.
But the most important lesson from this sample was learning about hierarchically connect data in one single query.

Thursday, December 27, 2007

PL/SQL : Add_Month function

Be aware of this function, it may have unexpected results :

SELECT add_months(TO_DATE('27-JAN-2007'), 1) FROM dual;
SELECT add_months(TO_DATE('28-JAN-2007'), 1) FROM dual;
SELECT add_months(TO_DATE('29-JAN-2007'), 1) FROM dual;
SELECT add_months(TO_DATE('30-JAN-2007'), 1) FROM dual;
SELECT add_months(TO_DATE('31-JAN-2007'), 1) FROM dual;
SELECT add_months(TO_DATE('01-FEV-2007'), 1) FROM dual;
This queries returns :
27-02-2007
28-02-2007
28-02-2007
28-02-2007
28-02-2007
01-03-2007
If you need to add a specific number of days, consider to use :
SELECT TO_DATE('01-FEV-2007') + 30 FROM dual;

Monday, December 10, 2007

PL/SQL : How to query for & in SQL*Plus?

Today, I tried to execute a simple query like :

     select '&ab' from dual;

as expected I was prompted to insert value for ab...

So, to query special character & you have several options :
  • change DEFINE settings to allow &;

  • set define off;
    select '&ab' from dual;


  • define a escape character;

  • set escape '\';
    select '\&ab' from dual;


  • don't scan for substitution variables;

  • set scan off;
    select '\&ab' from dual;


Thursday, November 29, 2007

WHy MINUS does NOT EXISTs in our queries?

Read this great (and old) article about MINUS vs NOT EXISTS operators.


There really isn’t just one right way to design queries. In some cases, you really are better off using Not Exists. In many cases, however, you should use the set operator MINUS. Once you understand the principles, you can easily choose the best method for your particular case.

Monday, November 26, 2007

PL/SQL : Check script for object owners

In real life development environments, you develope a script logged as UserX, and someone in Support Team will install it in production environment logged as UserZ.
So far, nothing unusual happens. But, if we are talking about oracle scripts, there's something that you have to deal with, Schemas, If you didn't reference all you objects with full name, SchemaX.TableA for instance, when UserZ tries to install it, he will not find TableA in is schema.

In simple scripts, with few lines, it's easy to manually check for errors, but try to do it in a scripy with thousand of lines...

So I've developed this procedure to "parse" my code looking for this kind of errors

FUNCTION CHECK_OWNER_IN_SQL(
p_list varchar2,
p_del varchar2
) return boolean
is
l_idx pls_integer;
l_list varchar2(32767) := p_list;
l_value varchar2(32767);
keyword varchar2(10000);
existsObject integer;
result boolean;
begin
result := false;
loop
l_idx := instr(l_list,p_del);
if l_idx > 0 then
keyword := substr(l_list,1,l_idx-1);
select count(*) into existsObject from user_objects where lower(object_name) = lower(keyword);


if existsObject > 0 then
DBMS_OUTPUT.Put_Line( 'Possible error in ' keyword);
result := true;
end if;

l_list := substr(l_list,l_idx+length(p_del));
else
keyword := l_list;
select count(*) into existsObject from user_objects where lower(object_name) = lower(keyword);

if existsObject > 0 then
DBMS_OUTPUT.Put_Line( 'Possible error in ' keyword );
result := true;
end if;
exit;
end if;
end loop;
return result;
end;




--Possible values for p_type are :
-- DATABASE LINK,FUNCTION,INDEX,PACKAGE,PACKAGE BODY,PROCEDURE,SEQUENCE,SYNONYM,TABLE,TRIGGER,TYPE,VIEW


PROCEDURE CHECK_OWNER(
p_owner IN varchar2,
p_type IN varchar2,
p_name IN varchar2,
p_sql IN varchar2 := NULL
)
IS
cursor lines
is
Select text from all_source where lower(owner) = lower(p_owner) and lower(type) = lower(p_type) and lower(name) = lower(p_name) order by line;
sqltext varchar2(32000);
result boolean;
linha integer;
begin
if p_sql is not null then
result := siebel.check_owner_in_sql(p_sql , ' ');
else
linha := 1;
for line in lines loop
sqltext := TRIM(line.text);
IF substr(sqltext,1,2) <> '--' then
if siebel.check_owner_in_sql(sqltext , ' ') then
DBMS_OUTPUT.Put_Line( 'Linha ' linha ': ' sqltext);
end if;
end if;
linha := linha + 1;
end loop;
if linha = 1 then
DBMS_OUTPUT.Put_Line( 'Não foi encontrado o objecto a validar');
end if;
end if;
end;


After create this two procedures, you have only to execute the last one, and watch for possible warnings in your output window.

Tuesday, November 20, 2007

PL/SQL : Scripting for table drop

I came from MS SQLServer and I miss a lot of nice functionalities, like for instance, If exists.
Why am I talking about this? Simple, I want to run a script that "only" drops a few tables that I'don't no if they already exists.

Easy to say, hard to find. So here's a script for doing that, without returning any errors.

set echo off;
set heading off;
spool run.sql;
select 'drop table 'table_name';' from dba_tables where UPPER(table_name) in ('TABLEA', 'TABLEB', 'TABLEC');
spool off;
@run.sql;

Enjoy.

Thursday, November 15, 2007

PL/SQL : How to update using select

This "problem" came across me when I needed to update an entire temporary table column. One option is to loop all table rows, boring and time consumer task. After some background searchs I've adopted this method:

UPDATE TABLEA SET
(TABLEA.COLUMNA, TABLEA.COLUMNB) =
(SELECT TABLEB.COLUMNA, TABLEB.COLUMNB FROM TABLEB WHERE TABLEB.ID = TABLEA.ID)

I've basically do one inner select and join the two tables in the inner select's WHERE clause.

Tuesday, November 13, 2007

PL/SQL : Show locked objects

Today, after trying unsuccessfully to drop my temporary tables, and subsequent fails due to object locks, I tried to find who's locking my table.
Not an easy task, so I dig a little and found this amazing script that solved my problem. If you have the same problem, read here how to list all locked objects.

Wednesday, November 7, 2007

PL/SQL Tip #1

Query v$parameter view for BD parameters checking.
In this example I'm looking for the selected optimizer mode.

select value from v$parameter where name = 'optimizer_mode';

Enjoy.

Oracle : Reducing join execution time

After executing a join over two tables, whith 4 million rows each and indexes in join columns, I realized that 4 minutes it's too much time waiting for results. So I decided to watch for the execution plan :

select columns from tableA join TableB on tableA.id = tableB.id

Execution Plan----------------------------------------------------------
0 SELECT STATEMENT Optimizer=RULE
1 0 FILTER
2 1 SORT (GROUP BY)
3 2 TABLE ACCESS (BY INDEX ROWID) OF 'TABLEA'
4 3 NESTED LOOPS
5 4 TABLE ACCESS (FULL) OF 'TABLEB'
6 4 INDEX (RANGE SCAN) OF 'TABLEA_U1' (UNIQUE)

All seems to be correct, after all I'm using Rule Base Optimizer. In order to reduce execution time I force the execution of an hash join instead of a Nested Loops.
Execution time droped to about 2 minutes and execution plan looks like this :

Execution Plan----------------------------------------------------------
0 SELECT STATEMENT Optimizer=RULE (Cost=624 Card=40000 Bytes=2 800000)
1 0 FILTER

2 1 SORT (GROUP BY) (Cost=624 Card=40000 Bytes=2800000)
3 2 HASH JOIN (Cost=172 Card=40000 Bytes=2800000)
4 3 TABLE ACCESS (FULL) OF 'TABLEA' (Cost=35 Card=2000 Bytes=62000)
5 3 TABLE ACCESS (FULL) OF 'TABLEB' (Cost=136 Card=2000 Bytes=78000)

And all I've to do is change my query to :

select /*+ USE_HASH (tableA tableB ) */ columns from tableA join TableB on tableA.id = tableB.id

Why is this happening? why RBO this choose this plan? The answer is simple, RBO doesn't consider hash joins has a valid execution path, so you have to force it.
This specific join is used when a large amount of data needs to be joined or when a large fraction of the the table needs to be joined, however this is a very memory expensive operation and need to be carefully analised.

Last but not least, if you want that hash joins were a valid option just start using Cost Base Optimizer. If you don't decide yet what join you should use, just read this article.