Using parameters in procedures - plsql

I need to create a procedure that receives as a parameter a month and a year.
Inside the procedure I need to have a query to retrieve some values, that will have to take in account the parameters received.
create or replace procedure GET_REVS(MONTH in VARCHAR2,YEAR in varchar2) is
SELECT
*
FROM
REVENUS_TABLE
WHERE
Y_CODE IN ('YEAR')
AND M_CODE IN ()
Now, M_CODE should have the values since the start of the year until the month received by parameter.
Example if i receive in as parameter for the month a 4 i want my select to like this AND M_CODE IN ('1','2','3','4')
But if i receive MONTH = 3.. i need the select to have AND M_CODE IN ('1','2','3')
So what is the best way to do the procedure in order to be able to do that?
Thanks a lot

You could cast both M_CODE and MONTH as numbers and use BETWEEN operator or just <=:
CREATE OR REPLACE PROCEDURE get_revs(month IN VARCHAR2, year IN VARCHAR2) IS
BEGIN
....
SELECT *
FROM revenus_table
WHERE y_code = year
AND TO_NUMBER(m_code) BETWEEN 1 AND TO_NUMBER(month);
...
END;

You can do some thing like below example to make your procedure more dynamic
CREATE OR REPLACE PROCEDURE get_revs (
MONTH IN VARCHAR2,
YEAR IN VARCHAR2
)
IS
variable_name table_name%ROWTYPE;
v_sql VARCHAR2 (1000)
:= 'SELECT * FROM REVENUS_TABLE WHERE Y_CODE IN ('
|| MONTH
|| ')';
BEGIN
IF MONTH = 4 THEN
v_sql := v_sql || ' and M_CODE IN (''1'',''2'',''3'',''4'')';
END IF;
IF MONTH = 3 THEN
v_sql := v_sql || 'and M_CODE IN (''1'',''2'',''3'')';
END IF;
EXECUTE IMMEDIATE v_sql
INTO variable_name;
END;

Just an alternative to think about.
CREATE OR REPLACE PROCEDURE get_revs(month IN VARCHAR2, year IN VARCHAR2) IS
lv_in_clause VARCHAR2(100 CHAR);
p_ref sys_refcursor;
BEGIN
SELECT 'IN ('
||WMSYS.WM_CONCAT(A.NUM)
||')'
INTO lv_in_clause
FROM
(SELECT ''''
||LEVEL
||'''' NUM,
1 ID1
FROM DUAL
CONNECT BY LEVEL < to_number(MONTH)
)A
GROUP BY a.ID1;
OPEN p_ref FOR 'SELECT *
FROM revenus_table
WHERE y_code = '|| year
||' AND m_code '||lv_in_clause;
END;

Related

How to use for loop in sql developer or oracle

