PL/SQL Select column value into a variable - oracle

I have a View that I've created and I need to extract some tokens from it.I read about BULK COLLECT but I don't think that it will work for me as later on I'll need the extracted values stored separately.
SELECT TOKEN FROM MAPPING_VIEW WHERE USERNAME='Peter'.
Here for example I'l get returned a number of tokens (the number of tokens returned can not be predicted).
I'm pretty new at pl/sql can someone give me directions on how to store the extracted tokens in different variables? As I mentioned I don't know how many tokens may be returned beforehand.

you can try this
DECLARE
TYPE rec_token IS TABLE OF VARCHAR2(100);
l_rec_token rec_token;
BEGIN
BEGIN
SELECT token
BULK COLLECT INTO l_rec_token
FROM mapping_view
WHERE username='Peter';
EXCEPTION
WHEN NO_DATA_FOUND THEN
l_rec_token := rec_token();
END ;
IF l_rec_token.COUNT >0 THEN
FOR i IN l_rec_token.FIRST..l_rec_token.LAST
LOOP
<doSomeThing>
END LOOP;
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)

Oracle - two loops in procedure

I need some help in writing Oracle PL/SQL procedure that should do the following:
the procedure is called from a trigger after an update of the field in one table with the input parameter B-block or D-activate (this is already done)
the procedure should first open one cursor that will catch the account numbers of a client and open a loop that will process account by account
this one account should be forwarded to another loop that will catch card numbers of that client for that account (second cursor) and when into this loop, the card number should be used as an input parameter for a stored procedure that is called to block/unblock this card - this stored procedure already exists I just need to call it
the procedure don't need to return any parameters, the idea is just to block/activate card number of a client with the already written stored procedure for that
Should I write a package for this or just a procedure? And how can I write one loop in another?
I just realized that i can do this without cursors in a procedure. For simple example:
create or replace procedure blokiraj_proc (core_cust_id varchar2, kyc_blocked varchar2) as
type NumberArray is Array(100) of test_racuni.foracid%type;
type StringArray is Array (1000) of test_kartice.card_num%type;
accnt NumberArray;
card_number StringArray;
begin
select foracid bulk collect into accnt from test_racuni where cif_id = core_cust_id;
for i in accnt.first..accnt.last
loop
select card_num bulk collect into card_number from test_kartice where rbs_acct_num = accnt(i);
dbms_output.enable (100000);
dbms_output.put_line (accnt(i));
for j in 1..card_number.count
loop
dbms_output.put_line (card_number(j));
blokiraj_karticu (card_number(j));
end loop;
end loop;
end;
Is this a better approach then the curssors? And why is dbms_output not printing anything when i trigger the procedure?
As #EdStevens indicated you cannot avoid processing cursors. But you can avoid the looping structure of cursor within cursor. And the implicit open and close cursor for the inner one. The queries have combine into a simple JOIN then bulk collect into a single collection.
For this I created a RECORD to contain both the account number and card number; then a collection of that record. The cursor is then bulk collected into the collection. Your initial code allows for up to 100000 cards to be processed, and while I am a fan of bulk collect (when needed) I am not a fan of filling memory, therefore I limit the number of rows bulk collect gathers of each fetch. This unfortunately introduces a loop-within-loop construct, but the penalty is not near as great as cursor-within-cursor construct. The following is the result.
create or replace procedure blokiraj_proc (core_cust_id varchar2) as
type acct_card_r
is record(
acct_num test_kartice.rbs_acct_num%type
, card_num test_kartice.card_num%type
);
type acct_card_array is table of acct_card_r;
acct_card_list acct_card_array;
k_acct_card_buffer_limit constant integer := 997;
cursor c_acct_card(c_cust_id varchar2) is
select r.foracid
, k.card_num
from test_racuni r
left join test_kartice k
on (k.rbs_acct_num = r.foracid)
where r.cif_id = c_cust_id
order by r.foracid
, k.card_num;
begin
dbms_output.enable (buffer_size => null); -- enable dbms_output with size unlimited
open c_acct_card(core_cust_id);
loop
fetch c_acct_card
bulk collect
into acct_card_list
limit k_acct_card_buffer_limit;
for i in 1 .. acct_card_list.count
loop
dbms_output.put (acct_card_list(i).acct_num || ' ==> ');
if acct_card_list(i).card_num is not null
then
dbms_output.put_line (acct_card_list(i).card_num);
blokiraj_karticu (acct_card_list(i).card_num);
else
dbms_output.put_line ('No card for this account');
end if;
end loop;
-- exit buffer fetch when current buffeer is not full. As that means all rows
-- from cursor have been fetched/processed.
exit when acct_card_list.count < k_acct_card_buffer_limit;
end loop;
close c_acct_card;
end blokiraj_proc;
Well this is just another approach. If it's better for you, great. I also want to repeat and expand Ed Stevens warning of running this from a trigger. If either of the tables here is the table on which the trigger fired you will still get a mutating table exception - you cannot just hide it behind a procedure. And even if not its a lot of looping for trigger.

