how to compare values in two columns of two different tables using cursor - sql

declare
v_column varchar2(100);
v_incident_id number;
v_check_incident varchar2(100);
v_check_audit varchar2(100);
query_str varchar2(1000);
cursor c1 is
select COLUMN_NAME
from ALL_TAB_COLUMNS
where upper(TABLE_NAME)=upper('Incidents');
begin
open c1;
loop
fetch c1 into v_column;
exit when c1%notfound;
select incident_id into v_incident_id from incident_audit;
DBMS_OUTPUT.PUT_LINE('incident column: '||v_column);
select v_column
into v_check_incident
from incidents
where incident_id= v_incident_id;
select to_char(v_column)
into v_check_audit
from incident_audit
where incident_id=v_incident_id;
DBMS_OUTPUT.PUT_LINE('incident column: '||v_check_incident);
DBMS_OUTPUT.PUT_LINE('audit column : '||v_check_audit);
if(v_check_incident != v_check_audit)
then
DBMS_OUTPUT.PUT_LINE('inside insert');
insert into PHIIT_AUD(AUDIT_ID ,INCIDENT_ID,OLD_VALUE,NEW_VALUE,UPDATED_BY,UPDATED_ON,COLUMN_NAME)
values(audit_id_seq.nextval,v_incident_id,v_check_audit,v_check_incident,NVL(v('APP_USER'),USER),sysdate,v_column);
end if;
end loop;
DBMS_OUTPUT.PUT_LINE('Total number of rows : '||c1%ROWCOUNT);
close c1;
end;
Problem: Getting Column names from ALL_TAB_COLUMNS in cursor and using it to compare with the other column of other table
output : incident column: INCIDENT_ID
incident column: INCIDENT_ID
audit column : INCIDENT_ID
incident column: BUSINESS_UNIT_ID
incident column: BUSINESS_UNIT_ID
audit column : BUSINESS_UNIT_ID
The cursor is returning value as column_name instead of its value in order to compare.

"The cursor is returning value as column_name "
Of course it is, that's what you're telling it to do. select v_column from incidents means select the value in the variable.
What you need to do is use Dynamic SQL to create a select statement which substitutes the value in the variable for a column name: 'select '||v_column||' from incidents' .
Here is the sort of thing you need to do.
declare
v_column varchar2(100);
v_incident_id number;
v_check_incident varchar2(100);
v_check_audit varchar2(100);
query_str varchar2(1000);
cursor c1 is
select COLUMN_NAME
from ALL_TAB_COLUMNS
where upper(TABLE_NAME)=upper('Incidents');
rc sys_refcursor;
begin
open c1;
loop
fetch c1 into v_column;
exit when c1%notfound;
select incident_id into v_incident_id from incident_audit;
DBMS_OUTPUT.PUT_LINE('incident column: '||v_column);
-- dynamic SQL
open rc for
'select'|| v_column ||
' from incidents where incident_id= :1'
using v_incident_id;
fetch rc into v_check_incident;
close rc;
open rc for
'select'|| v_column ||
' from incident_audit where incident_id= :1'
using v_incident_id;
fetch rc into v_check_audit;
close rc;
DBMS_OUTPUT.PUT_LINE('incident column: '||v_check_incident);
DBMS_OUTPUT.PUT_LINE('audit column : '||v_check_audit);
if(v_check_incident != v_check_audit)
then
DBMS_OUTPUT.PUT_LINE('inside insert');
insert into PHIIT_AUD(AUDIT_ID ,INCIDENT_ID,OLD_VALUE,NEW_VALUE,UPDATED_BY,UPDATED_ON,COLUMN_NAME)
values(audit_id_seq.nextval,v_incident_id,v_check_audit,v_check_incident,NVL(v('APP_USER'),USER),sysdate,v_column);
end if;
end loop;
DBMS_OUTPUT.PUT_LINE('Total number of rows : '||c1%ROWCOUNT);
close c1;
end;
I have kept it close to your original implementation so you can understand the principle. However, I think this is a poor implementation which will be very inefficient if you have lots of audits to verify. But addressing that is a different matter.

Related