This is my code but while I'm running the project, it's showing error like
PLS-00306: wrong number or types of arguments in call to 'TEST_PROCEDURE'
I want to insert multiple records according given date.
CREATE OR REPLACE PROCEDURE TEST_PROCEDURE (
IP_START_DATE IN VARCHAR2,
IP_END_DATE IN VARCHAR2,
IP_MATERIAL_TYPE IN VARCHAR2,
IP_BRM_LIST IN VARCHAR2,
IP_ACTUAL_CONSUMPTION IN VARCHAR2,
IP_YARD_NO IN VARCHAR2,
IP_USER_ID IN VARCHAR2,
IP_USER_IP IN VARCHAR2,
IP_RID IN NUMBER,
IP_OPERATION IN VARCHAR2,
i OUT VARCHAR2,
OUT_RETURN_MSG OUT VARCHAR2,
OUT_RETURN_CODE OUT NUMBER)
IS
BEGIN
OUT_RETURN_MSG := '';
OUT_RETURN_CODE := 0;
BEGIN
IF IP_OPERATION = 'INSERT'
THEN
FOR i IN IP_START_DATE .. IP_END_DATE
LOOP
IF i <= IP_END_DATE
THEN
-- exit loop immediately
INSERT INTO MST_ACTUAL_CONSUMPTION (FROM_DATE,
TO_DATE,
MATERIAL_TYPE,
BRM_TYPE,
ACTUAL_CONSUMPTION,
YARD_NO,
USER_ID,
USER_IP_ADDRESS,
CREATED_DATE)
VALUES (IP_START_DATE,
IP_END_DATE,
IP_MATERIAL_TYPE,
IP_BRM_LIST,
IP_ACTUAL_CONSUMPTION,
IP_YARD_NO,
IP_USER_ID,
IP_USER_IP,
SYSDATE);
EXIT;
END IF;
END LOOP;
END IF;
IF IP_OPERATION = 'UPDATE'
THEN
UPDATE MST_ACTUAL_CONSUMPTION
SET FROM_DATE = IP_START_DATE,
TO_DATE = IP_END_DATE,
MATERIAL_TYPE = IP_MATERIAL_TYPE,
BRM_TYPE = IP_BRM_LIST,
ACTUAL_CONSUMPTION = IP_ACTUAL_CONSUMPTION,
YARD_NO = IP_YARD_NO,
LAST_UPD_TS = SYSDATE,
LAST_UPD_UID = IP_USER_ID
WHERE RID = IP_RID;
-- AND VESSEL_NAME = IP_VESSEL_NAME;
END IF;
OUT_RETURN_CODE := 1;
OUT_RETURN_MSG := 'SUCCESS';
EXCEPTION
WHEN OTHERS
THEN
OUT_RETURN_CODE := 0;
OUT_RETURN_MSG := SQLERRM;
END;
END TEST_PROCEDURE;
You need to select the days between start and end and use that for your loop counter instead of trying to use the variables directly as you currently are. Even though you're passing varchar2 with dates inside them, I still trunc the casted variable for safety to guarantee a whole number.
select trunc(to_date(ip_end_date)) - trunc(to_date(ip_start_date))
into loop_count
from dual;
Change your loop to count by this
if loop_count > 0 then
FOR i IN 1..loop_count loop
and I'm assuming you probably want to change the date for each iteration of the loop to match the day between start and end.
Firstly, you shouldn't be using varchar2 datatype for date datatypes. That is asking for trouble.
Now, since you want to insert records for multiple dates, you can't use for loop iterator for dates the way you do it for numbers as in for i in 1..10.
So, you could use a connect by as a method to generate dates. Use LEVEL pseudocolumn to refer to the current iteration (i in the for loop)
INSERT INTO mst_actual_consumption (
from_date,
TO_DATE, -- don't use this as a column_name as it's a function
material_type,
brm_type,
actual_consumption,
yard_no,
user_id,
user_ip_address,
created_date
)
SELECT
ip_start_date, -- you need to use to_date if you are passing date as strings
ip_end_date,
ip_material_type,
ip_brm_list,
ip_actual_consumption,
ip_yard_no,
ip_user_id,
ip_user_ip,
SYSDATE
FROM
dual
CONNECT BY
level <= ( ip_end_date - ip_start_date ); -- you need to use to_date if you are passing date as strings
I did not understand this part though
IF i <= IP_END_DATE
THEN
-- exit loop immediately
Maybe you were trying this? If so, put it before insert and update
IF ip_end_date <= ip_start_date
THEN
RETURN;
END IF;
EDIT you said:
error has fixed but no records are inserting even one time also
It could be because you are not passing correct dates.As I said in the code comments, you should convert them to dates using to_date function.
For eg: if you are passing the dates as strings in the format dd-mm-yyyy, In the insert, you should use
level <= ( to_date(ip_end_date,'dd-mm-yyyy') - to_date(ip_start_date,'dd-mm-yyyy') )
Same thing has to be done in the select part of insert, if the column in table MST_ACTUAL_CONSUMPTION is of date datatype.
select to_date(ip_start_date,'dd-mm-yyyy'), to_date(ip_end_date,'dd-mm-yyyy')
you can't loop through dates, you can probably do something like this.
create or replace procedure testproc(
startdate in date,
enddate in date
)
as
datediff NUMBER := enddate-startdate;
newdate date := startdate;
begin
FOR i IN 1..datediff LOOP
newdate := newdate+1;
insert into temp values(newdate);
END LOOP;
end;
and for your error it's because you are passing your parameters incorrectly like passing a string where argument requires integer value or you are passing incorrect number of parameters.
create or replace PROCEDURE PROC_ADD_ACTUALCONSUMPTION (
IP_START_DATE IN VARCHAR2,
IP_END_DATE IN VARCHAR2,
IP_MATERIAL_TYPE IN VARCHAR2,
IP_BRM_LIST IN VARCHAR2,
IP_ACTUAL_CONSUMPTION IN VARCHAR2,
IP_YARD_NO IN VARCHAR2,
IP_USER_ID IN VARCHAR2,
IP_USER_IP IN VARCHAR2,
IP_RID IN NUMBER ,
IP_OPERATION IN VARCHAR2,
--S_DATE OUT DATE,
--E_DATE OUT DATE,
--datediff OUT NUMBER,
OUT_RETURN_MSG OUT VARCHAR2,
OUT_RETURN_CODE OUT NUMBER)
IS
BEGIN
OUT_RETURN_MSG := '';
OUT_RETURN_CODE := 0;
-- S_DATE :=TO_DATE(IP_START_DATE,'DD-MM-YYYY HH24:MI:SS');
-- E_DATE :=TO_DATE(IP_END_DATE,'DD-MM-YYYY HH24:MI:SS');
-- datediff :=E_DATE-E_DATE;
BEGIN
IF IP_OPERATION = 'INSERT' THEN
INSERT INTO MST_ACTUAL_CONSUMPTION
(
FROM_DATE,
TO_DATE,
MATERIAL_TYPE,
BRM_TYPE,
ACTUAL_CONSUMPTION,
YARD_NO,
USER_ID,
USER_IP_ADDRESS,
CREATED_DATE
)
select
TO_CHAR((to_date(IP_START_DATE,'DD-MM-YYYY HH24:MI:SS')+ (level-1)),'DD-MM-YYYY') ,
TO_CHAR(to_date(IP_END_DATE,'DD-MM-YYYY HH24:MI:SS'),'DD-MM-YYYY') ,
IP_MATERIAL_TYPE,
IP_BRM_LIST,
IP_ACTUAL_CONSUMPTION,
IP_YARD_NO,
IP_USER_ID,
IP_USER_IP,
SYSDATE
FROM
dual
CONNECT BY
level <= to_date(IP_END_DATE,'DD-MM-YYYY HH24:MI:SS')-to_date(IP_START_DATE,'DD-MM-YYYY HH24:MI:SS')+1;
-- to_date(IP_START_DATE,'DD-MM-YYYY HH24:MI:SS'):=to_date(IP_START_DATE,'DD-MM-YYYY HH24:MI:SS')+1;
END IF;
END PROC_ADD_ACTUALCONSUMPTION;

