How to execute DDLs conditionally in oracle? - sql

I have a script that creates a lot of tables, indexes, triggers etc. And I want to run all those DDLs conditionally. i tried to wrap the script with 'if then' but it didn't work
IF exists (select 1 from xxx where yyy) THEN
create table...
create table...
CREATE UNIQUE INDEX ...
CREATE TRIGGER ...
END IF;
how can i achieve that?

One of the ways:
begin
for cur in (select 1 from xxx where yyy and rownum <= 1) loop
execute immediate 'create table...';
execute immediate 'create table...';
execute immediate 'create unique index...';
end loop;
end;
/
P.S. One more way is to generate exception and proceed in SQL*Plus.
file example.sql:
SET ECHO OFF
SET VERIFY OFF
WHENEVER SQLERROR EXIT;
VAR x NUMBER
EXEC :x := &1
BEGIN
FOR cur IN (SELECT 1 FROM dual WHERE 1=:x) LOOP
RETURN;
END LOOP;
RAISE NO_DATA_FOUND;
END;
/
PROMPT Here we are
Result:
SQL> #example
Enter value for 1 1: 1
PL/SQL procedure completed.
PL/SQL procedure completed.
Here we are
SQL> #example
Enter value for 1: 2
PL/SQL procedure completed.
BEGIN
*
error in line 1:
ORA-01403: no data found
ORA-06512: in line 5
When I use value 2 the block raises exception and the script exists SQL*Plus.
P.S. One more example in response - hope this cleas questions below. I create table only when it does not exist. Table containts 1024 partitions and ' characters in DEFAULT statement. Text size > 32K.
SQL> set serveroutput on
SQL> DECLARE
2 sql_code clob;
3 delim varchar2(1) := '';
4 amount int;
5 sql_text varchar2(32767);
6 BEGIN
7
8 dbms_lob.createtemporary(sql_code,cache => true);
9 sql_text := q'[CREATE TABLE TEST_TAB (X INT PRIMARY KEY, Y VARCHAR2(10) DEFAULT 'DEF', Z INTEGER) PARTITION BY RANGE(Z) ( ]';
10 amount := length(sql_text);
11 dbms_lob.writeappend(sql_code,amount,sql_text);
12
13 for i in 1..1024 loop
14 sql_text := delim||'PARTITION P_'||i||' VALUES LESS THAN ('||i||')';
15 amount := length(sql_text);
16 dbms_lob.writeappend(sql_code,amount,sql_text);
17 delim := ',';
18 end loop;
19
20 dbms_lob.writeappend(sql_code,1,')');
21
22 FOR cur IN (
23 SELECT * FROM dual WHERE NOT EXISTS (
24 SELECT * FROM user_tables WHERE table_name = 'TEST_TAB')
25 ) LOOP
26 EXECUTE IMMEDIATE sql_code;
27 END LOOP;
28
29 dbms_output.put_line(dbms_lob.getlength(lob_loc => sql_code));
30
31 END;
32 /
39877
PL/SQL procedure completed.
SQL> desc test_tab
Имя Пусто? Тип
----------------------------------------- -------- ----------------------------
X NOT NULL NUMBER(38)
Y VARCHAR2(10)
Z NUMBER(38)
SQL> select count(*) from user_tab_partitions where table_name = 'TEST_TAB';
COUNT(*)
----------
1024

Related

How to dynamically construct table name

I would like to construct a query where a table name is based off of another table's column mod 12. For example:
SELECT *
FROM table_b_XX
where XX here is determined by table_a.column_a % 12.
Presuming you have such a tables:
SQL> create table table_a as
2 select 1212 as column_a from dual;
Table created.
As the following result returns 0, we need table_b_00 so I'll create it:
SQL> select mod(1212, 12) from dual;
MOD(1212,12)
------------
0
SQL> create table table_b_00 as select 'table 00' name from dual;
Table created.
SQL> create table table_b_01 as select 'table 01' name from dual;
Table created.
Now, create a function which returns ref cursor; it selects rows from a table whose name is designed by the help of the table_a contents:
SQL> create or replace function f_test return sys_refcursor
2 is
3 l_str varchar2(200);
4 rc sys_refcursor;
5 begin
6 select 'select * from table_b_' || lpad(mod(a.column_a, 12), 2, '0')
7 into l_str
8 from table_a a;
9
10 open rc for l_str;
11 return rc;
12 end f_test;
13 /
Function created.
Let's try it:
SQL> select f_test from dual;
F_TEST
--------------------
CURSOR STATEMENT : 1
CURSOR STATEMENT : 1
NAME
--------
table 00
Right; that's contents of table_b_00.
Consider the following meta code:
DECLARE
n VARCHAR2(32767);
r VARCHAR2(32767);
BEGIN
SELECT column_a INTO name FROM table_a;
EXECUTE IMMEDIATE 'SELECT r FROM table_b_'||n INTO r;
END;
/