how to check if SYS_REFCURSOR is empty

I have a general SYS_REFCURSOR that I want to check if it is empty or not.
The code is like this:
declare
v_cursor SYS_REFCURSOR;
begin
OPEN v_cursor FOR <any select statement>.
check if v_cursor is empty.
end;
Can someone tell me how to check if the weak cursor is empty, please?
I have to mention that the base SELECT statement can be anything from any table.
The column numbers or type it is known only at runtime.
Thank you,
You can't see if a ref cursor contains data without fetching, and consuming at least one row from it.
If you really need to determine this at the point the cursor is opened, without knowing the structure at that point, you could execute a modified query that just counts the rows returned by your real query - possibly limited to a single row if that helps performance - either as a simple execute immediate ... into ... or with a separate open/fetch/close for consistency; something like:
declare
v_cursor SYS_REFCURSOR;
v_query VARCHAR2(4000);
v_count PLS_INTEGER;
begin
v_query := <any select statement>;
-- see if the query finds any data
OPEN v_cursor FOR 'select count(*) from (' || v_query || ')'; -- could limit rows
FETCH v_cursor INTO v_count;
CLOSE v_cursor;
if v_count = 0 then
dbms_output.put_line('No data');
return;
end if;
dbms_output.put_line('Found data, opening cursor for real');
OPEN v_cursor FOR v_query;
-- loop over results, return to caller, etc.
end;
/
db<>fiddle
Try to fetch a row and then use v_cursor%NOTFOUND to determine of the cursor is empty:
DECLARE
v_cursor SYS_REFCURSOR;
v_value DUAL.DUMMY%TYPE;
BEGIN
OPEN v_cursor FOR SELECT DUMMY FROM DUAL WHERE 1 = 0;
FETCH v_cursor INTO v_value;
IF v_cursor%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('Cursor empty');
ELSE
DBMS_OUTPUT.PUT_LINE('Cursor not empty');
END IF;
END;
/
or
DECLARE
CURSOR v_cursor IS
SELECT DUMMY FROM DUAL WHERE 1 = 0;
v_row v_cursor%ROWTYPE;
BEGIN
OPEN v_cursor;
FETCH v_cursor INTO v_row;
IF v_cursor%NOTFOUND THEN
DBMS_OUTPUT.PUT_LINE('Cursor empty');
ELSE
DBMS_OUTPUT.PUT_LINE('Cursor not empty');
END IF;
END;
/
Both output:
Cursor empty
db<>fiddle here

How to declare a cursor if table used in select statement may not exist sometimes?

