oracle plsql complex statement inside if condition - oracle

In the pl/sql code i have a variable that grabs the designation_code from a table of a specific employee.
STMT#1
select basic_designation into source_designation
from tbl_emp_basic_profile where basic_id=source_id;
now I need to check if source_designation is in a set of codes or not. the {set of codes} can be generated by the following sql:
STMT#2
select distinct(BASIC_DESIGNATION) as "SET_OF_CODES"
from TBL_EMP_BASIC_PROFILE
where BASIC_DESIGNATION in (select to_number(SD_DESIGNATION_CODE)
from TBL_SETTINGS_DESIGNATION
where lower(SD_DESIGNATION_NAME) like '%professor%'
or lower(SD_DESIGNATION_NAME) like '%lecturer%');
how do I do it? Could I simply write an IF statement like the following?
IF(source_designation in (STMT#2)) then
--do somtehing
END IF;

I would have written it this way , using the exists to avoid extra scannings, and a count to avoid exception handling.
select count(1)
into designation_is_in_set
from dual
where exists (select 1 from TBL_SETTINGS_DESIGNATION
where to_number(SD_DESIGNATION_CODE)=source_designation
and (
lower(SD_DESIGNATION_NAME) like '%professor%'
or lower(SD_DESIGNATION_NAME) like '%lecturer%'
)
);
if designation_is_in_set=1 then
-- the des is in the set
else
-- the des is not in the set
end if;

You can do it that way.
1) declare collection
2) fetch all values into collection from sql.
3) Check if your value is in collection.
Example.
declare
type T_SET_OF_CODES is table of varchar2(xxx); -- where xxx is appropriate size of varchar2 for size_of_codes
V_SET_OF_CODES T_SET_OF_CODE;
begin
select distinct(BASIC_DESIGNATION) as "SET_OF_CODES" bulk collect into V_SET_OF_CODES
from TBL_EMP_BASIC_PROFILE
where BASIC_DESIGNATION in (select to_number(SD_DESIGNATION_CODE)
from TBL_SETTINGS_DESIGNATION
where lower(SD_DESIGNATION_NAME) like '%professor%'
or lower(SD_DESIGNATION_NAME) like '%lecturer%');
IF source_designation member of V_SET_OF_CODES then
--do somtehing
END IF;
end;

Related

How to insert records into variables from cte in oracle?

I have a procedure in which I want to fetch all records from cte into Names variable. But this code is not writing into names from CTE. How can I fetch records into names so that I can later loop through names and get content of field_name?
CREATE OR REPLACE PROCEDURE sp_market
IS
Names VARCHAR2(32767);
BEGIN
WITH CTE(sqql) As
(
SELECT field_name sqql FROM pld_medicare_config
)
SELECT sqql into Names from CTE;
END sp_market;
SELECT sqql into Names from CTE;
You are assigning multiple rows returned from table to a variable, which will fail.
You could simply use a CURSOR FOR LOOP which will create an implicit cursor and you can loop through the names:
CREATE OR REPLACE PROCEDURE sp_market IS
BEGIN
FOR i IN (
SELECT field_name
FROM pld_medicare_config
)
LOOP
-- Your logic goes here
dbms_output.put_line(i.field_name);
END LOOP;
END;
/
I think your best bet is to create a associative array and use BULK COLLECT to populate the table. In its simplest form, the code would look like this:
CREATE OR REPLACE PROCEDURE sp_market IS
TYPE lt_names IS TABLE OF VARCHAR2(32767) INDEX BY PLS_INTEGER;
l_tNames lt_names;
BEGIN
SELECT field_name
BULK COLLECT INTO l_tNames
FROM pld_medicare_config
IF l_tNames.COUNT() > 0 THEN
FOR i IN l_tNames.FIRST..l_tNames.LAST LOOP
NULL; --REPLACE WITH YOUR LOGIC
END LOOP;
END IF;
END;
/
A few notes:
I'm assuming that you've set MAX_STRING_SIZE to EXTENDED. Otherwise, you'll have an issue with VARCHAR2 that big.
As I said, that is the simplest way to do this. If you're expecting a huge result set, you'll want to look into chunking it up. This Oracle blog post is very helpful in giving you multiple options for how to perform bulk processing. (Blog Post)

Back up Oracle with script (Insert statements, DDL)