Passsing two VARRAYS to a Procedure to pass them to a Index-By table

I try to pass two VARRAYs into a Procedure but when call it to test it I don't get any kind of respond from my database.
CREATE OR REPLACE PACKAGE PKG_TEST AS
TYPE PNO IS VARRAY(20) OF VARCHAR(20);
TYPE QTY IS VARRAY(20) OF INTEGER;
TYPE indexTest IS TABLE OF NUMBER INDEX BY VARCHAR2(20);
PROCEDURE blatest(i_PNO IN PNO, i_QTY IN QTY);
END PKG_TEST;
/
CREATE OR REPLACE PACKAGE BODY PKG_TEST AS
PROCEDURE blatest(i_PNO IN PNO , i_QTY IN QTY)
IS
V_COUNT_PNO INTEGER;
V_COUNT_QTY INTEGER;
bla_list indexTest;
name VARCHAR(20);
BEGIN
V_COUNT_PNO := i_PNO.COUNT;
V_COUNT_QTY := i_QTY.COUNT;
IF V_COUNT_PNO = V_COUNT_QTY THEN
FOR I IN 1..V_COUNT_PNO LOOP
bla_list(i_PNO(I)) := i_QTY(I);
END LOOP;
name := bla_list.FIRST;
WHILE name IS NOT null LOOP
dbms_output.put_line('Name: ' || name || ' is ' || TO_CHAR(bla_list(name)));
name := bla_list.NEXT(name);
END LOOP;
ELSE
dbms_output.put_line('Amount of Variables is not identical!');
END IF;
END blatest;
END PKG_TEST;
/
PKG_TEST.blatest(PKG_TEST.PNO('P123','P124'), PKG_TEST.QTY(2,3));
/
And if there is any easier way to fill in a Index-By table I dynamicly I am more than happy to read this ^^. Thanks in advance!
Call it as below, then you will get response:
set serveroutput on;
BEGIN
pkg_test.blatest (pkg_test.pno ('P123', 'P124'), pkg_test.qty (2, 3));
END;

