Get result set in plsql - plsql

am wondering if there already some rewriting suggestions to get functions such generating recurrence dates between two dates - generate_recurrences() from this link for recurrency recurrency events
in plsql? it returns a setof date, but in plsql i can't figure out how to get a resultset for dates and looping return next next_date, where next also returns a next date on a list.
I tried it to rewrite it in plsql but with only return of one date, because i can't find out how to return a resultset in plsql, that what i've tried:
CREATE OR REPLACE FUNCTION GENERATE_RECURRENCE( rec in VARCHAR2,
start_date in TIMESTAMP,
end_date in TIMESTAMP )
RETURN TIMESTAMP
IS
next_date TIMESTAMP := start_date;
duration INTERVAL DAY TO SECOND;
day INTERVAL DAY TO SECOND;
BEGIN
IF recurs = 'none' THEN
return next_date;
elsif recurs = 'daily' then
duration := INTERVAL '1' DAY ;
while next_date <= end_date loop
return next_date + duration;
END IF;
END;

I wrote the following pipelined function a while ago. It's not exactly what you're asking for, but it gives you a resultset that's a range of dates, so you should be able to match it to your needs.
It requires you to create a type object to hold the return value and I used an existing object instead of creating a custom one. So you should modify this to use an object just big enough (and use date type instead of string). But the functionality does what you're asking for.
Enjoy!
CREATE OR REPLACE FUNCTION date_range_stream(start_date_in IN DATE,
end_date_in IN DATE) RETURN rpt_results_10_obj_type_type
DETERMINISTIC
PIPELINED IS
/*
Parameters: start_date_in - First date to return (truncated)
end_date_in - Last date to return, inclusive
Results: date string formatted as MM/DD/YYYY
Author: Stew Stryker
Usage: SELECT to_date(text01, 'MM/DD/YYYY') AS a_date
FROM TABLE(aeo.aeo_misc_tools.date_range_stream('01-MAR-2009', SYSDATE))
Returns a rows from starting date to current
Requires the definition of the following object:
CREATE OR REPLACE TYPE rpt_results_10col_obj AS OBJECT
( seq_num NUMBER,
place VARCHAR2(20),
rep_info VARCHAR2(20),
text01 VARCHAR2(512),
text02 VARCHAR2(512),
text03 VARCHAR2(512),
text04 VARCHAR2(512),
text05 VARCHAR2(512),
text06 VARCHAR2(512),
text07 VARCHAR2(512),
text08 VARCHAR2(512),
text09 VARCHAR2(512),
text10 VARCHAR2(512));
*/
cur_date DATE := trunc(start_date_in);
date_row rpt_results_10col_obj := aeo.rpt_results_10col_obj(NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL);
BEGIN
WHILE cur_date <= trunc(end_date_in)
LOOP
date_row.text01 := TO_CHAR(cur_date, 'MM/DD/YYYY');
PIPE ROW(date_row);
cur_date := cur_date + 1;
END LOOP;
RETURN;
EXCEPTION
WHEN no_data_found THEN
RETURN;
WHEN OTHERS THEN
dbms_output.put_line('EXCEPTION IN aeo.aeo_misc_tools.date_range_stream - ' || SQLCODE || ': ' ||
SQLERRM);
RAISE;
RETURN;
END date_range_stream;

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;

error in executing ODCIIndexStart() routine

