Get a array of values form a PLSQL Function - oracle

I coded a function to select some flux in a queue and lock them with an updated flag.
I made it with a cursor and it worked great. But i need to get the ID of the flux i locked to process them in my application.
So i start to code a function:
CREATE OR REPLACE Function getIDArray
RETURN VARCHAR2 is
arr varchar2(1000);
CURSOR flux_to_process
IS
SELECT FLUX_ID, LOCKED_FLAG
FROM (
SELECT FLUX_ID, FLUX, GROUP_STORE_ID, STORE_ID, REFID, FLUX_TYPE, LOCKED_FLAG
FROM DEV_ISB_TRANSACTIONS.BUFFER_FLUX
WHERE status = 0
AND LOCKED_FLAG = 0
ORDER BY DATE_CREATION ASC)
WHERE ROWNUM <= 8;
BEGIN
FOR flux_rec IN flux_to_process
LOOP
IF flux_rec.LOCKED_FLAG = 0
THEN
UPDATE DEV_ISB_TRANSACTIONS.BUFFER_FLUX
SET LOCKED_FLAG = 1
WHERE FLUX_ID = flux_rec.FLUX_ID;
arr := flux_rec.FLUX_ID;
else exit;
COMMIT;
END IF;
END LOOP;
RETURN arr;
END;
The function compilation return an OK but i got no return of my values.
Do you guys have any clue to how to do this ?

Concerning your issue per se, the only two reasons I can see for the function to return "no value" would be either:
the SELECT part returns an empty set,
you have one record where FLUX_ID is NULL.
For improbable that could be that later option given the name of the column, it would be rather coherent with the fact that you override the result at each iteration -- and the ORDER BY orders NULL after not-NULL by default.

Related

PL/SQL Unable to get return value of a function when called through trigger - Oracle

I am calling a function in oracle in an after update trigger. The Function is returning a value that is equated to perform a select and an insert operation.
The issue is when I am calling this function in the trigger it is getting terminated, that is it is not performing the corresponding insert operation. But the function is working fine when I execute it by itself. Also, if the trigger is run by removing the condition which is returned by the function, it is getting executed as expected.
Function:
CREATE OR REPLACE FUNCTION VERIFY_FINAL
(case_id IN number)
RETURN varchar2
IS
is_marked_final varchar2(4);
loop_count number(2);
cursor c1 is
SELECT sub_case_status from
cdm_master_sub_case
where master_id = (case_id);
BEGIN
is_marked_final := 'Y';
loop_count := 0;
FOR rec in c1
LOOP
IF (rec.sub_case_status = '1') THEN
is_marked_final := 'Y';
ELSIF (rec.sub_case_status = '2') THEN
is_marked_final := 'Y';
ELSE
loop_count := loop_count + 1;
END if;
END LOOP;
IF (loop_count > 0) THEN
is_marked_final := 'N';
END if;
RETURN is_marked_final;
END;
Trigger:
CREATE OR REPLACE TRIGGER CDM_MASTER_SUB_CASE_TRIGGER
AFTER UPDATE
on CDM_MASTER_SUB_CASE
FOR EACH ROW
DECLARE
check_var varchar2(4);
unique_id varchar2(100);
transaction_id number(10);
BEGIN
transaction_id := :new.MASTER_ID;
check_var := VERIFY_FINAL(transaction_id);
IF (check_var = 'Y') THEN
select UNIQUE_CUST_ID
INTO unique_id
from ASM355.cdm_matches
where MASTER_ID = :new.MASTER_ID
and rownum = 1;
INSERT INTO tracking_final_cases (MASTER_ID,unique_cust)
values (:new.master_id,unique_id);
END if;
END;
I would appreciate it if anyone can point me in the right direction.
1.) As tmrozek points out a return of 'N' will not do the associated insert. I might suggest having an ELSE to that IF that does something to indicate if that is what is happening.
2.) I would also point out that your SELECT INTO, if it does not find a corresponding value, would cause issues. You might want to do something to ensure that this trigger is failsafe, or have you considered what you want the code to do if that situation occurs? (Error out? Insert a null unique_id?)
3.) If you are looking at the results from a different session, bear in mind that the inserted tracking_final_cases will not be visible until you commit your changes in the session that called the trigger.
I don't know your table data but it is possible to your function to return 'N' so it wouldn't meet your trigger condition (check_var = 'Y').
If you run command like that:
update CDM_MASTER_SUB_CASE
set sub_case_status = 3;
you will probably get your problem.
Thanks guys for the time, it got resolved. I was querying a select statement in the function body over a table on which the corresponding trigger was created.