I would like to backup part of a database with Insert and DDL statements with output similar to what you can get with TOAD or SQL Developer, but from a script.
Details:
This is to be able to see changes and differences with source control.
We are using SQL Developer, LINUX tools and Python (With Oracle).
try this? Change settings as per your need.
Exporting Schema:
1) Run following in command prompt (not mandatory though)
SET NLS_LANG AMERICAN_AMERICA.UTF.8
2) Once above is set run the below, run the below. Change username/password/schemaname, path to export
exp userid=<schemaname>/<pwd>#<dbname or SID> file=<localpath\1.dmp>
log=<localpath\2.log> buffer=1000000000 feedback=25000
direct=y recordlength=64000 owner=<schemaname>
There must be tools for what you are trying to do (other than TOAD and PL/SQL).
This is a great project you have if you intend to write the code for this. You must work as follows:
For DDLs
. Write a script that list all the objects you need to extract; this will be tables, schemas, procedures, functions:
for x in (
select * from dba_objects where object_type in ('PACKAGES', 'TABLES', '>others>') loop
--- things to do with x.>columns<
end loop;
for u in (
select * from dba_users where username in ( <list of needed schemas> ) loop
)
--- things to do with users
end loop;
. Then from above list, you have Oracle function dbms_metadata.get_ddl that generates the DDL. e.g. with users:
begin
for u in (select * from dba_users where <list of needed schemas>
order by username)
loop
dbms_metadata.get_ddl('USER', u.username);
end loop;
end;
For the inserts
(this could take time to generate on databases with large tables (thousands of rows is already a lot for my approach).
I advise you create a generic function that generates the insert statements. This is rather simple as long as it does not include BLOB and CLOB. You take each column of each table.
Remember to generate the inserts in an orderly way, otherwise the comparison will not be possible.
The generic function should take a table as input. From the table name, it gets the columns from dba_tables, then selects the table.
--- plsql code below lists the columns of the tables
begin
for t in (select * from dba_tables order by table_name) loop
for c in (select * from dba_tab_columns where table_name=t.table_name and owner=t.owner order by owner, table_name, column_name) loop
dbms_output.put_line(c.table_name||'.'||c.column_name);
end loop;
end loop;
end;
Here is how you could collect the values in an array, then loop on this array to display the insert statements as text (with dbms_output.put_line)
declare
type t_v is table of varchar2(32000);
t_vchars t_v;
v_ins varchar2(2000):=' ';
v_sel varchar2(2000):=' ';
v_res varchar2(4030):=' ';
begin
for t in (select * from dba_tables order by table_name) loop
v_ins :='insert into '||t.table_name||'(';
v_sel :=' select ';
for c in (select * from dba_tab_columns where table_name=t.table_name and owner=t.owner) loop
v_ins:= v_ins||c.column_name||',';
v_sel:= v_sel||'''''''||'||c.column_name||'||'''''',';
end loop;
-- remove last comma
v_ins:= substr(v_ins,1,length(v_ins)-1);
v_sel:= substr(v_sel,1,length(v_sel)-1);
v_ins:= v_ins||')';
v_sel:= v_sel||' from dual';
v_res:= 'select '''||v_ins||v_sel||''' from '||t.table_name;
-- above, you still need to insert an order by, but I let you choose which solution
execute immediate v_res bulk collect into t_vchars;
FOR i in 1..t_vchars.count LOOP
dbms_output.put_line(t_vchars(i)||';');
END LOOP;
end loop;
end;
After you're done and want to test the whole script.
One tricky thing when you want to test and run the inserts is if you have foreign key. You must deactivate them before inserting, otherwise you need to do the inserts in a ordered way, which is not easy to do.
for c in (select * from dba_constraints)
loop
--- disable constraint
end loop;
Then after inserts are done
for c in (select * from dba_constraints)
loop
--- enable constraint
end loop;

Storing an entire column in a variable inside a stored procedure

I have a complex stored procedure inside a package. Inside this SP, I need to query a table to get all the data pertaining one column and then use this to check some other condition inside an "IF" statement.
Here is what I am doing:
--declare a variable to store the holidays
l_holidays MySchema. HolidayTable.DateColumn%TYPE
-- populate this variable
Select a.DateColumn into l_holidays
from MySchema. HolidayTable a;
-- using this variable inside an "IF" statement
IF (current_Date IN l_holidays)
THEN
-- do something
ELSE
-- do something
END IF;
Every time I run this, I get the following error
ORA-01422: exact fetch returns more than requested number of rows
I know this is because I am trying to populate the entire column using the "INTO" clause. But I don't know any other way of doing it.
Create a collection and use BULK COLLECT INTO:
CREATE PROCEDURE my_proc (
current_date IN MySchema.HolidayTable.DateColumn%TYPE
)
AS
TYPE date_tab IS TABLE OF MySchema.HolidayTable.DateColumn%TYPE;
l_holidays date_tab;
BEGIN
SELECT DateColumn
BULK COLLECT INTO l_holidays
FROM MySchema.HolidayTable;
IF (current_Date MEMBER OF l_holidays)
THEN
NULL; -- do something
ELSE
NULL; -- do something
END IF;
END;
Otherwise you can just test in the select:
CREATE PROCEDURE my_proc (
current_date IN MySchema.HolidayTable.DateColumn%TYPE
)
AS
has_date NUMBER(1,0);
BEGIN
SELECT CASE WHEN EXISTS ( SELECT 'X'
FROM MySchema.HolidayTable
WHERE DateColumn = Current_Date )
THEN 1
ELSE 0
END
INTO has_date
FROM DUAL;
IF has_date = 1
THEN
NULL; -- do something
ELSE
NULL; -- do something
END IF;
END;
Hello similarly you can use this query to fulfill your requirements
SET serveroutput ON;
SET sqlbl ON;
DECLARE
type l_holiday
IS
TABLE OF DATE;
tab_holiday l_holiday;
BEGIN
SELECT a.dt BULK COLLECT
INTO tab_holiday
FROM
(SELECT SYSDATE dt FROM DUAL
UNION
SELECT SYSDATE+1 dt FROM DUAL
UNION
SELECT SYSDATE+2 dt FROM DUAL
UNION
SELECT SYSDATE+3 FROM DUAL
)a;
IF tab_holiday.COUNT > 0 THEN
IF SYSDATE MEMBER OF tab_holiday THEN
dbms_output.put_line('yes working');
ELSE
dbms_output.put_line('awsme working');
END IF;
END IF;
END;

Using like with varchar2 that have null value at start

The following is excerpt from my procedure that creates logical backup after checking the backup_schedule value in schema_info table:
CREATE OR REPLACE PROCEDURE BACKUP_EXECUTE
(
[...]
) AS
[...]
schemas varchar2(255):=' ';
cursor schema_name is select upper(schema_name) schema_name from schema_info
where backup_schedule like '%'||to_char(sysdate, 'D')||'%'
and exists (select * from dba_users where username=upper(schema_name));
type sl is table of schema_name%ROWTYPE
index by pls_integer;
schema_list sl;
BEGIN
open schema_name;
fetch schema_name bulk collect into schema_list;
close schema_name;
if schema_list.count != 0 then
for indx in 1..schema_list.count loop
if(schemas not like '%'||schema_list(indx).schema_name||'%') then
if indx>1 then
schemas:=schemas||',';
else
schemas:=null;
end if;
schemas:=schemas||''''||schema_list(indx).schema_name||'''';
end if;
end loop;
[...]
end if;
EXCEPTION
[...]
END;
Besides the fact, that this can be done better, I want to concentrate on one thing. As we know like is not working with null values. Should I leave the code as above and write some start value which later is deleted or is it better to use nvl(schemas,' ') in if statement?

Creating a result set (using a select statement ) within a loop

I have created a cursor which returns me a set of rows. While iterating through each of the row, I want to get another result set (by forming a SELECT statement by with a WHERE clause having value from the processed row) from another table. I am a newbie in PLSQL. Can you please guide me on how this could be done? (Can we have a Cursor defined inside the loop while looping for the resultset of the cursor)?
Please excuse me if I am not able to make myself clear.
Thanks in advance
DECLARE
CURSOR receipts IS
SELECT CREATED_T, ACCT_NO, AMT FROM receipt_t
WHERE OBJ_TYPE='misc';
receipts_rec receipts%ROWTYPE;
BEGIN
-- Open the cursor for processing
IF NOT receipts%ISOPEN THEN
OPEN receipts;
END IF;
LOOP
FETCH receipts INTO receipts_rec;
EXIT WHEN receipts%NOTFOUND;
/* Loop through each of row and get the result set from another table */
newQuery := 'SELECT * FROM ageing_data WHERE ACCT_NO = ' || receipts_rec.ACCT_NO;
-- Execute the above query and get the result set, say RS
LOOP
-- For above result set-RS
END LOOP;
END LOOP;
CLOSE receipts;
END;
Yes, you can define a cursor that takes a set of parameters and use those values in the WHERE clause.
DECLARE
CURSOR c_cursor1 IS
SELECT field1, field2, ... , fieldN
FROM table1
WHERE conditions;
CURSOR c_cursor2 (p_parameter NUMBER) IS
SELECT field1, field2, ..., fieldN
FROM table2
WHERE table2.field1 = p_parameter;
BEGIN
FOR record1 IN c_cursor1 LOOP
FOR record2 IN c_cursor2(record1.field1) LOOP
dbms_output.put_line('cursor 2: ' || record2.field1);
END LOOP
END LOOP;
END;
Yes, you can do that, but there is absolutely no reason to. Try the following:
BEGIN
FOR aRow IN (SELECT rt.CREATED_T, rt.ACCT_NO, rt.AMT, ad.*
FROM RECEIPT_T rt
INNER JOIN AGEING_DATA ad
ON (ad.ACCT_NO = rt.ACCT_NO)
WHERE rt.OBJ_TYPE='misc')
LOOP
-- Process the data in aRow here
END LOOP;
END;
This does exactly the same work as the original "loop-in-a-loop" structure but uses the database to join the tables together on the common criteria instead of opening and closing cursors multiple times.
Share and enjoy.
Something like this can be done in the following manner:
DECLARE
CURSOR cursor1 IS
SELECT *
FROM table1;
CURSOR cursor2 IS
SELECT *
FROM table2
WHERE column1 = I_input_param;
BEGIN
FOR table_1_rec in cursor1 LOOP
I_input_param := table_1_rec.column_1;
FOR table_2_rec in cursor2 LOOP
....
....
END LOOP;
END LOOP;
END;
I have used an implicit open/fetch here. I hope you get the idea.

Resources