I have a SEARCH package with several functions within that each return a pipelined table.
I am getting the following error:
ORA-29902: error in executing ODCIIndexStart() routine
ORA-06512: at "<SCHEMA_NAME>.SEARCH", line 955
ORA-06512: at "<SCHEMA_NAME>.SEARCH", line 216
ORA-06512: at line 1
ORA-06512: at "<SCHEMA_NAME>.SEARCH", line 1143
ORA-06512: at line 1
29902. 00000 - "error in executing ODCIIndexStart() routine"
*Cause: The execution of ODCIIndexStart routine caused an error.
*Action: Examine the error messages produced by the indextype code and
take appropriate action.
The command that I use to get this error is:
select * from table(search.user_search(10000003, '', 1, 20, 5, '', '', ''));
Here is the code around line 1143 of the SEARCH body:
FUNCTION USER_SEARCH(p_user_id number, p_search varchar2, p_page number, p_num_results number, p_concept_type_id number := null, p_standard_org varchar2 := null, p_standard_title varchar2 := null, p_standard_number varchar2 := null) RETURN SEARCH_RESULTS_TABLE AS
v_sub char(1);
v_rtn search_results_table := search_results_table();
v_user_id number;
v_page number;
v_num_results number;
v_query varchar2(4000);
cursor c_concept is
select * from table(search.concept_new(v_user_id, v_query, v_page, v_num_results, p_concept_type_id, p_standard_org, p_standard_title, p_standard_number));
BEGIN
v_page := p_page;
v_num_results := p_num_results;
v_query := p_search;
if (v_query = '') then
v_query := null;
end if;
-- ONLY DURING DEVELOPMENT
v_user_id := p_user_id;
if v_user_id = 0 then
v_user_id := 2;
end if;
if v_page = null then
v_page := 1;
end if;
if v_num_results = null then
v_num_results := -1;
end if;
select security.has_permission(p_user_id, 'UNLIMITED_RESULTS') into v_sub from dual;
case v_sub
when 'N' then
v_rtn := search.term(v_user_id, p_search, 1, 20, P_concept_type_id, p_standard_org, p_standard_title, p_standard_number);
when 'Y' then
for r_concept in c_concept loop -- line 1143
v_rtn.extend;
v_rtn(v_rtn.last) := search_results_row(r_concept.SCORE, r_concept.FROM_TABLE, r_concept.ID, r_concept.IN_DICTIONARY);
end loop;
end case;
return v_rtn;
END USER_SEARCH;
The code around line 216:
FUNCTION CONCEPT_NEW(p_user_id number, p_search varchar2, p_page number := 1, p_num_results number := -1, p_concept_type_id number := null, p_standard_org varchar2 := null, p_standard_title varchar2 := null, p_standard_number varchar2 := null) RETURN SEARCH_RESULTS_TABLE PIPELINED AS
out_rec search_results_row := search_results_row(null,null,null,null);
v_num_pages number;
v_num_results number;
cursor c_count is
select count(*) cnt, ceil(count(*) / p_num_results) num_pages from (
select * from (
select rownum rn, bb.* from (
select * from (
select sum(score) score, concept_id from
(
(
select
sum(score) score,
concept_id
from
(
select
s.score + (case
when c.eotd = 'N' then
200
else
0
end) score,
ct.concept_id
from
table(search.synonym_search(p_search)) s,
term_synonym ts,
concept_term ct,
concept c
where
c.eotd = 'N' and
s.table_id = ts.synonym_id and
ts.term_id = ct.term_id and
c.concept_id = ct.concept_id
)
group by
concept_id
)
union all
(
select
sum(score) score,
concept_id
from
(
select
s.score + (case
when c.eotd = 'N' then
200
else
0
end) score,
cd.concept_id
from
table(search.definition_search(p_search)) s,
concept_definition cd,
concept c
where
c.eotd = 'N' and
s.table_id = cd.definition_id and
c.concept_id = cd.concept_id
)
group by
concept_id
)
)
group by concept_id
) order by score desc
) bb) where score > 100) where rn < 1000;
-- another cursor here called c_search (removed for cleanliness)
v_added boolean;
v_in_dict varchar(1);
v_concept_id number;
cursor c_in_dict is
select * from dictionary_concept where concept_id = v_concept_id and dictionary_id in (select dictionary_id from company_dictionary where client_company_id in (select client_company_id from users where users_id = p_user_id));
v_in_dict_result c_in_dict%rowtype;
BEGIN
open c_count;
fetch c_count into v_num_results, v_num_pages; -- line 216
close c_count;
out_rec.SCORE := v_num_results;
out_rec.FROM_TABLE := 'HEADER';
out_rec.ID := v_num_pages;
out_rec.IN_DICTIONARY := 'N';
pipe row(out_rec);
-- more code here but been removed
RETURN;
END CONCEPT_NEW;
The code around line 995:
FUNCTION STANDARD_SEARCH(p_search varchar2) RETURN RESULT_TABLE
PIPELINED
AS
out_rec result_type := result_type(null, null);
cursor c_search is
select
45 score,
source_id
from
source
where
source_title = p_search or
standard_number = p_search
union all
select
40,
source_id
from
source
where
lower(source_title) = lower(p_search) or
lower(standard_number) = lower(p_search)
union all
select
35,
source_id
from
source
where
lower(source_title) like lower(p_search) || '%' or
lower(standard_number) like lower(p_search) || '%'
union all
select
30,
source_id
from
source
where
lower(source_title) like '%' || lower(p_search) || '%' or
lower(standard_number) like '%' || lower(p_search) || '%'
union all
select
10 * contains(source_title, lower(p_search)),
source_id
from
source
where
contains(source_title, lower(p_search)) > 1
union all
select
10 * contains(standard_number, lower(p_search)),
source_id
from
source
where
contains(standard_number, lower(p_search)) > 1;
BEGIN
for r_search in c_search loop -- line 995
out_rec.SCORE := r_search.SCORE;
out_rec.TABLE_ID := r_search.SOURCE_ID;
pipe row(out_rec);
end loop;
return;
END;
Change = null to is null. Zero-length strings in Oracle are already NULL so = '' is also incorrect.
Those are some obvious bugs but the code is so complex I'm not sure if they are directly related to the errors. You may need to shrink your code down until it can fit inside a small, reproducible test case. That can take hours of work sometimes.
Another suggestion is to rethink whether you need to use pipelined functions. In my experience pipelined functions are buggy and over-used. Sometimes they can be replaced by regular SQL statements. Sometimes they can be replaced by a simpler function that returns a complete collection, instead of returning it one row at a time. Unless the collection is so ginormous that it can't reasonably fit in memory, or you need the function to return the first N results so they can be processed immediately, the pipelined feature doesn't help.