function not returning a value for no rows returned

I created a function that takes a movie id as input and returns stock information based from the ID. The function mostly works but if I want to retrieve information from a movie that is not in the database(returns no rows) nothing returns. Can't figure out why?
doesn't give me an error when i call an ID that returns no rows so exception handling wouldn't work.
create or replace function stock_info
(p_id IN NUMBER
)
return VARCHAR2
IS
cursor c1 is
select movie_id, movie_title, movie_qty
from mm_movie
where p_id = movie_id;
lv_movie_info VARCHAR2(100);
BEGIN
for i in c1 loop
if p_id = i.movie_id then
lv_movie_info := i.movie_title || ' is available: ' || i.movie_qty || ' on the shelf';
else
lv_movie_info := 'no data found';
end if;
end loop;
return lv_movie_info;
END STOCK_INFO;
/
The reason you don't get anything when there is no data is that the loop doesn't execute. Logically the For expression says "execute the following loop for every row returned in the cursor" but there are no rows in the cursor so it never executes the loop. Further the structure actually indicates you are expecting multiple for a given p_id. If that's not the case you can eliminate the cursor all together. Assuming p_id is the primary key you have either 0 or 1 row so:
create or replace function stock_info (p_id in number)
return text
is
lv_movie_info varchar2(100);
begin
select i.movie_title || ' is available: ' || i.movie_qty || ' on the shelf'
into lv_movie_info
from mm_movie i
where p_id = movie_id;
return lv_movie_info;
exceptions
when no_data_found
then return 'no data found';
end stock_info;
Of course if do expect more that 1 row the cursor is needed, but the IF is not as the were clause guarantees it's true. Still with 0 rows the loop will not be executed so the 'no data found' message needs to go after "End Loop".
Belayer
the cursor statement you used fetches data from the in parameter. i.e., in the cursor select you limiting based on the movie id passed.
on passing a movie id which is not in the data base, the cursor select statement would not fetch any records, and so the flow won't even go inside the for loop.
if you wanted to return no data found - on passing a movie id which is not in the database, two ways to resolve
1. before the loop, have select statement to set a flag to Y or N if exists according and to have your requirement.
2. in if not using for cursor, there is an option to check not found...
sample:
declare
cursor c1 is select * from table_sample; -- empty table
c_rec c1%rowtype;
begin
open c1;
fetch c1 into c_rec;
if c1%notfound then
dbms_output.put_line('not found');
end if;
close c1;
end;
-- output
not found

PL/SQL function while loop returning 1 row only