PLSQL invalid number error after fetch

sorry if this is an confusing post and the style is not proper but this is my first question but im having an error after i want to fetch c_latlong :
the things he will fetch are:
LAT value : 50,9459744669855
LON value : 5,9704909091251
ORA-01722: invalid number
ORA-06512: at "DOMINOS.GETLATLONG", line 14
ORA-06512: at "DOMINOS.ZOEKWINKELVOORADRES2", line 34
ORA-06512: at line 9
This is the code i use:
create or replace FUNCTION getLatLong(v_postcode varchar2, v_huisnummer varchar2)
RETURN varchar2
IS
v_lat NVARCHAR2(38) ;
v_long NVARCHAR2(38);
v_postcode_id varchar2(200);
v_all varchar2(200);
CURSOR c_latlong IS
SELECT LAT, LON
FROM POSTCODE
WHERE POSTCODE = v_postcode AND v_huisnummer BETWEEN MINNUMBER AND MAXNUMBER;
BEGIN
OPEN c_latlong;
FETCH c_latlong INTO v_lat, v_long;
v_all := v_lat || '-' || v_long;
-- print(v_all);
RETURN v_all;
CLOSE c_latlong;
END getLatLong;
create or replace FUNCTION zoekWinkelVoorAdres2(v_postcode varchar2, v_huisnummer varchar2)
RETURN varchar2
IS
v_array_this apex_application_global.vc_arr2 := apex_util.string_to_table(getLatLong(v_postcode,v_huisnummer), '-');
v_temp_postcode varchar2(200);
v_temp_huisnummer varchar2(200);
v_temp_id varchar2(200);
v_temp_winkel apex_application_global.vc_arr2;
v_array dbms_sql.varchar2_table;
v_max number;
v_closest_winkel varchar2(200);
v_closest_dis varchar2(200);
CURSOR c_all_stores_max IS
SELECT count(*)
FROM WINKEL;
CURSOR c_all_stores IS
SELECT ID, POSTCODE, HUISNR
FROM WINKEL;
CURSOR c_select_dis IS
SELECT WINKEL_ID, DISTANCE
FROM allwinkeldis ORDER BY DISTANCE DESC;
BEGIN
--DELETE FROM allwinkeldis;
OPEN c_all_stores_max;
FETCH c_all_stores_max INTO v_max;
CLOSE c_all_stores_max;
OPEN c_all_stores;
FOR i IN 1..v_max
LOOP
FETCH c_all_stores INTO v_temp_id, v_temp_postcode, v_temp_huisnummer;
-- print('-------------------1---------------------' ||v_temp_postcode);
print(getLatLong(v_temp_postcode,v_temp_huisnummer));
v_temp_winkel :=apex_util.string_to_table(getLatLong(v_temp_postcode,v_temp_huisnummer), '-');
-- print(v_temp_id || ' ' || v_temp_winkel(1) || ' ' || v_temp_winkel(2));
-- print('-------------------2---------------------');
--v_array(i) := v_temp_id ||','||distance(v_array_this(2), v_array_this(3),v_temp_winkel(2), v_temp_winkel(3));
INSERT INTO allwinkeldis(winkel_id, distance) VALUES (v_temp_id, distance(v_array_this(1), v_array_this(2), v_temp_winkel(1), v_temp_winkel(2)));
--print(v_temp_id ||' -------------- '||distance(v_array_this(2), v_array_this(3), v_temp_winkel(2),v_temp_winkel(3)));
END LOOP;
CLOSE c_all_stores;
OPEN c_select_dis;
FETCH c_select_dis INTO v_closest_winkel, v_closest_dis;
CLOSE c_select_dis;
print(v_closest_winkel || '--------------' || v_closest_dis);
END zoekWinkelVoorAdres2;
I'm not sure what is the data type of LAT and LON in your table POSTCODE.
But whatever variables you define to fetch data into, they must match these types.
For examples, assuming that LAT and LON are of the type NVARCHAR2, then I would rewrite your declaration section as follows:
CREATE OR REPLACE FUNCTION getLatLong(v_postcode varchar2, v_huisnummer varchar2)
RETURN varchar2
IS
v_postcode_id varchar2(200);
v_all varchar2(200);
CURSOR c_latlong IS
SELECT LAT, LON
FROM POSTCODE
WHERE POSTCODE = v_postcode AND v_huisnummer BETWEEN MINNUMBER AND MAXNUMBER;
TYPE temp_nvarchar2 IS TABLE OF NVARCHAR2(38);
v_lat temp_nvarchar2;
v_long temp_nvarchar2;
BEGIN
....
The invalid number error occurs when it expects a number and gets something else instead, like a varchar.
v_lat and v_lon are declared as NVARCHAR2. Is this the same datatype that is stored in POSTCODE? If not, this is the cause of the error.
It might also be having trouble with the line v_all := v_lat || '-' || v_long;. If v_lat or v_long are numbers, then you need to cast them to chars instead, like so:
v_all := TO_CHAR(v_lat) || '-' || TO_CHAR(v_long);