I need to run below code on different environments and it works fine when table1 exists but when it does not exist then it throws error in cursor declaration that "table or view does not exist".
I am running this on Oracle.
Could you please help me in correcting this?
Thanks in advance.
DECLARE
CURSOR my_cursor IS (select "col1" from "table1");
name1 VARCHAR2(256);
tableCount NUMBER;
BEGIN
Select count(*) into tableCount from user_tab_cols where table_name = 'table1' and column_name = 'col2';
IF tableCount > 0 THEN
OPEN my_cursor;
LOOP
FETCH my_cursor into name1;
EXIT WHEN my_cursor%notfound;
-- Update or delete statement here
DBMS_OUTPUT.PUT_LINE('value is ' || name1);
END LOOP;
CLOSE my_cursor;
END IF;
END;
/
A CURSOR with a defined return type is strongly typed. Sys_refcursors are weakly typed. This means that any return type in a CURSOR must be valid. A SYS_REFCURSOR is more flexible and can be defined at a later time.
When setting a CURSOR in the declaration section, you must use a SQL statement that will execute properly. In this case, you are setting the CURSOR to select a column from a table that does not exist. The database execution will not reach the body of the code since it errors prior to exiting the declaration block.
To fix this, use a SYS_REFCURSOR with a dynamic sql query, as mentioned by Tejash above. This allows you to check to see if the table exists before setting the cursor. If the table exists, set the cursor to select from the specified table. If it doesn't, output a message saying that it does not exist.
Note that you can also use the SQL error codes, as shown in other answers. I prefer personally to handle business rules in the logic prior to the error occurring.
DECLARE
my_cursor sys_refcursor;
name1 VARCHAR2(256);
tableCount NUMBER;
BEGIN
Select count(*) into tableCount from user_tab_cols where table_name = 'table1' and column_name = 'col2';
IF tableCount > 0 THEN
OPEN my_cursor for 'select order_id from table1';
LOOP
FETCH my_cursor into name1;
EXIT WHEN my_cursor%notfound;
-- Update or delete statement here
DBMS_OUTPUT.PUT_LINE('value is ' || name1);
END LOOP;
CLOSE my_cursor;
else
dbms_output.put_line('Table does not exist');
END IF;
END;
http://docs.oracle.com/database/122/LNPLS/static-sql.htm#LNPLS568
You can use the exceptions and dynamic string for a cursor as follows:
DECLARE
MY_CURSOR SYS_REFCURSOR;
NAME1 VARCHAR2(256);
TABLECOUNT NUMBER;
BEGIN
OPEN MY_CURSOR FOR 'SELECT ACC_NR FROM ACCOUNT'; -- dynamic string for cursor
LOOP
FETCH MY_CURSOR INTO NAME1;
EXIT WHEN MY_CURSOR%NOTFOUND;
DBMS_OUTPUT.PUT_LINE('value is ' || NAME1);
END LOOP;
CLOSE MY_CURSOR;
EXCEPTION -- exception handling using SQLCODE
WHEN OTHERS THEN
IF SQLCODE = -942 THEN
DBMS_OUTPUT.PUT_LINE('Table does not exists');
ELSIF SQLCODE = -904 THEN
DBMS_OUTPUT.PUT_LINE('Invalid column name');
ELSE
DBMS_OUTPUT.PUT_LINE('Other error');
END IF;
END;
/

Oracle: Iterate over table and print out an xml file for every row name

I have created a table where, for example, there is a id number in each row, but it is not continuous (1,2,4,6,7). Now I want to create an XML file for each number, which also contains certain other data, but which are not important for the question.
My question now is how do I set up this for loop to pull out the number one by one and transfer it to my PL SQL? And i need to write an export date into the table after the export. Thanks for your help.
Table
id_order | export date
This is my code for the xml files and now i need a loop for the id_order Number:
declare
fhandle utl_file.file_type;
-- Output Variablen
output_intro varchar2(23000);
ID_order NUMBER := 1;
filename varchar2(100) := 'test.xml';
-- Intro
BEGIN
fhandle := utl_file.fopen('EXPORT', filename , 'w');
begin
SELECT
'<"ID">'|| ID ||'</ID>' || CHR(10)
into output_intro FROM Table
WHERE ID = ID_order;
exception
when others THEN
output_intro := 'Issue Intro';
end;
BEGIN
utl_file.put(fhandle, output_intro);
END;
utl_file.fclose(fhandle);
exception
when others then
dbms_output.put_line('ERROR: ' || SQLCODE
|| ' - ' || SQLERRM);
raise;
end;
/
If the purpose is to simnply return the 'ID' value for each row as XML, you should look at the SQLXML functions.
In your case, try this:
SELECT XMLFOREST(id) FROM table
It handles multiple columns easily:
SELECT XMLFOREST(id, col2, col3,) FROM table
To nest this between 'Data' element:
SELECT XMLELEMENT( "Data", XMLFOREST(id) ) FROM table
Putting it all together:
DECLARE
l_file UTL_FILE.FILE_TYPE;
l_xmltype XMLType;
BEGIN
SELECT XMLELEMENT( "Data", XMLFOREST(id) )
INTO l_xmltype
FROM table;
l_file := UTL_FILE.fopen ('EXPORT', 'output.xml', 'w');
UTL_FILE.PUT_LINE(l_file , l_xmltype.getStringVal( ));
UTL_FILE.fclose (l_file);
END;
/
Note if the XML size is greater than 32K then you will need to write tout the the file in chunks.
You can use a cursor to create a loop
-- Declare a cursor
Cursor Cur is select * from YourTable where YourCondition;
Row_ Cur%rowtype;
begin
-- Open the cursor
Open Cur;
-- Loop over the cursor rows
loop
-- Fetch every into the object Row_
fetch cur into Row_;
-- Exit when no rows found
exit when Cur%notfound;
-- Perform the export to a file here
--You can access Row_ columns by appending columns to Row_. For example : Row_.FirstName
end loop;
Close Cur;
end;