How to use checkbox in a report in oracle apex to insert values in a table

I have a table call OUTGOING which has many fields but the ones to be populated in this situation is:
FILENUMBER
OUTGOINGDATE
DEPARTMENT
now i have a report which whas an sql
SELECT APEX_ITEM.CHECKBOX2(1,registry.filenumber) "Select",
INCOMINGREQUESTNOTIFICATION.REQUESTEDFILE as REQUESTEDFILE,
INCOMINGREQUESTNOTIFICATION.FILENUMBER as FILENUMBER,
INCOMINGREQUESTNOTIFICATION.REQUESTEDDEPARTMENT as REQUESTEDDEPARTMENT,
INCOMINGREQUESTNOTIFICATION.REQUESTDATE as REQUESTDATE,
REGISTRY.STATUS as STATUS
from REGISTRY REGISTRY,
INCOMINGREQUESTNOTIFICATION INCOMINGREQUESTNOTIFICATION
where REGISTRY.FILENUMBER(+) =INCOMINGREQUESTNOTIFICATION .FILENUMBER
and INCOMINGREQUESTNOTIFICATION.STATUS ='PENDING'
which is fine .. what i need is for
INCOMINGREQUESTNOTIFICATION.FILENUMBER as FILENUMBER
INCOMINGREQUESTNOTIFICATION.REQUESTEDDEPARTMENT as REQUESTEDDEPARTMENT
and sysdate
to be inserted in the outgoing table under the relevant names of course.
I have a pl/sql
DECLARE
L_FILENUMBER WWV_FLOW_GLOBAL.VC_ARR2;
BEGIN
L_FILENUMBER := APEX_APPLICATION.G_F01;
FOR IDX IN 1 .. L_FILENUMBER.COUNT
LOOP
IF L_FILENUMBER(IDX) IS NOT NULL THEN
INSERT INTO OUTGOING
(FILENUMBER,OUTGOINGDATE,DEPARTMENT)
VALUES
((to_number(APEX_APPLICATION.G_F01(1)))
,SYSDATE
,to_char(APEX_APPLICATION.G_F02(2)) )
;
END IF;
END LOOP;
END;
which is not working.. However if i leave only filenumber
DECLARE
L_FILENUMBER WWV_FLOW_GLOBAL.VC_ARR2;
BEGIN
L_FILENUMBER := APEX_APPLICATION.G_F01;
FOR IDX IN 1 .. L_FILENUMBER.COUNT
LOOP
IF L_FILENUMBER(IDX) IS NOT NULL THEN
INSERT INTO OUTGOING
(FILENUMBER)
VALUES
((to_number(APEX_APPLICATION.G_F01(1)))
;
END IF;
END LOOP;
END;
its inserting only the file number fine . This is all being done via a submit button.
NB: i also tried putting outgoing date and department in the declare statement but it still doesnt work
I'd suggest you to learn how to use table aliases. SELECT you wrote is difficult to read due to VERY long table & column names; alias would certainly help.
As of your question: saying that "it is not working" doesn't help at all. What exactly doesn't work? Is there any error? If so, which one? Did you run the page in debug mode and check what's going on? If not, do that.
Apart from that, code you wrote doesn't make much sense - you're trying to insert the same values all over again. E.g. shouldn't APEX_APPLICATION.G_F01(1) be APEX_APPLICATION.G_F01(IDX)? Something like this:
begin
for idx in 1 .. apex_application.g_f01.count
loop
if apex_application.g_f01(idx) is not null then
insert into outgoing
(filenumber,
outgoingdate,
department
)
values
(apex_application.g_f01(idx),
sysdate,
apex_application.g_f02(idx)
);
end if;
end loop;
end;
Check (by inspecting the page) whether (tabular form?) items really are those that you've used (G_F01 and G_F02). If not, you'll have to fix that.
I didn't use any TO_NUMBER nor TO_CHAR functions; I don't know whether your really need them. Even if you don't have them, Oracle will perform implicit datatype conversion when possible, but it'll fail if you try to put e.g. 'abc123' into a NUMBER datatype column. You didn't share that information so - I left those out. Apply them if necessary.

get every result of a query to use after

I'm trying to get every result of a query to then print an output and make other query with the result. I'm trying to make it with a Cursor. But when I try to print the query result it says me:
PLS-00306: wrong number or types of arguments in call to 'PUT_LINE'
My code is:
DECLARE
-- Store the SELECT query in a cursor
CURSOR l_cur IS select memname from emuser.DEF_TABLES#controlm t, emuser.DEF_JOB#controlm j where (t.TABLE_ID = j.TABLE_ID) and t.sched_table = 'DWHRAC_DIARIOS2_DC2';
--Create a variable that will hold each result from the cursor
l_cur_rec l_cur%ROWTYPE;
BEGIN
-- Open the Cursor so that we may retrieve results
OPEN l_cur;
LOOP
-- Get a result from the SELECT query and store it in the variable
FETCH l_cur INTO l_cur_rec;
-- EXIT the loop if there are no more results
EXIT WHEN l_cur%NOTFOUND;
-- INSERT INTO another table that has the same structure as your results
DBMS_OUTPUT.PUT_LINE( l_cur_rec);
END LOOP;
-- Close the cursor to release the memory
CLOSE l_cur;
END;
Could you help me please?
Thanks
DBMS_OUTPUT.PUT_LINE accepts a single string as a parameter, but here it is being passed a variable of type l_cur%ROWTYPE which is not allowed. You should change the call to PUT_LINE to specify the name of the column(s) you want to print, such as:
DBMS_OUTPUT.PUT_LINE(l_cur_rec.MEMNAME);
Share and enjoy.

PLSQL Procedure get user input at runtime

Im doing a college assignment that requires me to create a PLSQL procedure that where the user can add a new customer order containing a number of items and the quantity for each item. I came up with the following that would ask the user to input a number of items to be added to the order and would then use a loop to ask for specific details such as product no and quantity. Im having problems with the user input at runtime though... When compiling the code it asks for the product code and quantity and wont ask again at runtime instead it saves the values given earlier at compile...
CREATE OR REPLACE
PROCEDURE Add_Order (Item_amount IN NUMBER, CustNo IN NUMBER) AS
ItemNo NUMBER;
var_Quantity NUMBER;
var_PONo NUMBER;
BEGIN
IF Item_amount BETWEEN 2 AND 9 THEN
SELECT seq_PONo.NEXTVAL INTO var_PONo from dual;
INSERT INTO PurchaseOrder_tab
SELECT var_PONo, REF(C),
SYSDATE,
ItemList_tab()
FROM Customer_tab C
WHERE C.CustNo = CustNo;
FOR i IN 1..Item_amount LOOP
DBMS_OUTPUT.PUT_LINE('INSIDE LOOP');
ItemNo := &Enter_ProdCode;
var_Quantity := &Quantity_Amount;
INSERT INTO TABLE (
SELECT P.ItemList
FROM PurchaseOrder_tab P
WHERE P.PONo = var_PONo
)
SELECT seq_ItemNo.nextval, REF(Pr), var_Quantity
FROM Products_tab Pr
WHERE Pr.ProductCode = ItemNo ;
DBMS_OUTPUT.PUT_LINE('Added '||var_Quantity ||' items of '||ItemNo||' to order No: '||var_PONo);
END LOOP;
ELSE
DBMS_OUTPUT.PUT_LINE('Amount of items entered onto an order must be between 2 - 9. Please try again with correct amount.');
END IF;
EXCEPTION
WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE('Operation failed '||'SQLCODE: '||SQLCODE);
DBMS_OUTPUT.PUT_LINE('SQL Error Message '||SQLERRM);
ROLLBACK;
END;
/
Short answer: you can't. PL/SQL is executed inside the database engine, and the database engine has no access to the terminal window (or database tool) you are using to start the procedure.
The code in your quesion seems to partially work, because it asks for input once, but what really happens is that: the tool (SQL*Plus, SQL Developer or whatever) parses over the PL/SQL block and sees the &-Signs, so it asks what to replace them with. Once the input is given, the PL/SQL-Block - including the entered values - is given to the database for execution.
Since you can't do that in PL/SQL, better create a front-end program first that collects the values, then sends them to the database.
Try to play around with :
BEGIN
DBMS_OUTPUT.GET_LINE(:buffer, :status);
END;
Instead of using & refferences
To Get data input from the USer,
SET SERVEROUTPUT ON;
ACCEPT Enter_ProdCode VARCHAR2 PROMPT "Please enter Product code : ";
ItemNo := &Enter_ProdCode;

Resources