I'm using a collection to compare with i/p parameter ,but the last index of the collection is not getting compared

create or replace procedure save_pod_place_tag1 ( p_START_DATE in varchar2,
p_END_DATE in varchar2,
p_USER_ID in number,
p_TAG_ID in out number,
p_status out varchar2,
p_status_dtl out varchar2) is
v_rec_cnt number;
v_ID Number;
v_CNT Number;
type l_START_DATE is table of varchar2(100);
type l_END_DATE is table of varchar2(100);
v_st_dt l_START_DATE := l_START_DATE();
v_ed_dt l_END_DATE := l_END_DATE();
begin
v_ID := pod_unique_val_seq.nextval;
select to_char(start_date,'dd-mon-yyyy'),to_char(end_date,'dd-mon-yyyy')
bulk collect into v_st_dt,v_ed_dt
from pod_place_tag_tb;
for i in v_st_dt.first .. v_st_dt.count loop
if nvl(v_st_dt(i),chr(0)) < nvl(p_START_DATE,chr(0)) or nvl(v_st_dt.last,chr(0)) < nvl
(p_START_DATE,chr(0)) then
dbms_output.put_line('START_DATES are' ||' ' ||v_st_dt(i));
dbms_output.put_line('INSERTION IS ALLOWED');
else
dbms_output.put_line('INSERTION NOT ALLOWED');
end if;
end loop;
The last start _date is not getting compared and I get the o/p insertion not possible even i am passing proper p_START_DATE
I think this in roots is wrong... you cant get compare on varchar2 type expecting result as date, because varchar2 orders in other way.
example
select to_char(sysdate+level,'dd-mon-yyyy') dates from dual connect by level<=100 order by dates;
Result
01-aug-2014
01-jul-2014
01-jun-2014
01-sep-2014
02-aug-2014
02-jul-2014
02-jun-2014
03-aug-2014
03-jul-2014
03-jun-2014
04-aug-2014
04-jul-2014
04-jun-2014
...
select trunc(sysdate)+level dates from dual connect by level<=100 order by dates ASC
result
2014/05/25/
2014/05/26/
2014/05/27/
2014/05/28/
...
check anonimos block
declare
v_date1 date;
v_date2 date;
v_check_date boolean;
v_char_date1 varchar2(200);
v_char_date2 varchar2(200);
v_check_char_date boolean;
v_start_date date:=to_date('01.01.2014','dd.mm.yyyy');
v_count pls_integer:=0;
begin
for i IN 1 .. 300
LOOP
v_char_date1:=to_char(v_start_date+i,'dd-mon-yyyy');
v_date1 :=v_start_date+i;
for i1 IN 1..300
LOOP
v_char_date2:=to_char(v_start_date+i1,'dd-mon-yyyy');
v_date2 :=v_start_date+i1;
IF v_date1<v_date2 THEN
v_check_date:=true;
ELSIF v_date1>v_date2 THEN
v_check_date:=false;
END IF;
IF v_char_date1<v_char_date2 THEN
v_check_char_date:=true;
ELSIF v_char_date1>v_char_date2 THEN
v_check_char_date:=false;
END IF;
IF (v_check_char_date AND not v_check_date) THEN
dbms_output.put_line ('v_date1='||v_date1||' > '||'v_date2='||v_date2||' AND '||'v_char_date1='||v_char_date1||' < '||'v_char_date2='||v_char_date2);
exit;
ELSIF (not v_check_char_date AND v_check_date) THEN
dbms_output.put_line ('v_date1='||v_date1||' < '||'v_date2='||v_date2||' AND '||'v_char_date1='||v_char_date1||' > '||'v_char_date2='||v_char_date2);
exit;
END IF;
v_count:=v_count+1;
END LOOP;
IF (v_check_char_date AND not v_check_date) OR (not v_check_char_date AND v_check_date) THEN
exit;
END IF;
END LOOP;
dbms_output.put_line ('count='||v_count);
end;