How to add a table name in EXECUTE IMMEDIATE query?

I have one question about "EXECUTE IMMEDIATE".
I have dynamicly changed table name in next plsql statement
DECLARE
TYPE CurTyp IS REF CURSOR;
cur CurTyp;
str1 VARCHAR2(30);
str2 VARCHAR2(30);
table_name VARCHAR2(30);
BEGIN
select data
into table_name
from ref
where o_id = 111
and a_id = 222;
OPEN cur FOR
'select name, sname from :1 b,myobjects a where a.obj_id = b.obj_id'
USING table_name;
LOOP
FETCH cur INTO str1, str2;
EXIT WHEN cur%NOTFOUND;
dbms_output.put_line(str1||str2);
END LOOP;
CLOSE cur;
END
Is it possible to read the result of next Execute Immediate query to cursor?
'select name, sname from :1 b,myobjects a where a.obj_id = b.obj_id'
USING table_name;
Or maybe is there any way to do this?
Thanks in advance.
For object names you must use concatenation, not bind variables.
From the Dynamic SQL chapter of the PL/SQL Language Reference:
The database uses the values of bind variables exclusively and does
not interpret their contents in any way.
Bind variables help with security and performance. But they won't work with objects like tables. If you pass in a table name then Oracle must interpret the contents, which will negate the security and performance benefits.
You can use ref_cursor
see http://docs.oracle.com/cd/B10500_01/appdev.920/a96590/adg09dyn.htm
the example:
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;
/
but as #jonearles said, you can't insert the table names as params

How to find Number of null column in table using PL/SQL

A database has a lot of columns (more than 100). Some of these columns have null entries. How can I find out how many columns have null entries in at least one row, without manually testing each and every column?
Try:
declare
l_count integer;
begin
for col in (select table_name, column_name
from user_tab_columns where table_name='EMP')
loop
execute immediate 'select count(*) from '||col.table_name
||' where '||col.column_name
||' is not null and rownum=1'
into l_count;
if l_count = 0 then
dbms_output.put_line ('Column '||col.column_name||' contains only nulls');
end if;
end loop;
end;
Try analyzing your table (compute statistics, don't estimate) and then (immediately) do:
select column_name, num_nulls
from all_tab_columns
where table_name = 'SOME_TABLENAME'
and owner = 'SOME_OWNER';
Of course as data later changes, this will become slightly more incorrect. If you need to get more fancy and do a field population count (fieldpop), then you'll need to loop through all rows and check for nulls explicitly (and exclude any other values you deem "not populated", perhaps a default of 0 for a number field for example).
I can give you the direction in which to research:
Check "user_tab_columns" through which you can get information related to columns in a table.
E.g.
select count(*) from user_tab_columns where table_name = 'YOURTABLENAME'
This gives you the number of columns in that table.
Together with this you would need to use a cursor, i think, to check each column for null values rather than adding a null check in WHERE clause for each column.
This will give you the number of NULL column values per row of data:
declare
TYPE refc IS REF CURSOR;
col_cv refc;
l_query varchar(3999);
v_rownum number;
v_count number;
begin
l_query := 'select rownum, ';
for col in (select table_name, column_name
from user_tab_columns where table_name='EMP')
loop
l_query := l_query ||'DECODE('||col.column_name||',NULL,1,0)+';
end loop;
l_query := l_query||'+0 as no_of_null_values from EMP';
DBMS_OUTPUT.PUT_LINE(l_query);
OPEN col_cv FOR l_query;
LOOP
FETCH col_cv into v_rownum, v_count;
EXIT WHEN col_cv%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_rownum || ' ' || v_count);
END LOOP;
CLOSE col_cv;
end;
I feel dirty even writing it! (It won't work when the number of columns in the table is very large and l_query overflows).
You just need to change the table name (EMP above).