Using parameters in procedures

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;

Bulding a fix length string with values passed using the length/position in pl/sql

I am writing a function which would build a fix a length empty string with value in a fix length string. for e.g.
I have a string of length 1000. When I pass string 'ABC' of length 5 with position 50, the function puts 'ABC' at 50th position of that fixed length string and pads 2 char with ' '.
I have managed to write it using regexp_replace. However I need it which doesnt use regexp_replace as it seems very slow in processing.
This function will be called in batch processing to build message string to be passed to other interface.
create or replace function insert_string(i_value varchar2, i_length number, i_position number) return varchar2
is
fix_string varchar2(1000) := ' ';
begin
fix_string := rpad(fix_string,1000,' ');
fix_string := regexp_replace(fix_string,'.{'||i_length ||'}',rpad(i_value,i_length,' '),i_position,1);
return fix_string;
end;
Something like this:
CREATE OR REPLACE FUNCTION insert_string(i_value VARCHAR2, i_length NUMBER, i_position NUMBER) RETURN VARCHAR2 IS
BEGIN
RETURN RPAD(LPAD(i_value, i_position + LENGTH(i_value) - 1), i_length);
END;
I dont know if this is what youve been trying to do/say but in case it is, ive added another parameter which will contain the source string. Without the string inside the function, its hard to manipulate values.
CREATE OR REPLACE FUNCTION INSERT_STRING(source_string VARCHAR2,
input_string VARCHAR2,
start_position NUMBER,
input_len NUMBER)
RETURN VARCHAR2 AS
TYPE char_arr IS TABLE OF CHAR(1) INDEX BY pls_integer;
TYPE char_arr2 IS TABLE OF CHAR(1);
source_array char_arr2:= char_arr2();
input_array char_arr;
X NUMBER:=1;
new_string VARCHAR2(2000);
BEGIN
FOR i IN 1..LENGTH(source_string) -- converts the source string to array
LOOP
source_array.extend;
source_array(i) := substr( source_string, i, 1 );
END LOOP;
-------------------------------------------------------------------
FOR j IN 1 .. input_len -- converts the input string to array with size input_len
LOOP
input_array(j) := substr( input_string, j, 1 );
END LOOP;
-------------------------------------------------------------------
--dbms_output.put_line(input_array(1));
FOR k IN 1..LENGTH(source_string) -- loop to insert the input_string to the source string
LOOP
IF k >= start_position THEN
source_array.extend;
source_array(k) := input_array(X) ;
x := x+1;
IF x > input_array.count THEN
exit;
end if;
END IF;
END LOOP;
FOR m IN 1 .. LENGTH(source_string) --loop to convert array back to string
LOOP
IF source_array(m) is null THEN
source_array(m) := ' ';
END IF;
new_string := new_string||source_array(m);
END LOOP;
RETURN new_string;
END;
Sample:
select insert_string('**********', 'ABC', 3, 5) from dual;
Output:
INSERT_STRING('**********','ABC',3,5)
**ABC ***
take note.
Source string should not be empty.
the size of the source string should be greater than the size of the input_string else the excess character from the input will not be inserted in the source string

How to get starting and ending date from last year in plsql

I have a plsql function count_order_cust with three parameters. But p_start and p_end are optional. If I call function with three parameters, then function is executed well, but if I call function with one parameter p_id, then it has to count orders for last year (i.e. 01/Jan/2013 - 31/Dec/2013) and this is my problem. How can I do this?
Here is function count_order_cust:
CREATE OR REPLACE FUNCTION count_order_cust(p_id f_customers.id%TYPE, p_sd f_orders.order_date%TYPE DEFAULT NULL, p_ed f_orders.order_date%TYPE DEFAULT NULL)
RETURN NUMBER IS v_count_orders NUMBER(3) := 0;
BEGIN
SELECT COUNT(cust_id) INTO v_count_orders
FROM f_orders
WHERE cust_id = p_id AND f_orders.order_date BETWEEN p_sd AND p_ed;
RETURN v_count_orders;
EXCEPTION WHEN NO_DATA_FOUND THEN RETURN NULL;
END count_order_cust;
With this select I get date in last year, but I need have in p_sd = '01/Jan/2013' and in p_ed = '31/Dec/2013'.
select add_months(sysdate,-12) from dual;
The below code shows you how to derive the required dates, then bundle the code into functions which can be used as the default parameters of your procedure:
declare
function getFirstDayOfLastYear return date is
begin
return trunc(add_months(sysdate,-12),'YEAR');
end;
function getLastDayOfLastYear return date is
begin
return trunc(sysdate,'YEAR') - 1;
end;
procedure myProc(
p_start date default getFirstDayOfLastYear,
p_end date default getLastDayOfLastYear
) is
begin
dbms_output.put_line(p_start);
dbms_output.put_line(p_end);
end;
begin
myProc;
end;

Resources