Store sql query on object - sql

Is there a way to store a query in an object so that you can use it in a cursor or as a subquery of a bigger query? all this without using execute immediate?
Lets supose you want this:
set serveroutput on;
DECLARE
CNT NUMBER;
v1 varchar2(4000);
SQL_QUERY view := SELECT table_name FROM USER_TABLES;
CURSOR C1 IS
SQL_QUERY;
BEGIN
OPEN C1;
FETCH C1 INTO V1;
dbms_output.put_line('name of the first table: '||v1);
CLOSE C1;
SELECT COUNT(*) INTO CNT FROM SQL_QUERY;
dbms_output.put_line('Count: '|| cnt);
end;
Is it possible?

"store a query in an object"
which is the definition of a view.
create view foo as select table_name from user_tables;
then
DECLARE
CNT NUMBER;
v1 varchar2(4000);
CURSOR C1 IS
select * from foo;
BEGIN
OPEN C1;
FETCH C1 INTO V1;
dbms_output.put_line('name of the first table: '||v1);
CLOSE C1;
SELECT COUNT(*) INTO CNT FROM (select null from foo);
dbms_output.put_line('Count: '|| cnt);
end;
isn't that what you want?

Related

Query not giving poper output when using cursor

I have a PL/SQL block where I am trying to get the count of filled and not filled values for each column of a table i.e. "demo_table".
select count(*) into v_count from demo_table
where owner='OWNER_1' and(rec.COLUMN_NAME is null OR
REGEXP_LIKE(rec.COLUMN_NAME,'^-$') OR REGEXP_LIKE (rec.COLUMN_NAME,'^-$'));
I am displaying the v_count through dbms_output. But I am not able to get the count.
This is what I am doing:
declare
CURSOR C1 IS
SELECT COLUMN_NAME FROM ALL_TAB_COLUMNS
WHERE TABLE_NAME ='demo_table';
v_count number(10);
begin
for rec in c1 loop
dbms_output.put_line(rec.COLUMN_NAME);
select count(*) into v_count from demo_table
where owner='OWNER_1'
and(rec.COLUMN_NAME is null
OR REGEXP_LIKE(rec.COLUMN_NAME,'^-$')
OR REGEXP_LIKE (rec.COLUMN_NAME,'^-$'));
dbms_output.put_line(v_count);
end loop;
dbms_output.put_line(v_count);
end;

Dynamic SQL - ORACLE

I have the following procedure, which does not compile correctly, because it refers to non existing objects (table does not exist)
Here is only a section of the code (i used generic names for tables and columns):
DECLARE
C INTEGER := 0;
BEGIN
SELECT COUNT(1) INTO C FROM USER_TABLES WHERE TABLE_NAME = 'MY_TABLE';
IF C > 0 THEN
DECLARE
CURSOR c_maps IS SELECT COLUM_NAME1, COLUM_NAME2 FROM MY_TABLE WHERE ACTIVE = 1;
BEGIN
FOR prec IN c_maps LOOP
some code...;
END LOOP;
EXECUTE IMMEDIATE 'some code..';
END;
END IF;
END;
/
I don't know how to write this statement dynamically, since the table "MY_TABLE" does not exist:
CURSOR c_maps IS SELECT COLUM_NAME1, COLUM_NAME2 FROM MY_TABLE WHERE ACTIVE =1;
I also tried to write it like:
CURSOR c_maps IS SELECT COLUM_NAME1, COLUM_NAME2 FROM (Select 'MY_TABLE' from dual) WHERE ACTIVE = 1;
However, than it refers to the column "ACTIVE" which also does not exist at compile time...It is possible to write the whole procedure inside "execute immediate" - block? I have tried different variants, however without success
You may need to open the cursor in a different way, so that the non existing table is only referred in dynamic SQL; for example:
declare
c integer := 0;
curs sys_refcursor;
v1 number;
v2 number;
begin
select count(1)
into c
from user_tables
where table_name = 'MY_TABLE';
if c > 0
then
open curs for 'select column_name1, column_name2 from my_table where active = 1';
loop
fetch curs into v1, v2;
exit when curs%NOTFOUND;
dbms_output.put_line(v1 || ' - ' || v2);
end loop;
else
dbms_output.put_line('The table does not exist');
end if;
end;
/

How to declare %rowtype dynamically?