need to create a temporary table inside my procedure which has a cursor in it

Procedure code below :: TYPE line has errors while compilation.let me the know the correct usage of table creation here.
CREATE OR REPLACE PROCEDURE bulk_order_export
(
startdate IN varchar2,
enddate IN varchar2,
batchsize IN varchar2
)
IS
bulkorderdata_cursor sys_refcursor ;
p_query_string VARCHAR2(100);
TYPE FBL_BACKUP_ORDER IS TABLE OF DPS_USER%TYPE;
temp_order_id FBL_BACKUP_ORDER;
BEGIN
p_query_string := 'SELECT ID FROM abc_order WHERE REGISTRATION_DATE BETWEEN :startDate AND :endDate';
OPEN bulkorderdata_cursor FOR p_query_string USING startdate, enddate;
LOOP
FETCH bulkorderdata_cursor BULK COLLECT into temp_order_id LIMIT batchsize;
FORALL i IN 1..temp_order_id.count
INSERT INTO FBL_BACKUP_ORDER VALUES(temp_order_id(i));
--COMMIT;
DBMS_OUTPUT.PUT_LINE('Commit '||temp_order_id.count||' inserted rows');
Total := Total+temp_order_id.count;
EXIT WHEN bulkorderdata_cursor%NOTFOUND;
END LOOP;
CLOSE bulkorderdata_cursor;
END;
With regard to DPS_USER%TYPE, I assume that is a table? If so, then the syntax is: DPS_USER%ROWTYPE.
Then you need to make sure your variables are defined, batchsize is numeric, and then you have sorted out some syntax, and you'll be good to go
SQL> create table DPS_USER ( id int );
Table created.
SQL> create table abc_order ( id int );
Table created.
SQL> create table FBL_BACKUP_ORDER ( id int );
Table created.
SQL>
SQL>
SQL> CREATE OR REPLACE PROCEDURE bulk_order_export
2 (
3 startdate IN varchar2,
4 enddate IN varchar2,
5 batchsize IN int
6 )
7 IS
8 bulkorderdata_cursor sys_refcursor ;
9 p_query_string VARCHAR2(100);
10 TYPE FBL_BACKUP_ORDER IS TABLE OF DPS_USER%ROWTYPE;
11 temp_order_id FBL_BACKUP_ORDER;
12 total int;
13 BEGIN
14
15 p_query_string := 'SELECT ID FROM abc_order WHERE REGISTRATION_DATE BETWEEN :startDate AND :endDate';
16 OPEN bulkorderdata_cursor FOR p_query_string USING startdate, enddate;
17 LOOP
18 FETCH bulkorderdata_cursor BULK COLLECT into temp_order_id LIMIT batchsize;
19 FORALL i IN 1..temp_order_id.count
20 INSERT INTO FBL_BACKUP_ORDER VALUES temp_order_id(i);
21 --COMMIT;
22 DBMS_OUTPUT.PUT_LINE('Commit '||temp_order_id.count||' inserted rows');
23 Total := Total+temp_order_id.count;
24 EXIT WHEN bulkorderdata_cursor%NOTFOUND;
25 END LOOP;
26 CLOSE bulkorderdata_cursor;
27 END;
28 /
Procedure created.

Display result from loop tables (oracle, pl/sql)