PL/SQL functions [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Homework on PL/SQL FUNCTIONS
plsql functions
Function:
function to Display_Employee_Name_In_Uppercase that accepts the Employee_ID from the Empoyees table and returns the first and the last name of the employee in uppercase.
Write a small PL/SQL program to display the names of the employees whose Employee_IDs are 107, 200 and 205.
this is what I have done I didnt know how to complete it
can help ?
CREATE OR REPLACE FUNCTION disp (emp_id in varchar20) return emp_name
select into emp_name
fname||lname
from employees
where employee_id=emp_id
END disp ;
Something like this...
CREATE OR REPLACE
FUNCTION Fn_Display(p_empId IN VARCHAR2)
RETURN VARCHAR2
IS
empName VARCHAR2(100);
BEGIN
BEGIN
SELECT UPPER(first_name || ' ' || last_name)
INTO empName
FROM Employees
WHERE employee_id = p_empId;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE NO_DATA_FOUND
END;
RETURN empName;
END Fn_Display;
You can call this function wherever you want. here is a sample...
DECLARE
empId VARCHAR2(100);
empName VARCHAR2(100);
BEGIN
empId := &ENTER_EMPLOYEE_ID;
empName := Fn_Display(empId);
DBMS_OUTPUT.PUT_LINE('Employee Name: '||empName);
END;
You could try this code, maybe this one works for you:
CREATE OR REPLACE FUNCTION disp (emp_id in varchar2) return varchar2 IS
emp_name varchar2(256);
BEGIN
select UPPER(fname || ' ' || lname)
into emp_name
from employees
where employee_id = emp_id;
return emp_name;
END disp;

Resources