I have the below pl/sql which fetches and prints multiple columns from different tables and prints on console. However for a real time scenario, i need all the data to go into a temporary table. SO that user can do a Select * from the temp table and fetch the data retrieved. Can someone please confirm how can I do that?
SET SERVEROUTPUT ON
Declare
Cursor crsr(companyschema varchar2) is Select Person_Id,Birth_Name from salesdemo.PER_PERSON;
personId number;
bioBirthName varchar2(128);
personalBirthName varchar2(128);
usersSysId varchar2(256);
legalEntity number;
territoryId number;
country varchar2(256);
BEGIN
FOR c_schema IN
(SELECT company_schema
FROM sap.sf_companies c,
sap.sf_feature_map m
WHERE c.company_id = m.company_id
AND m.feature_id = 326
)
LOOP
BEGIN
open crsr(c_schema.company_schema);
if crsr%isopen then
loop
begin
fetch crsr into personId, bioBirthName;
exit when crsr%notfound;
dbms_output.Put_line('Schema '|| c_schema.company_schema);
dbms_output.Put_line('PersonId '|| personId);
dbms_output.Put_line('Bio Birth Name '|| bioBirthName);
execute immediate 'select Birth_Name from '||c_schema.company_schema||'EMP_PERSONAL_INFO_T where person_id = :1' INTO personalBirthName using personId;
dbms_output.Put_line('Personal Birth Name '|| personalBirthName);
execute immediate 'select users_sys_id from '||c_schema.company_schema||'EMP_EMPLOYMENT_INFO where person_id = :1' INTO usersSysId USING personId;
dbms_output.Put_line('UsersSysId '|| usersSysId);
execute immediate 'select company from '||c_schema.company_schema||'EMP_JOB_INFO_T where users_sys_id = :1 ' INTO legalEntity USING usersSysId ;
dbms_output.Put_line('Legal Entity '|| legalEntity);
execute immediate 'select territory_id from '||c_schema.company_schema||'FO_LEGAL_ENTITY_T where internal_code =:1' INTO territoryId USING legalEntity;
dbms_output.Put_line('Territory Id '|| territoryId);
execute immediate 'select territory_name from '||c_schema.company_schema||'Territory where territory_id = :1 ' INTO country USING territoryId;
dbms_output.Put_line('Country '|| country);
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -00942 THEN
CONTINUE;
END IF;
end;
end loop;
end if;
close crsr;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -00942 THEN
CONTINUE;
END IF;
END;
END LOOP;
END;
Inserting console output data into a Temp Table from pl/sql
The output you see on the console is from the DBMS_OUTPUT.PUT_LINE procedure. If you want to insert the same out put into a table to use it for further processing, I would suggest a Global Temporary Table.
The data in a global temporary table is private, such that data inserted by a session can only be accessed by that session. The session-specific rows in a global temporary table can be preserved for the whole session, or just for the current transaction. The ON COMMIT clause indicates that the data should be deleted or preserved at the end of the transaction.
ON COMMIT DELETE ROWS
ON COMMIT PRESERVE ROWS
In your case, create the GTT once, and then in your PL/SQL code, insert the rows into the GTT.
For example,
INSERT INTO company_gtt
VALUES
(c_schema.company_schema, personId, bioBirthName, personalBirthName ....)
See more examples here http://oracle-base.com/articles/misc/temporary-tables.php
Related
I have created a package and defined a procedure to delete specific rows retrieved by the cursor.
Before the rows were deleted from the table, I want to take a backup of those records every time the package is compiled and I need the backup table to be created as tablename concatenated with sysdate.
ex: if table name is emp, backup table should be created as emp_2020_10_16
Below is the sample code I have created:
PROCEDURE DELETE_REC(
P_retcode NUMBER,
P_errorbuff VARCHAR2,
P_unit_id NUMBER,
P_join_date VARCHAR2
)
IS
CURSOR cur1
IS
SELECT unit_ID,dept_ID,join_DATE
FROM EMP MMT
WHERE MMT.dep_TYPE_ID IN (44,35)
AND MMT.unit_id = P_unit_id
AND MMT.join_date < to_date(P_join_date,'RRRR/MM/DD HH24:MI:SS');
BEGIN
--begin
-- EXECUTE IMMEDIATE 'Create table EMP_' || to_char(sysdate,'yyyy_mm_dd') || ' as select * from EMP MMT WHERE MMT.dep_TYPE_ID IN (44,35)
AND MMT.unit_id = P_unit_id
AND MMT.join_date < to_date(P_join_date,'RRRR/MM/DD HH24:MI:SS');
--
-- end;
/*Here i would like to create backup table like above before executing the below delete statement but i am not sure about the correct standards that i should be using for above dynamic statement*/
FOR val IN cur1
LOOP
DELETE
FROM EMP MMT
WHERE MMT.dept_ID= val.dept_id;
How can I backup the table using above dynamic statement in best possible way? I am still learning PL&SQL.
Maybe sth like this would help:
create table employees as select * from hr.employees;
--drop table emp_2020_10_18;
--drop table employees;
----------------
declare
vTabName varchar2(50);
nDept_id number := 10;
nCnt number := 0;
vSQL varchar2(1000);
begin
vTabName := 'emp_'||to_char(sysdate, 'yyyy_mm_dd');
-- check if table exists
begin
execute immediate 'select count(*) from emp_tmp' into nCnt;
exception when others then
nCnt := -1;
end;
-- if not exists create one
if nCnt = -1 then
execute immediate 'create table '|| vTabName||' as select * from employees where 1=2' ;
end if;
execute immediate 'insert into '|| vTabName ||' select * from employees where department_id = :nDept_id' using nDept_id;
delete from employees where department_id = nDept_id;
exception when others then
dbms_output.put_line(sqlerrm);
end;
/
I need to store output of a query(which returns multiple rows) temporarily in Oracle. One of the ways to do this is using temporary table and I am using that currently. But the problem with them is that they stay around and cannot be created and dropped in a procedure willy nilly. Is there another object that I can use to temporarily store the output of a query to be able to use in the stored procedure ?
Here is my stored procedure for reference:
create or replace PROCEDURE procPrintOutput
IS
l_stmt VARCHAR2(512) := '';
create_table_stmt VARCHAR2(512) := 'create GLOBAL TEMPORARY TABLE temp(matViewLogQuery CLOB) ON COMMIT DELETE ROWS';
drop_mat_view_logs_stmt VARCHAR2(100) := 'DROP MATERIALIZED VIEW LOG ON ';
drop_temp_table VARCHAR2(512) := 'DROP TABLE temp';
ref_cursor SYS_REFCURSOR;
BEGIN
EXECUTE IMMEDIATE(create_table_stmt);
DBMS_OUTPUT.PUT_LINE('created temp table');
DECLARE
CURSOR LIST_OF_MVL IS SELECT * FROM USER_MVIEW_LOGS;
CURSOR CREATE_MVL IS SELECT * FROM temp;
BEGIN
FOR TEST IN LIST_OF_MVL
LOOP
BEGIN
DBMS_OUTPUT.PUT_LINE('owner : ' || TEST.OWNER || ' - name : ' || TEST.MASTER);
l_stmt := 'insert into temp SELECT SYS.DBMS_METADATA.get_dependent_ddl (''MATERIALIZED_VIEW_LOG'', '''||TEST.MASTER||''', '''||TEST.LOG_OWNER||''') from dual';
EXECUTE IMMEDIATE (l_stmt);
EXECUTE IMMEDIATE (drop_mat_view_logs_stmt || TEST.MASTER);
DBMS_OUTPUT.PUT_LINE('view dropped ... ');
END;
END LOOP;
FOR CREATE_SCRIPT IN CREATE_MVL
LOOP
BEGIN
DBMS_OUTPUT.PUT_LINE('SCRIPT : ' || CREATE_SCRIPT.matViewLogQuery);
EXECUTE IMMEDIATE(CREATE_SCRIPT.matViewLogQuery);
DBMS_OUTPUT.PUT_LINE('view recreated ... ');
END;
END LOOP;
END;
EXECUTE IMMEDIATE(drop_temp_table);
DBMS_OUTPUT.PUT_LINE('Done! Table dropped');
ROLLBACK;
END;
Thanks
instead of of temp table you can use collection in pl/sql and store all information that you need into the collection by BULK COLLECT
here small example
in my example I collect all DDLs for matviews and name of the views, then in simple cycle we can do any operations...
DECLARE
TYPE tDDL IS record (name varchar2(30), ddl CLOB);
TYPE tblDDL IS TABLE OF tDDL INDEX BY PLS_INTEGER;
l_ddls tblDDL;
BEGIN
SELECT object_name, dbms_metadata.get_ddl('MATERIALIZED_VIEW', object_name)
BULK COLLECT INTO l_ddls
from user_objects
where object_type= 'MATERIALIZED VIEW';
FOR indx IN 1 .. l_ddls.COUNT
LOOP
--do what you need with collected DDLs
DBMS_OUTPUT.PUT_LINE('view dropped ... ');
--your complex execute immediate logic here
dbms_output.put(l_ddls(indx).name);
dbms_output.put(' : ');
dbms_output.put_line(substr(l_ddls(indx).ddl, 100));
DBMS_OUTPUT.PUT_LINE('view recreated ... ');
END LOOP;
END;
I am working on Oracle stored procedures.
My requirement is below
IF variable1 := 'true"
THEN
tableName=abr
ELSE
tableName=mvr
END IF;
FOR i IN (select unique(row1) as sc from tableName t where t.row2 = 'name') LOOP
BEGIN
-- required Logic
END
END LOOP;
But here I am not able to pass the table name in tableName parameter. How to do it?
You'll need to use Execute Immediate - it's designed for operations that aren't known until run time.
For normal operations, Oracle must know the tables and columns at compile time. You can't do SELECT * FROM tableName because it has no idea what tableName is and therefore it can't be compiled correctly.
Instead, you can do EXECUTE IMMEDIATE 'SELECT * FROM ' || tableName;
You can select your results INTO a variable, loop the result set, or BULK COLLECT into a structure and then iterate that.
For a simple select into, you can do this:
EXECUTE IMMEDIATE 'SELECT COL1, COL2 FROM ' || tableName INTO V_COL1, V_COL2
V_COL1 & V_COL2 are just local variables, tableName is a string representing your table name, and COL2 and COL2 are columns in the table you're selecting from. You can use the likes of ALL_TAB_COLUMNS to get the structure of a table dynamically.
Here is an example from Oracle docs:
CREATE OR REPLACE PROCEDURE query_invoice(
month VARCHAR2,
year VARCHAR2) IS
TYPE cur_typ IS REF CURSOR;
c cur_typ;
query_str VARCHAR2(200);
inv_num NUMBER;
inv_cust VARCHAR2(20);
inv_amt NUMBER;
BEGIN
query_str := 'SELECT num, cust, amt FROM inv_' || month ||'_'|| year
|| ' WHERE invnum = :id';
OPEN c FOR query_str USING inv_num;
LOOP
FETCH c INTO inv_num, inv_cust, inv_amt;
EXIT WHEN c%NOTFOUND;
-- process row here
END LOOP;
CLOSE c;
END;
/
http://docs.oracle.com/cd/B12037_01/appdev.101/b10795/adfns_dy.htm
You are going to have to build a for loop for each table then use your logic to determine which loop you will execute.
I need to drop an Oracle table only if it 1) exists AND 2) Is NOT Empty
I wrote this code but if the table does not exist the code does not work:
DECLARE
rec_cnt1 NUMBER :=0;
rec_cnt2 NUMBER :=0;
BEGIN
SELECT COUNT(*) INTO rec_cnt1 FROM ALL_TABLES WHERE TABLE_NAME = 'MyTable';
SELECT num_rows INTO rec_cnt2 FROM USER_TABLES WHERE TABLE_NAME = 'MyTable';
IF rec_cnt1 = 1 THEN
BEGIN
IF rec_cnt2 < 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE MyTable cascade constraints';
END IF;
END;
END IF;
END;
/
What am I doing wrong? Please help.
Many thanks in advance
If you want to drop a table if it exists and empty(as the title of the question states) you could do this as follows:
create or replace procedure DropTableIfEmpty(p_tab_name varchar2)
is
l_tab_not_exists exception;
pragma exception_init(l_tab_not_exists, -942);
l_is_empty number;
l_query varchar2(1000);
l_table_name varchar2(32);
begin
l_table_name := dbms_assert.simple_sql_name(p_tab_name);
l_query := 'select count(*)
from ' || l_table_name ||
' where rownum = 1';
execute immediate l_query
into l_is_empty;
if l_is_empty = 0
then
execute immediate 'drop table ' || l_table_name;
dbms_output.put_line('Table "'|| p_tab_name ||'" has been dropped');
else
dbms_output.put_line('Table "'|| p_tab_name ||'" exists and is not empty');
end if;
exception
when l_tab_not_exists
then dbms_output.put_line('Table "'|| p_tab_name ||'" does not exist');
end;
When you are trying to drop a table, or query a table, which does not exist, Oracle will raise ORA-00942 exception and execution of a pl/sql block halts. We use pragma exception_init statement to associate ORA-00942 exception with our locally defined exception l_tab_not_exists in order to handle it appropriately.
Test case:
SQL> exec droptableifempty('tb_test'); -- tb_test table does not exists
Table "tb_test" does not exist
SQL> create table tb_test(
2 col number
3 );
table TB_TEST created.
SQL> exec droptableifempty('tb_test');
Table "tb_test" has been dropped
As a side note. Before querying num_rows column of [dba][all][user]_tables in order to determine number of rows a table has, you need to gather table statistic by executing dbms_stats.gather_table_stats(user, '<<table_name>>');, otherwise you wont get the actual number of rows.
In PL/SQL it is 'normal' to catch the exception.
If it is the correct exception then continue with the next part of your code.
DECLARE
rec_cnt1 NUMBER :=0;
rec_cnt2 NUMBER :=0;
BEGIN
SELECT COUNT(*) INTO rec_cnt1 FROM ALL_TABLES WHERE TABLE_NAME = 'MyTable';
SELECT num_rows INTO rec_cnt2 FROM USER_TABLES WHERE TABLE_NAME = 'MyTable';
IF rec_cnt1 = 1 THEN
BEGIN
IF rec_cnt2 < 1 THEN
EXECUTE IMMEDIATE 'DROP TABLE MyTable cascade constraints';
END IF;
END;
END IF;
EXCEPTION
DBMS_OUTPUT.PUT_LINE('OH DEAR AN EXCEPTION WAS THROWN DUE TO' || SQLERRM);
DBMS_OUTPUT.PUT_LINE('THE ORACLE CODE IS ' || SQLCODE);
-- if it is the oracle code for no such table, or no data selected
-- everything is fine.
END;
Of course it won't work if the table doesn't exist. Your second select would get a "No data found" exception, and you're not doing any exception handling. At least you should move the second select inside the first IF block. Best to add exception handling.
here is an easy way to solve this problem:
BEGIN
EXECUTE IMMEDIATE 'DROP TABLE [sssss]';
EXCEPTION WHEN OTHERS THEN NULL;
END;
Here is a scenario.
I have around 300 tables in my database and I want to merge another database in my database. Both the databases have same tables but the datatype and no of columns vary.
Now how to convert data from other database to my database ?
eg.
db1: Table T1(col1 int,col2 char,......,col31 int)
db2: Table T1(col1 int,col2 char,......,col31 int,col32 char,col33 char )
Since datatype and no of column vary,I cant use
"insert into db1.tbl
select * from db2.tbl ".
I dont want to create script for each and every table . Help me out !!!
every morning I copy a large amount of tables from the production database into my datawarehouse. This is my "check & fix" procedure I run before each truncate + insert.
procedure check_and_fix_table(p_naam in varchar2)
/**
* check if columns have changed on PROD and create and execute the matching ALTER TABLE statement
*/
is
v_coltype varchar2(100);
v_sql varchar2(200);
function check_column(p_column in varchar2)
return boolean
is
v_dummy number;
begin
select 1 into v_dummy
from user_tab_cols tc
where tc.table_name = upper(p_naam)
and tc.column_name = p_column;
return true;
exception
when no_data_found then return false;
end;
begin
-- loop through all columns that are altered (if nothing altered, then nothing will happen
for i in (select tc.column_name
, tc.data_type
, tc.data_length
, tc.data_precision
from user_tab_cols#DB_LINK_TO_PRODUCTION tc
where tc.table_name = upper(p_naam)
and tc.column_name not like 'SYS_NC%' -- These columns are created by oracle for function based indexes
minus
select tc.column_name
, tc.data_type
, tc.data_length
, tc.data_precision
from user_tab_cols tc
where tc.table_name = upper(p_naam))
loop
-- create column type
if i.data_type in ('CHAR','VARCHAR2') then
v_coltype := i.data_type||'('||i.data_length||')';
elsif i.data_type = 'NUMBER' then
if i.data_precision is not null then
v_coltype := i.data_type||'('||i.data_precision||')';
else
v_coltype := i.data_type;
end if;
else -- DATE, CLOB, BLOB, etc
v_coltype := i.data_type;
end if;
-- check if the column is altered or added
if check_column(i.column_name) then
-- execute the ALTER TABLE to fix the column
v_sql := 'alter table '||p_naam||' modify '||i.column_name||' '||v_coltype;
else
-- add new column
v_sql := 'alter table '||p_naam||' add '||i.column_name||' '||v_coltype;
end if;
execute immediate v_sql;
-- logging change
prc_log(c_procedureid, 1, p_naam||' changed. Fixed by executing: '||v_sql);
end loop;
exception
when others then
prc_log(c_procedureid, 3, 'Error at copy_package.check_and_fix_table - '||substr(sqlerrm, 0, 1900));
end;
Then in my main procedure I use it like this (where p_naam is the tablename passed as parameter to the procedure):
check_and_fix_table(p_naam);
-- full copy of table from PROD
execute immediate 'truncate table ' || p_naam;
execute immediate 'insert /*+append*/ into ' || p_naam || ' select * from ' || p_naam || '#DB_LINK_TO_PRODUCTION';
Hope this helps :)