I'm try to loop some tables and run select as below:
set serveroutput on
declare
type tables_names is table of varchar2(30);
type selectTable is table of varchar2(30);
tName tables_names;
sTableName selectTable;
begin;
tName := tables_names('PERIOD','SETTING','RAP','LOG');
sTableName := selectTable('m_table1','m_table2','m_table3','m_table4','m_table5');
for i in 1..tName.count loop
for j in 1..sTableName.count loop
select col10, count(*) from user.sTableName(j)
where table_name = tName(i) group by col10;
end loop;
end loop;
end;
I got error:PL/SQL: ORA-00933.
Can you please tell me how can I correctly run PL/SQL procedure to have displayed result from my select?
UPDATE: looking result
Normally, to get this I need to run below select's:
select column_name,
count(*) as countColumn
from user.m_table1 where table_name = 'PERIOD' group by column_name;
select column_name,
count(*) as countColumn
from user.m_table2 where table_name = 'PERIOD' group by column_name;
Oracle complains (ORA-00933) that command isn't properly ended. That's probably because of a semi-colon behind the BEGIN; also, you lack the INTO clause.
I'm not sure what PERIOD, SETTING, ... are opposed to m_table1, m_table2, ... Which ones of those are table names? What are those other values, then?
Anyway: here's an example which shows how to do something like that - counting rows from tables. Try to adjust it to your situation, or - possibly - add some more info so that we'd know what you are doing.
SQL> set serveroutput on
SQL> declare
2 tname sys.odcivarchar2list := sys.odcivarchar2list();
3 l_cnt number;
4 l_str varchar2(200);
5 begin
6 tname := sys.odcivarchar2list('EMP', 'DEPT');
7
8 for i in 1 .. tname.count loop
9 l_str := 'select count(*) from ' || tname(i);
10 execute immediate l_str into l_cnt;
11 dbms_output.put_line(tname(i) ||': '|| l_cnt);
12 end loop;
13 end;
14 /
EMP: 14
DEPT: 4
PL/SQL procedure successfully completed.
SQL>
[EDIT: added GROUP BY option]
Here you go; as EMP and DEPT share the DEPTNO column, I chose it for a GROUP BY column.
SQL> declare
2 tname sys.odcivarchar2list := sys.odcivarchar2list();
3 type t_job is record (deptno varchar2(20), cnt number);
4 type t_tjob is table of t_job;
5 l_tjob t_tjob := t_tjob();
6 l_str varchar2(200);
7 begin
8 tname := sys.odcivarchar2list('EMP', 'DEPT');
9
10 for i in 1 .. tname.count loop
11 l_str := 'select deptno, count(*) from ' || tname(i) ||' group by deptno';
12 execute immediate l_str bulk collect into l_tjob;
13
14 for j in l_tjob.first .. l_tjob.last loop
15 dbms_output.put_Line('Table ' || tname(i) || ': Deptno ' || l_tjob(j).deptno||
16 ': number of rows = '|| l_tjob(j).cnt);
17 end loop;
18
19 end loop;
20 end;
21 /
Table EMP: Deptno 30: number of rows = 6
Table EMP: Deptno 20: number of rows = 5
Table EMP: Deptno 10: number of rows = 3
Table DEPT: Deptno 10: number of rows = 1
Table DEPT: Deptno 20: number of rows = 1
Table DEPT: Deptno 30: number of rows = 1
Table DEPT: Deptno 40: number of rows = 1
PL/SQL procedure successfully completed.
SQL>
You are probably looking for something like this. Note that you can't run a simple select statement inside a PL/SQL without INTO clause. use a refcursor and DBMS_SQL.RETURN_RESULT
DECLARE
TYPE tables_names IS TABLE OF VARCHAR2 (30);
TYPE selectTable IS TABLE OF VARCHAR2 (30);
tName tables_names;
sTableName selectTable;
rc SYS_REFCURSOR;
BEGIN
tName :=
tables_names ('PERIOD',
'SETTING',
'RAP',
'LOG');
sTableName :=
selectTable ('m_table1',
'm_table2',
'm_table3',
'm_table4',
'm_table5');
FOR i IN 1 .. tName.COUNT
LOOP
FOR j IN 1 .. sTableName.COUNT
LOOP
OPEN rc FOR
'select col10, count(*) from '||USER||'.'
|| sTableName (j)
|| ' where table_name = '''
|| tName (i)
|| ''' group by col10';
DBMS_SQL.RETURN_RESULT (rc);
END LOOP;
END LOOP;
END;
/

Dynamic sql/query: apostrophe in SELECT

So I have the problem, that there is a variable, which could contains different column names
and then in the SELECT I want to compare the column with a specific word.
But then it seems like the apostrophe make problems:
query := 'SELECT value FROM table WHERE ' || variable || ' like ''word''';
EXECUTE IMMEDIATE query INTO rec;
EXCEPTION WHEN OTHERS THEN htp.p(dbms_utility.format_error_stack);
SQL> set serveroutput on;
SQL> DECLARE
2 VAR VARCHAR2(20);
3 REC NUMBER;
4 query VARCHAR2(1000);
5 BEGIN
6 var := 'TABLE_NAME';
7 QUERY := 'SELECT count(*) FROM USER_TABLES WHERE ' || VAR || ' like ''%EMP%''';
8 dbms_output.put_line(query);
9 EXECUTE IMMEDIATE QUERY INTO REC;
10 dbms_output.put_line(rec);
11 END;
12 /
SELECT count(*) FROM USER_TABLES WHERE TABLE_NAME like '%EMP%'
1
PL/SQL procedure successfully completed.
With REC as collection type :
SQL> DECLARE
2 var VARCHAR2(20);
3 TYPE rec_typ
4 IS TABLE OF user_tables%ROWTYPE;
5 rec REC_TYP;
6 query VARCHAR2(1000);
7 BEGIN
8 var := 'TABLE_NAME';
9
10 query := 'SELECT * FROM USER_TABLES WHERE '
11 || var
12 || ' like ''%EMP%''';
13
14 dbms_output.Put_line(query);
15
16 EXECUTE IMMEDIATE query bulk collect INTO rec;
17
18 FOR i IN 1..rec.count LOOP
19 dbms_output.Put_line(Rec(i).table_name);
20 END LOOP;
21 END;
22 /
SELECT * FROM USER_TABLES WHERE TABLE_NAME like '%EMP%'
EMP
PL/SQL procedure successfully completed.
If you're searching for values in column that are equal to word then change like to =.
If you're searching for values in column that contains word then change like ''word'' to like ''%word%'' (don't forget about the third apostrophe that's closing dynamic query).