Below is the sample code in which I have stored all the table names in one table (table_config) and trying to insert one record of every table into its temporary table and trying to get the particular rowid for further need.
So I need every table rowtype to make this work, something dynamic. Could you please help me with this?
DECLARE
l_row table_name%ROWTYPE;
l_rowid ROWID;
l_table_name all_tab_partitions.table_name%TYPE;
l_temp_table_name all_tab_partitions.table_name%TYPE;
BEGIN
FOR tab IN
(select table_name from
Table_config)
LOOP
l_table_name:= tab.table_name;
l_temp_table_name:= 'TEMP_'||l_table_name;
SELECT * INTO l_row
FROM l_table_name
WHERE ROWNUM=1;
INSERT INTO l_temp_table_name VALUES l_row
RETURNING ROWID INTO l_rowid;
COMMIT;
END LOOP;
END;
Thank you,
Pradeep
Without coding the complete answer for you.
Why don't you do something like this?
FOR tab IN
(select table_name from
Table_config)
EXECUTE_IMMEDIATE(
'declare
l_row '||table_name||'%ROWTYPE;
begin
INSERT INTO '||l_temp_table_name
SELECT * FROM '||l_table_name||' WHERE ROWNUM=1;
end;');
EXECUTE_IMMEDIATE ('SELECT ROWID FROM '||l_table_name)
INTO l_rowid;
END LOOP;
it assumes target table is empty to begin with with only one record inserted during the process.
You can't do that as already mentioned in the comment by OldProgrammer above.
You'll have to use Dynamic SQL to achieve what you're trying to achieve.
DECLARE
temp_table VARCHAR2(255);
source_table VARCHAR2(255);
sql_stmt VARCHAR2(255);
CURSOR c1 IS
SELECT table_name FROM user_Tables;
BEGIN
FOR c1_Rec IN c1 LOOP
temp_table := 'TEMP_'||c1_rec.table_name;
source_table := c1_rec.table_name;
sql_stmt := 'INSERT INTO '||temp_table||' SELECT * FROM '||source_table||' WHERE rownum = 1';
EXECUTE IMMEDIATE sql_stmt;
END LOOP;
END;
/
Below is solution. What do you need this rowids for? I would be much simpler without it, as you cannot use returning with insert as select
DECLARE
l_rowid ROWID;
l_table_name all_tab_partitions.table_name%TYPE;
l_temp_table_name all_tab_partitions.table_name%TYPE;
v_sql1 varchar2(4000);
v_sql2 varchar2(4000);
BEGIN
FOR tab IN (select table_name from Table_config) LOOP
l_table_name:= tab.table_name;
l_temp_table_name:= 'TEMP_'||l_table_name;
v_sql1 := 'select rowid from ' || l_table_name || ' where rownum =1 for update';
v_sql2 := 'insert into ' || l_temp_table_name || ' select * from ' || l_table_name || ' where rownum = 1';
execute immediate v_sql1 into l_rowid;
execute immediate v_sql2;
commit;
END LOOP;
END;
/
You should investigate EXECUTE IMMEDIATE INTO. I think this would be an excellent way to get the ROWID when combined with some dynamic SQL examples from above. Here's an example:
DECLARE
DYN_SQL VARCHAR(4000) := 'SELECT 1 FROM DUAL';
INTO_VAR NUMBER(1);
BEGIN
EXECUTE IMMEDIATE DYN_SQL INTO INTO_VAR;
DBMS_OUTPUT.PUT_LINE(INTO_VAR);
END;
Thank you guys for your response. Actually I was trying to implement partition exchange on interval partitioned tables. I achieved it by using Dynamic Sql now. Initially I was trying to implement it by using rowid which is ok when I hard coded for one table, but when I thought of configuring it and using it for multiple tables I got stuck at that %ROWTYPE.
In the below code I have hard coded table name in few places which can be modified as dynamic but the problem is how to get the %ROWTYPE for the every table we pass.
DECLARE
l_table_name table_config.table_name%TYPE;
l_query_temp VARCHAR2(1000);
l_part_table_name all_tab_partitions.table_name%TYPE;
l_part_name all_tab_partitions.partition_name%TYPE;
l_temp_table_name all_tab_partitions.table_name%TYPE;
l_row test_archival%ROWTYPE;
l_rowid ROWID;
l_arch_table_name all_tab_partitions.table_name%TYPE;
l_arch_part_name VARCHAR2(30);
l_query_arch VARCHAR2(1000);
l_query_source VARCHAR2(1000);
BEGIN
<<outer_loop>>
FOR tab IN
(SELECT table_name FROM
table_config)
LOOP
l_table_name:= tab.table_name;
<<inner_loop>>
FOR part IN
(SELECT table_name, partition_position, partition_name FROM
(SELECT table_name, partition_position, partition_name,
DENSE_RANK() OVER (PARTITION BY table_name ORDER BY partition_position DESC) AS RANK
FROM all_tab_partitions
WHERE table_name=l_table_name
) WHERE RANK NOT IN(1, 2) ORDER BY partition_position)
LOOP
l_part_table_name:= part.table_name;
l_part_name:= part.partition_name;
l_temp_table_name := 'TEMP_'||l_part_table_name;
l_arch_table_name := 'ARCH_'||l_part_table_name;
l_query_temp := 'ALTER TABLE '
|| l_part_table_name
|| ' EXCHANGE PARTITION '
|| l_part_name
|| ' WITH TABLE '
|| l_temp_table_name
||' INCLUDING INDEXES WITHOUT VALIDATION';
EXECUTE IMMEDIATE l_query_temp;
COMMIT;
SELECT * INTO l_row FROM temp_test_archival WHERE ROWNUM = 1;
INSERT INTO arch_test_archival VALUES l_row RETURNING ROWID INTO l_rowid;
COMMIT;
SELECT subobject_name
INTO l_arch_part_name FROM user_objects
WHERE data_object_id = dbms_rowid.rowid_object(l_rowid);
DELETE from arch_test_archival where rowid=l_rowid;
COMMIT;
l_query_arch := 'ALTER TABLE '
||'ARCH_TEST_ARCHIVAL'
||' EXCHANGE PARTITION '
||l_arch_part_name
||' WITH TABLE '
||'TEMP_TEST_ARCHIVAL'
||' INCLUDING INDEXES WITHOUT VALIDATION';
EXECUTE IMMEDIATE l_query_arch;
END LOOP;
END LOOP;
END;
/

Execute immediate statement

I'm trying to insert data into table using execute immediate statement. But I get an error
FROM keyword not found where expected
Could anyone take a look what's wrong?
declare
c1 SYS_REFCURSOR;
v_tabl_name varchar2(30);
begin
open c1 for
select tablename from table1;
LOOP
FETCH c1 INTO v_tabl_name;
EXIT WHEN c1%NOTFOUND;
execute immediate 'insert tabl2(tabl_name) (select ''tem'' from'||v_tabl_name||')' ;
END LOOP;
close c1;
end;
create table table1(tem varchar2(50), tablename varchar2(50));
create table tabl2(tabl_name varchar2(50));
insert into table1(tem, tablename) values ('table1','table1');
begin
for rc in (select tablename from table1) loop
--dbms_output.put_line('insert into tabl2(tabl_name) (select ''tem'' from '||rc.tablename||')');
execute immediate 'insert into tabl2(tabl_name) (select ''tem'' from '||rc.tablename||')' ;
end LOOP;
end;

Selecting all tables with no records

I need to display all tables that have zero records.
I tried,
select * from user_all_tables where (select count(*) from user_all_tables)=0;
But it doesn't seem to work.
How should I go about redesigning this query?
Thanks.
If all of your tables are analyzed, you can check the column num_rows of table user_tables.
Otherwise, you will need PL/SQL to make this work. This will output all tables of your current user without records (use all_tables if you need tables of other users):
Set Serveroutput On;
Declare
cnt PLS_INTEGER;
Begin
For c In ( Select table_name From user_tables ) Loop
Execute Immediate 'Select Count(*) From "' || c.table_name || '" where rownum=1'
Into cnt;
If( cnt = 0 ) Then
dbms_output.put_line( c.table_name );
End If;
End Loop;
End;
You'd have to resort to PL/SQL and issue a select count(*) for every table. Or you can use dbms_xmlgen to do this for you in a tricky way:
select table_name
from ( select table_name
, extractvalue
( dbms_xmlgen.getxmltype('select count(*) c from '|| table_name)
, '/ROWSET/ROW/C'
) cnt
, rownum to_prevent_predicate_push
from user_tables
)
where cnt = '0'
Regards,
Rob.
Variation of the accepted answer but using a more efficient method.
Declare
cnt PLS_INTEGER;
Begin
For c In ( Select table_name From user_tables ) Loop
begin
Execute Immediate 'Select 1 From dual where exists (select 1 from ' || c.table_name ||')' Into cnt;
exception when no_data_found then
dbms_output.put_line( c.table_name );
end;
End Loop;
End;
select TABLE_NAME
from USER_ALL_TABLES
where NUM_ROWS = 0
This answer is one Fetch-per-table more efficient than Rene's. SELECT INTO requires an Extra Fetch to see if the "TOO_MANY_ROWS" exception should be raised. We can take control of that process with an explicit cursor and NOT doing an unnecessary extra fetch.
Declare
cnt PLS_INTEGER;
s_Cur Varchar2(255);
c_Cur Sys_refcursor;
Begin
For c In ( Select table_name From user_tables ) Loop
s_Cur := 'Select 1 From dual where exists (select 1 from ' || c.table_name ||')';
Open c_Cur For s_cur ;
Fetch c_cur into cnt;
If c_cur%NOTFOUND then
dbms_output.put_line( c.table_name );
end if;
End Loop;
End;