I have tables(Customers,Pbasket and pp) with the following values inserted to the table
http://pastebin.com/eMUtLJn9
Basically I am tasked to create a function that finds all the product number(p#) purchased by the customer and I have to pass in the customer id(c#) as a input parameter;
This is my pl/sql script
http://pastebin.com/SqkY0P9N
I noticed that
there is no results returned for c#(100), which is correct.
but i noticed that for c#(101) and c#(102)
the result should return more than one p# but it only returns 1 result even though I had my while loop.
the output of my results
Please advice.
The function you wrote was supposed to return One row only. And also, you had a return in your LOOP. So after first iteration the control is returned back.(leaving the cursor opened for ever)
Create a TYPE of SQL Object
create type my_numbers as table of NUMBER;
/
FUNCTION returning a table!
CREATE OR REPLACE FUNCTION purchased(cId IN NUMBER)
RETURN my_numbers
IS
product VARCHAR2(45);
I NUMBER;
v_my_list my_numbers := my_numbers();
CURSOR CursorRow IS
SELECT p#
FROM customer c
LEFT OUTER JOIN pbasket pb
ON c.c# = pb.c#
LEFT OUTER JOIN pp pp
ON pb.whenfinalised = pp.whenfinalised
WHERE c.c# = cId;
CurrentPos CursorRow%ROWTYPE;
BEGIN
OPEN CursorRow;
I := 1;
FETCH CursorRow into CurrentPos;
LOOP
EXIT WHEN CursorRow%NOTFOUND
v_my_list.EXTEND;
v_my_list(I) := CurrentPos.p#;
I := I + 1;
END LOOP;
CLOSE CursorRow;
RETURN v_my_list;
END purchased;
/
Final SELECT Query:
select * FROM TABLE(CAST(purchased(100) as my_numbers));
We can also use Pipelined functions (Slight modification needed) for performance over Large resultsets!

How to select a different dataset depending on a parameter value in Reporting Services

It is possible to select a different dataset (query) depending on the value of a parameter in Reporting Services? Thanks in advance!
if you call a stored procedure to return your result set, just have a parameter determine what version of the query to run. keep the result set columns and their types the same.
create procedure YourReportProcedure
(
#ReportVersion char(1)
,#filterParam1 varchar(12)
,#filterParam2 int
....
)
if #ReportVersion='A'
BEGIN
SELECT
A,B,C
FROM .....
WHERE x=#filterParam1 and y=#filterParam2
END
ELSE IF#ReportVersion='C'
BEGIN
SELECT
A,B,C
FROM .....
WHERE g>#filterParam1 and r<#filterParam2
END
ELSE
BEGIN
return 1 --error
END
return 0
go

PL/SQL procedure for loop though a table and change values

Basically I need to make a for loop that will loop though the amount of rows. In each row I need to check a value and change it if it meets the requirements.
I'm new to Oracle, so I just started building it one step at a time and I'm stuck on looping through the table rows. I need to first get the number count of the rows that a Boolean flag set to 0 (false). So then I can loop through only those rows, not every row in the table. Once I'm done with whatever I need to change in that row, set the flag to 1 (true), so when I run the procedure again it won't include that row.
Here's what I have so far:
My table:
CREATE TABLE test_table_results (
name varchar,
account number,
address varchar,
database_search NUMBER(1) DEFAULT 0 NOT NULL
CONSTRAINT searched_in_database CHECK (database_search IN (0,1))
);
The table in my database:
CREATE TABLE test_table_accounts (
name varchar,
account number,
address varchar,
);
Now the procedure will go though the results table and see if the address match, if they do it will copy the account number from the database table into the results account number, then change the flag from 0 to 1, so the next time I search though the table it won't include it because it was already searched.
create or replace PROCEDURE FIND_MATCH_ADDRESS AS
BEGIN
DECLARE
v_cnt NUMBER;
BEGIN
FOR i IN (SELECT rowid, r.* FROM test_table
WHERE database_searched = 0)
LOOP
LOOP
SELECT COUNT(1) INTO v_cnt
FROM test_table
WHERE database_searched = 0;
DBMS_OUTPUT.PUT_LINE(v_cnt);
END LOOP;
END LOOP;
END;
END FIND_MATCH_ADDRESS;
EDIT: Added the two tables in hopes to make my question/task more understandable.
Again thank you for your time!!
In your example I see some mistakes.
In the procedure you do not need Declare. Declaration block is between as and begin
create or replace PROCEDURE proc
AS
-- here variable declaration or local function or procedures
BEGIN
-- here you can write a business logic
END proc;
You can iterate over the records of a table with a For loop. In your example, you also tried to use them. You can iterate over the records of a table with a For loop. In your example, you also tried to use them.
FOR record IN (cursor)
LOOP
{...statements...}
END LOOP;
I did not quite understand why you used another loop in the loop. a loop statement is an endless loop.
loop
...
end loop;
In the loop you can now implement your logic. If you really want to use a loop, then your solution might look like this
create or replace PROCEDURE FIND_MATCH_ADDRESS
AS
v_cnt NUMBER;
BEGIN
FOR rec IN (SELECT r.name
,r.address
,r.account
,a.account as new_account
FROM test_table_results r
join test_table_accounts a on a.address = r.address
WHERE r.database_searched = 0)
LOOP
update test_table_results
set account = rec.new_account
, database_searched = true
where account = rec.account
and name = rec.name
and adress = rec.adress;
END LOOP;
END FIND_MATCH_ADDRESS;
Alternatively, you can also do that with an update. Since I do not know your tables, you should then optimize the where condition.
update test_table_results t
set database_searched = true
, account = (select account
from test_table_accounts a
where a.account = t.account
and a.name = t.name
and a.adress = t.adress)
where database_searched = false
and exists(select 1
from test_table_accounts a
where a.account = t.account
and a.name = t.name
and a.adress = t.adress);

Resources