Im trying to make a procedure in oracle with PL/SQL. This proc has to call a function with a value return and then update a colown with this value. My proc is executing normaly but the insertion to try the proc are not working. Im a bit lost.
The proc :
PROCEDURE depot_enrichment(babas number) IS
-- Curseur
CURSOR V_CURS_CDIS_CTC IS SELECT DISTINCT CD_REGATE,
OPT_SITE_DEPOT,
TYP_PARUTION_DECLA,
NUM_PUBLICATION_DECLA,
CD_POSTAL,
ID_LIB_PREP
FROM DEPOT_IMPORT
WHERE OPT_SITE_DEPOT = 81001210 OR OPT_SITE_DEPOT = 81001211;
-- Variables proc
p_opt_site varchar2(40); -- DepotCDIS/DepotCTC
-- Variables parameters function
v_CodeSiteDepot VARCHAR2(40); ----- 531980
v_OptionSiteDepot VARCHAR2(40); ---- 81001210/81001211
v_TypeParution VARCHAR2(40); ---- N1, N2, N3 ..........
v_NoPublication VARCHAR2(40);---- 1222K82523
v_codePostal VARCHAR2(40); ----- 55000
v_NiveauPrepa VARCHAR2(40); ---- 50001,50002,5003
-- Libelle parameters function
v_LibOptionSiteDepot VARCHAR2(40); ---- DepotCDIS/DepotCTC
v_LibTypeParution VARCHAR2(40); ---- NUM , FCS ..........
v_LibNiveauPrepa VARCHAR2(40); ---- ATTF, LATD, LDCP, LDQL ou PEM
BEGIN
-----------------------------------------------------------------------------------------------------
-- SUB-PROC OPT SIT DEPOT ENRICHIMENT - ENR4 :détermination de OptionSiteDepot pour les produits Presse
-- Enrichment of the opt_site_depot - JIRA : 1003
-----------------------------------------------------------------------------------------------------
-- option DepotCDIS / CTC
OPEN V_CURS_CDIS_CTC;
LOOP
fetch V_CURS_CDIS_CTC into v_CodeSiteDepot,v_OptionSiteDepot,v_TypeParution,v_NoPublication,v_codePostal,v_NiveauPrepa;
EXIT WHEN V_CURS_CDIS_CTC%notfound;
Insert into babas values ('Parametres : ' || v_CodeSiteDepot || ' / ' || v_OptionSiteDepot || ' / ' || v_TypeParution || ' / ' || v_NoPublication || ' / ' || v_codePostal || ' / ' || v_NiveauPrepa);
-- GET LIBELLE FROM PARAMETERS VARIABLES
SELECT LIB.LIB_LIBELLE INTO v_LibOptionSiteDepot FROM LIBELLE LIB WHERE typ_libelle = 'OSD' and id_libelle = v_CodeSiteDepot;
SELECT TYP.LIB_PARUTION INTO v_LibTypeParution FROM TYPE_PARUTION TYP WHERE id_type_parution = v_TypeParution;
SELECT LIB.VAL_LIBELLE INTO v_LibNiveauPrepa FROM LIBELLE LIB WHERE typ_libelle = 'PEC' and id_libelle = v_NiveauPrepa;
Insert into babas values ('Parametres libelles: ' || v_CodeSiteDepot || ' / ' || v_LibOptionSiteDepot || ' / ' || v_LibTypeParution || ' / ' || v_NoPublication || ' / ' || v_codePostal || ' / ' || v_LibNiveauPrepa);
-- CALL FUNCTION
p_opt_site := return_option_CDIS_CTC(v_CodeSiteDepot,v_LibOptionSiteDepot,v_LibTypeParution,v_NoPublication,v_codePostal,v_LibNiveauPrepa);
Insert into babas values ('option site dépot : ' || p_opt_site);
-- UPDATE ROWS
UPDATE DEPOT_IMPORT SET OPT_SITE_DEPOT = p_opt_site;
END LOOP;
CLOSE V_CURS_CDIS_CTC;
END depot_enrichment;
When i call the proc :
what is inserted in babas table :
I've the impression the modification code into libelle ( the 3 select ) is not working, i try the query alone and it returns me the right value. I dont understand, if you have any documentions or advises, will be really cool :)
Commit is necessary to be placed at the end of the procedure to reflect the changes.
....
....
UPDATE DEPOT_IMPORT SET OPT_SITE_DEPOT = p_opt_site;
END LOOP;
COMMIT; -- this is needed here
CLOSE V_CURS_CDIS_CTC;
There are 3 insert into babas statements within the loop, but - according to output you get, only the first one:
Insert into babas values ('Parametres : ' ...
does its job. Where are other two?
When calling the procedure, you specified owner name:
call sagapec_import.depot_enrichment(0);
--------------
this
Is the babas table owned by that user? Are you connected as sagapec_import? Because, maybe you're executing procedure in one schema and check the result in another.
Finally, this:
UPDATE DEPOT_IMPORT SET OPT_SITE_DEPOT = p_opt_site;
doesn't contain WHERE clause which means that you'll every time update all rows in that table. Is that correct?
Related
I created the following PL/SQL anonymous block. The cursor below retrieves data from the select statement:
select mod_benutzer, count(*)
from dok_auspraegung
where parent_objekt_id = 1093
group by mod_benutzer;
This statement displays exactly two records:
DDMS_USER | 8
HU2MAMU | 14
But when I want to display these two records by cursor, it displays "HU2MAMU|14" two times like below:
Modifications:
DDMS_USER, 8x
HU2MAMU, 14x
HU2MAMU, 14x
declare
my_exception_1 exception;
var_parent_objekt_id dok_auspraegung.parent_objekt_id%TYPE := 1093;
var_date varchar(30);
var_mod_benutzer varchar2(10);
var_benutzer_modifs number;
cursor cursor_dok_auspraegung
is select mod_benutzer, count(*) from dok_auspraegung
where parent_objekt_id = 10935797565
group by mod_benutzer;
begin
select distinct to_char(mod_datum,'YYYY-MON-DD') into var_date from dok_auspraegung where parent_objekt_id = var_parent_objekt_id;
IF var_date is not null THEN
dbms_output.put_line('Parent Object ID' || ': ' || var_parent_objekt_id);
dbms_output.put_line('Date: ' || ' ' || var_date);
ELSE RAISE my_exception_1;
END IF;
open cursor_dok_auspraegung;
dbms_output.put_line('Modifications:');
loop
fetch cursor_dok_auspraegung into var_mod_benutzer, var_benutzer_modifs;
dbms_output.put(var_mod_benutzer);
dbms_output.put_line(', ' || var_benutzer_modifs || 'x');
exit when cursor_dok_auspraegung%notfound;
end loop;
dbms_output.put_line(cursor_dok_auspraegung%rowcount);
close cursor_dok_auspraegung;
exception
when NO_DATA_FOUND then
dbms_output.put_line('Parent Object ID not found!');
when my_exception_1 then
dbms_output.put_line('');
end;
What is the reason of that?
Because exiting from the cursor occurs after printing the value of the variables in the current case, this repeats the last value to be printed. So, it should occur before printing as follows
loop
fetch cursor_dok_auspraegung into var_mod_benutzer, var_benutzer_modifs;
exit when cursor_dok_auspraegung%notfound;
dbms_output.put(var_mod_benutzer);
dbms_output.put_line(', ' || var_benutzer_modifs || 'x');
end loop;
I have created a package with two procedures and two cursors in it, but while executing the procedure, it is executed successful, but same record executed multiple times and a buffer overflow occurred.
I also tried removing the loop from the cursor but for 1 record that will be fine and for multiple record it won't work as expected.
EXPECTED
I just need to remove multiple execution of same record from the procedure where i am getting multiple execution of same record
for single procedure and single cursor it is working properly but for multiple cursor and multiple procedure i am getting problem here which caused buffer overflow too where i need different record
Is there any alternative way that I can fix the problem ?
CREATE OR REPLACE PACKAGE test.report AS
PROCEDURE distribution (
code_in IN user.test.code%TYPE,
fromdate date,
todate date
);
PROCEDURE tdvalue (
id IN user.test.custid%TYPE
);
END report;
/
Package Body
CREATE OR REPLACE PACKAGE BODY test.report as
----------VARIABLE DECLARATION----------------
code_in user.test.code%TYPE;
custidin user.test.custid%TYPE;
fromdate DATE;
todate DATE;
diff number(17,2);
---------------CURSOR DECLARATION--------------
CURSOR td_data(code_in user.test.code%TYPE,
fromdate date,
todate date
) IS
( SELECT
test.code,
COUNT(test.code) AS count,
SUM(test2.Deposit_amount) AS total,
test.currency
FROM
user.test2
JOIN user.test ON test2.acid = test.acid
WHERE
user.test2.open_effective_date BETWEEN TO_DATE(fromdate, 'dd-mm-yyyy') AND TO_DATE(todate, 'dd-mm-yyyy')
and
user.test.code = code_in
GROUP BY
test.code,test.currency
);
td__data td_data%rowtype;
CURSOR C_DATA(custidin user.test.custid%TYPE) IS SELECT
test.custid,
test2.id,
TO_DATE(test2.initial_date, 'dd-mm-yyyy') - TO_DATE(test2.end_date, 'dd-mm-yyyy') AS noofdays,
round(((test2.deposit_amount *((TO_DATE(test2.initial_date, 'dd-mm-yyyy') - TO_DATE(test2.end_date, 'dd-mm-yyyy'
)) / 365) * test4.interest_rate) / 100), 2) + test2.deposit_amount AS calculated_amount,
SUM(test.flow_amt) + test2.deposit_amount AS system_amount
FROM
user.test
JOIN user.test2 ON test3.entity_id = test2.id
WHERE
test.custid = custidin
GROUP BY
test.custid,
test2.id;
c__data c_data%ROWTYPE;
PROCEDURE distribution
(
code_in IN user.test.code%TYPE,
fromdate in date,
todate in date
)
AS
BEGIN
OPEN td_data(code_in,fromdate,todate);
loop
FETCH td_data INTO td__data;
dbms_output.put_line(td__data.code
|| ' '
|| td__data.count
|| ' '
||td__data.currency
||' '
||td__data.total
);
end loop;
CLOSE td_data;
END distribution;
PROCEDURE tdvalue (
custidin IN user.test.custid%TYPE
)
AS
BEGIN
open c_data(custidin);
fetch c_data into c__data;
loop
diff:= c__data.calculated_amount- c__data.system_amount;
dbms_output.put_line(c__data.custid
|| ' '
|| c__data.noofdays
|| ' '
|| c__data.end_date
|| ' '
|| c__data.initial_date
|| ' '
|| c__data.calculated_amount
||' '
||diff
);
end loop;
close c_data;
END tdvalue;
END report;
/
To run
ALTER SESSION set nls_date_format='dd-mm-yyyy';
SET SERVEROUTPUT ON;
EXEC REPORT.DISTRIBUTION('872328','01-02-2016','08-02-2019');
/
EXEC REPORT.tdvalue('S9292879383SS53');
Buffer overflow - ORU-10027 - happens when the total number of bytes displayed through DBMS_OUTPUT exceeds the size of the serveroutput buffer. The default is only 20000 bytes (who knows why?). Your session is using that default because of how you enable serveroutput. Clearly one record is less than 2000 and you only hit that limit when you run for multiple records.
To fix this try this
SET SERVEROUTPUT ON size unlimited
It's not actually unlimited, but the upper bound is the PGA limit (session memory) and you really shouldn't hit that limit with DBMS_OUTPUT. Apart from anything else who would read all that?
So the other problem with your code - as #piezol points out - is that your loops have no exit points. You should test whether the FETCH actually fetched anything and exit if it didn't:
loop
FETCH td_data INTO td__data;
exit when td_data%notfound;
dbms_output.put_line(td__data.code
|| ' '
|| td__data.count
|| ' '
||td__data.currency
||' '
||td__data.total
);
end loop;
Remembering to do this is just one reason why implicit cursors and cursor for loops are preferred over explicit cursors.
The second cursor loop is even worse because not only does it not have an exist point, the fetch is outside the loop. That's why you have repeated output for the same record.
So let's rewrite this ...
open c_data(custidin);
fetch c_data into c__data; -- should be inside
loop
diff:= c__data.calculated_amount- c__data.system_amount;
… as a cursor for loop:
PROCEDURE tdvalue (
custidin IN user.test.custid%TYPE
)
AS
BEGIN
for c__data in c_data(custidin)
loop
diff:= c__data.calculated_amount- c__data.system_amount;
dbms_output.put_line(c__data.custid
|| ' '
|| c__data.noofdays
|| ' '
|| c__data.end_date
|| ' '
|| c__data.initial_date
|| ' '
|| c__data.calculated_amount
||' '
||diff
);
end loop;
END tdvalue;
No need for OPEN, CLOSE or FETCH, and no need to check when the cursor is exhausted.
In PL/SQL, the preferred mechanism for setting the DBMS_OUTPUT buffer size would be within your procedure. This has the benefit of working in any client tool, such as Java or Toad (though it is still up to the client tool to retrieve the output from DBMS_OUTPUT).
DBMS_Output.ENABLE
Pass in a parameter of NULL for unlimited buffer size.
It would go like this:
BEGIN
DBMS_OUTPUT.ENABLE(NULL);
FOR I IN 1..1000 LOOP
DBMS_OUTPUT.PUT_LINE('The quick red fox jumps over the lazy brown dog.');
END LOOP;
END;
/
Bonus fact:
You can use the other functions and procedures in DBMS_OUTPUT to roll your own if you aren't using SQL*Plus or a DBMS_OUTPUT-savvy tool like Toad.
You can use the GET_LINE or GET_LINES procedures from your client code to get whatever may have been written to DBMS_OUTPUT.
GET_LINE
I need to create a procedure that allows a user to input information for an order (which is below), but also for n number of suborders (where 2 ≤ n ≥ 10). How would I be able to incorporate this into the procedure I already have? I'm using SQLPlus. Any help or advice would be greatly appreciated. :)
CREATE OR REPLACE PROCEDURE VIEW_ORDER(ORDERS IN CHAR)
AS
CURSOR ORDER_CUR IS
SELECT * FROM SUBORDERS
WHERE ORDER_NO = ORDERS;
BEGIN
FOR O_REC IN ORDER_CUR
LOOP
DBMS_OUTPUT.PUT_LINE('Product Code: ' || O_REC.prod_id
|| ' Order: ' || O_REC.order_no
|| ' Suborder: ' || O_REC.suborder_no
|| ' Quantity: ' || O_REC.quantity);
END LOOP;
EXCEPTION
WHEN no_data_found THEN
DBMS_OUTPUT.PUT_LINE ('Product number does not exist');
WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE ('Operation failed ' || 'SQLCODE: ' || SQLCODE);
ROLLBACK;
END;
/
You can use for an input sqlplus command ACCEPT and make another loop to take n suborders.
If I didn't missunderstood your question it would be something like this:
-- You have to create the input outside the procedure
accept n char prompt 'Please enter n suborders: '
CREATE OR REPLACE PROCEDURE VIEW_ORDER(ORDERS IN CHAR)
AS
CURSOR ORDER_CUR IS
SELECT * FROM SUBORDERS
WHERE ORDER_NO = ORDERS;
BEGIN
FOR O_REC IN ORDER_CUR
LOOP
-- Here we check if n<=2
IF n>=2 THEN
-- here if n<=10
WHILE n<=10
LOOP
DBMS_OUTPUT.PUT_LINE('Product Code: ' || O_REC.prod_id
|| ' Order: ' || O_REC.order_no
|| ' Suborder: ' || O_REC.suborder_no
|| ' Quantity: ' || O_REC.quantity);
END LOOP;
END IF;
END LOOP;
EXCEPTION
WHEN no_data_found THEN
DBMS_OUTPUT.PUT_LINE ('Product number does not exist');
WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE ('Operation failed ' || 'SQLCODE: '
||
SQLCODE);
ROLLBACK;
END;
/
This is my attempt in creating a cursor in a stored procedure
--Second Stored Procedure--
CREATE OR REPLACE PROCEDURE sp_GetDiscountedRate (DiscountCode IN
VARCHAR2,Percentage IN NUMBER, ReserveDate IN DATE)
IS --Code declaration section--
--variables to store column values returned from select into
pPassengerID VARCHAR2(10);
pFirst VARCHAR2(20);
pMiddle VARCHAR2(20);
pLast VARCHAR2(20);
pPassengerType CHAR (1);
uUSMilitaryID VARCHAR (8);
uMilitaryBranch VARCHAR2 (20);
uMilitaryDiscountCode VARCHAR2(8);
rFlightNumber VARCHAR2(6);
rReservationCost NUMBER(6);
rReservationDate DATE;
--Declare Cursor
CURSOR cur_USMilitary IS
--Query cursor will point to results
SELECT P.PassengerID, P.First, P.Middle, P.Last, P.PassengerType,
U.USMilitaryID, U.MilitaryBranch,
U.MilitaryDiscountCode, R.FlightNumber, R.ReservationCost,
R.ReservationDate,
CASE U.MilitaryDiscountCode WHEN DiscountCode THEN
Percentage*R.ReservationCost
ELSE R.ReservationCost END "REVISED_RESERVATION_COST"
FROM PASSENGER P, US_Military U, RESERVATION R
WHERE P.PassengerID = U.MPassengerID
AND P.PassengerID = R.PassengerID
AND U.MilitaryDiscountCode = DiscountCode
AND R.ReservationDate = ReserveDate;
--Start Execution section--
BEGIN
--Open Cursor
OPEN cur_USMilitary; -- open cursor for use
--loop to display each record returned by cursor
--Use PL/SQL language control or loop to display each record pointed by cursor
LOOP
--Fetch cursor data
FETCH cur_USMilitary INTO pPassengerID,
pFirst,pMiddle,pLast,pPassengerType,
uUSMilitaryID,uMilitaryBranch,uMilitaryDiscountCode,rFlightNumber,
rReservationCost,rReservationDate;
EXIT WHEN cur_USMilitary%NOTFOUND;
--Display each record
--Displaying the results
DBMS_OUTPUT.PUT_LINE ('CUSTOMER INFORMATION: ');
DBMS_OUTPUT.PUT_LINE ('The PassengerID is: ' ||pPassengerID);
DBMS_OUTPUT.PUT_LINE ('First Name of passenger is: ' ||pFirst);
DBMS_OUTPUT.PUT_LINE ('Middle Name of passenger is: ' ||pMiddle);
DBMS_OUTPUT.PUT_LINE ('Last Name of passenger is: ' ||pLast);
DBMS_OUTPUT.PUT_LINE ('Passenger Type of customer is: ' ||pPassengerType);
DBMS_OUTPUT.PUT_LINE ('US Military ID of Passenger is: ' ||uUSMilitaryID);
DBMS_OUTPUT.PUT_LINE ('Military Branch of passenger is: ' ||uMilitaryBranch);
DBMS_OUTPUT.PUT_LINE ('Military Discount code of passenger is: ' ||uMilitaryDiscountCode);
DBMS_OUTPUT.PUT_LINE ('Flight number of passenger is: ' ||rFlightNumber);
DBMS_OUTPUT.PUT_LINE ('Reservation Cost of passenger is: ' ||rReservationCost);
DBMS_OUTPUT.PUT_LINE ('Reservation Date of passenger is: ' ||rReservationDate);
END LOOP;
CLOSE cur_USMilitary; --close cursor
END sp_GetDiscountedRate;
I get this error:
Error(36,9): PL/SQL: SQL Statement ignored
Error(36,9): PLS-00394: wrong number of values in the INTO list of a FETCH statement
I fully understand the error. I checked the number of columns and it looks like to me that they match the number of columns within the query. I've also checked the datatypes to make sure it was correct.
You have
12 columns in your cursor's SELECT part
but
11 columns in FETCH statement part
, i observe
CASE U.MilitaryDiscountCode WHEN DiscountCode THEN
Percentage*R.ReservationCost
ELSE R.ReservationCost END "REVISED_RESERVATION_COST"
part is missing in FETCH( or should be omitted in the SELECT part ).
It also represents a column, and it spoils 1-1 correspondence, which yields that error.
This is what happens when you overcomplicate things, I'm afraid. I don't see any need for all those variables and the corresponding fetch into that is hard to keep in sync. Why not just:
create or replace procedure sp_getdiscountedrate
( discountcode in varchar2
, percentage in number
, reservedate in date )
is
begin
for r in (
select p.passengerid
, p.first
, p.middle
, p.last
, p.passengertype
, u.usmilitaryid
, u.militarybranch
, u.militarydiscountcode
, r.flightnumber
, r.reservationcost
, r.reservationdate
, case u.militarydiscountcode
when discountcode then percentage * r.reservationcost
else r.reservationcost
end as revised_reservation_cost
from passenger p
join us_military u
on u.mpassengerid = p.passengerid
join reservation r
on r.passengerid = p.passengerid
and r.discountcode = u.militarydiscountcode
and r.reservedate = r.reservationdate
)
loop
dbms_output.put_line('CUSTOMER INFORMATION:');
dbms_output.put_line('The PassengerID is: ' || r.passengerid);
dbms_output.put_line('First Name of passenger is: ' || r.first);
dbms_output.put_line('Middle Name of passenger is: ' || r.middle);
dbms_output.put_line('Last Name of passenger is: ' || r.last);
dbms_output.put_line('Passenger Type of customer is: ' || r.passengertype);
dbms_output.put_line('US Military ID of Passenger is: ' || r.usmilitaryid);
dbms_output.put_line('Military Branch of passenger is: ' || r.militarybranch);
dbms_output.put_line('Military Discount code of passenger is: ' || r.militarydiscountcode);
dbms_output.put_line('Flight number of passenger is: ' || r.flightnumber);
dbms_output.put_line('Reservation Cost of passenger is: ' || r.reservationcost);
dbms_output.put_line('Reservation Date of passenger is: ' || r.reservationdate);
dbms_output.put_line('Revised reservation cost is: ' || r.revised_reservation_cost);
end loop;
end sp_getdiscountedrate;
(Untested)
You go through all the trouble of calculating "REVISED_RESERVATION_COST", and then you don't read it from the cursor.
No wonder Oracle is complaining. You need a variable for this column as well.
I would also advise you to learn to use modern, proper, explicit JOIN syntax.
This might be a simple query for most of you.
I would like to use a variable in nested query while writing an execute immediate, but somehow it throws an error everytime on the variable in nested query.
create or replace PROCEDURE p_decrement_id(
in_table VARCHAR2,
in_seg_nme VARCHAR2)
AS
version VARCHAR2(5) := 'v1.0';
BEGIN
EXECUTE IMMEDIATE 'UPDATE '|| in_table || '
SET X_VERSION_ID = X_VERSION_ID - 1
where DATE_1 = (SELECT DATE_2 FROM CYCLE_DATES
WHERE SEG_NME = ' ||in_seg_nme||')';
COMMIT;
END p_decrement_id;
for execution I use:
set serveroutput on
call p_decrement_id('TEST_DECREMENT', 'TEST');
Also, I need to put a constant value within the query
i.e.
SELECT DATE_2 FROM CYCLE_DATES
WHERE SEG_NME = ' ||in_seg_nme||'
and indicator = 'Y'
Please guide.
Thank you all in advance
It's better to use USING to bind params to your query
EXECUTE IMMEDIATE 'UPDATE '|| in_table || '
SET X_VERSION_ID = X_VERSION_ID - 1
where DATE_1 = (SELECT DATE_2 FROM CYCLE_DATES
WHERE SEG_NME = :1 and indicator = ''Y'')' using in_seg_nme;
Without USING you may try this (as it's VARCHAR2 you need '):
EXECUTE IMMEDIATE 'UPDATE '|| in_table || '
SET X_VERSION_ID = X_VERSION_ID - 1
where DATE_1 = (SELECT DATE_2 FROM CYCLE_DATES
WHERE SEG_NME = ''' ||in_seg_nme||''' and indicator = ''Y'')';
(Two consequent apostrophies is an escape sequence for apostrophe)