oracle choose which columns to show

i' trying to create a SELECT in ORACLE .
i'm selecting from a table 3 columns
and i want to do a test (result of an other select)
if it's true show all columns
if false only show two.
create table t1(a int ,b int , c int) ;
select a , case when (1=1) then (b ,c)
else (b) end;
from t1 ;
It's not quite clear what you're trying to do.
If you're just interested in the result of this query, you can't do this. You can't have a query that returns an unknown number of columns. You could have three column and one be null unless your condition is met, like:
SELECT a, CASE WHEN ( condition ) THEN b ELSE NULL END AS b, c
FROM t1
If your goal is to actually create a table (but I would suggest strongly against doing table creation like this), you can use EXECUTE IMMEDIATE string, where string is a DDL command:
DECLARE
ddl VARCHAR2(4000);
BEGIN
IF (condition) THEN
ddl := 'CREATE TABLE t1 (a NUMBER, b NUMBER, c NUMBER )';
ELSE
ddl := 'CREATE TABLE t1 (a NUMBER, b NUMBER )';
END IF;
EXECUTE IMMEDIATE ddl;
END;
plsql with execute immediate is good decigin in your case. But if you want only data you may try like this, may be it's help you:
SELECT a,
CASE
WHEN (condition) THEN
b
ELSE
nvl(b, '') || ';' || nvl(c, '')
END AS NEW_COL
FROM t1
i.e. using pl/sql to open a cursor that selects columns dependant on a given input.
declare
v_select_all_cols boolean := true; --set as applicable.
v_rc sys_refcursor;
begin
if (v_select_all_cols)
then
open v_rc for select a,b,c from t1;
else
open v_rc for select a,b from t1;
end if;
-- now you can return the resultset v_rc to the caller
end;
/
e.g a quick test with sqlplus (ill use a var to print the cursor instead of a pl/sql variable)
SQL> var rc refcursor;
SQL> declare
2 v_select_all_cols boolean := true; --set as applicable.
3 begin
4
5 if (v_select_all_cols)
6 then
7 open :rc for select a,b,c from t1;
8 else
9 open :rc for select a,b from t1;
10 end if;
11 end;
12 /
PL/SQL procedure successfully completed.
SQL> print rc
A B C
---------- ---------- ----------
1 2 3
SQL> declare
2 v_select_all_cols boolean := false;
3 begin
4
5 if (v_select_all_cols)
6 then
7 open :rc for select a,b,c from t1;
8 else
9 open :rc for select a,b from t1;
10 end if;
11 end;
12 /
PL/SQL procedure successfully completed.
SQL> print rc
A B
---------- ----------
1 2