Date based select query not working inside cursor - oracle

I am working on a stored procedure which has the following date based select query inside cursor like below
CURSOR inv_cur ( v_hotel_id IN NUMBER, v_stay_date IN DATE)
IS
SELECT id,
start_date ,
room_count ,
booked_room_count
FROM inventory
WHERE hotel_id = v_hotel_id
AND TRUNC( start_date ) = TRUNC(v_stay_date);
For some reason it does not work and I am not getting records. But when i execute without the date comparison or execute the query in the stand alone manner like below, it works
SELECT id,
start_date ,
room_count ,
booked_room_count
FROM inventory
WHERE hotel_id = 1509622
AND TRUNC( start_date ) = TO_DATE('08-03-18', 'dd-mm-yy');
This is how I am invoking the cursor call
PROCEDURE (.....)
IS
v_date_temp DATE;
BEGIN
v_date_temp := p_check_in_date;
FOR inventory_rec IN inv_cur
(
v_hotel_id, v_room_type_id, v_date_temp
)
LOOP
v_room_count := inventory_rec.room_count;
v_book_room_count := NVL ( inventory_rec.booked_room_count,0 );
v_inventory_id := inventory_rec.id;
dbms_output.put_line(' Inv id' || v_inventory_id);
END LOOP;
Since there are no records returned, it is not going inside loop.
I think there is something wrong with the date comparison. Can someone help me figure out this puzzling issue with my date comparison ?

Related

How put a condition in the where clause in the procedure?

I want to put a condition in the where clause according to the null condition of the parameter in the procedure. But the procedure I made has some problem. There's problem is in IF clause.
How I can put a condition in the where clause in the procedure?
CREATE OR REPLACE PROCEDURE SP_PROCEDURE(START_DATE, END_DATE)
IS
START_DATE DATE;
END_DATE DATE;
BEGIN
START_DATE := TO_DATE(START_DATE, 'YYYYMMDD');
END_DATE := TO_DATE(END_DATE, 'YYYYMMDD');
INSERT INTO USER
(
USR_KEY,
USR_NAME
)
SELECT
USR_KEY,
USR_NAME
FROM
USER
WHERE
1 = 1
IF START_DATE THEN --I think there's problem here..
AND USR_CRT_DATE >= START_DATE
END IF;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
END;
If I'm reading your requirements correctly, this should do the trick
CREATE OR REPLACE PROCEDURE SP_PROCEDURE(START_DATE, END_DATE)
IS
START_DATE DATE;
END_DATE DATE;
BEGIN
START_DATE := TO_DATE(START_DATE, 'YYYYMMDD');
END_DATE := TO_DATE(END_DATE, 'YYYYMMDD');
INSERT INTO USER
(
USR_KEY,
USR_NAME
)
SELECT
USR_KEY,
USR_NAME
FROM
USER
WHERE (USR_CRT_DATE >= START_DATE
or START_DATE is null );
COMMIT;
END;
You don't need that exception handler. By default if a PL/SQL unit fails, it will rollback changes to the commencement point of the procedure.
You are missing the data types in the signature of the procedure.
Never use TO_DATE on a value that is already a DATE (as it will implicitly convert it from the date to a string and then back to a date and is likely to introduce errors in the conversion). If you want to remove the time component of a date then use TRUNC instead of trying to use TO_DATE.
It is bad practice to use COMMIT in a procedure as it does not allow you to chain multiple procedures together and then if one fails you can ROLLBACK them all; instead you should COMMIT in the transaction that calls the procedure.
USER is a keyword and cannot be used as an unquoted identifier; if you try you will get the exception ORA-00903: invalid table name. Best practice would be to name the table something that is not a keyword but you could also use quoted identifiers (which is not considered best practice).
CREATE OR REPLACE PROCEDURE SP_PROCEDURE(
START_DATE IN DATE,
END_DATE IN DATE
)
IS
BEGIN
INSERT INTO "USER" (
USR_KEY,
USR_NAME
)
SELECT USR_KEY,
USR_NAME
FROM "USER"
WHERE (START_DATE IS NULL OR USR_CRT_DATE >= TRUNC(START_DATE));
END;
/
fiddle

Using a date variable in a query to fetch records based on a given date in oracle

I need to write a function in oracle plsql that with take a date as an input and return records from a table for that particular day. If no date is given then fetch the records for current day.
Note that the column (purchase_date) is a timestamp(6) type not null column and has an index on it so I would not like to use trunc() function on the column.
Example value present in purchase_date column is --> 01-DEC-21 06.14.06.388855001 AM
create or replace FUNCTION getRecordsForDate(
input_date DATE DEFAULT SYSDATE
) RETURN sys_refcursor IS
data_out SYS_REFCURSOR;
BEGIN
OPEN data_out FOR
SELECT
p.product_name,
p.product_type,
p.purchased_by
FROM
product_details p
WHERE
AND p.purchase_date BETWEEN TO_DATE(input_date, 'DD-MON-YY')
-- AND TO_DATE('03-MAR-22 23:59:59', 'DD-MON-YY HH24:MI:SS'); --harcoded value works but I need to use input_date
AND 'TO_DATE' ||'(''' || input_date || ' 23:59:59''' ||',' || '''YYYY-MM-DD HH24:MI:SS''' ||')';
return data_out;
END getRecordsForDate;
My concatenation is not working in the last line. It gives me ORA-01858: a non-numeric character was found where a numeric was expected. Not sure what's wrong here. Would someone be able to help.
Do not use TO_DATE on a DATE.
The last line of the cursor will not work as it is a (concatenated) string literal that cannot be converted to a TIMESTAMP or a DATE.
Even if it did work (which it will not), your purchase_date is a TIMESTAMP(6) data type so you are going to exclude all value from the time 23:59:59.0000001 until 23:59:59.999999.
You want to use:
create or replace FUNCTION getRecordsForDate(
input_date DATE DEFAULT SYSDATE
) RETURN sys_refcursor
IS
data_out SYS_REFCURSOR;
BEGIN
OPEN data_out FOR
SELECT product_name,
product_type,
purchased_by
FROM product_details
WHERE purchase_date >= TRUNC(input_date)
AND purchase_date < TRUNC(input_date) + INTERVAL '1' DAY;
return data_out;
END getRecordsForDate;
/

Can I create parameterized views in oracle

I have a query like this
SELECT ID,REF_ID,BATCHNO FROM reporttbl
where POSTING_DT >= '06/01/2020' and POSTING_DT <= '06/30/2020'
and I need it every month, so I would like to put it in a view, but as the date changes every month, it would be great to have a date parameter that I can pass to the view when calling it. Is there a way on how can i achieved this?
I'm new to oracle, appreciate every help. Thank youu.
From 19.6 you can create parameterized views using SQL macros.
create or replace function get_month (
tab dbms_tf.table_t, start_date date, end_date date
) return varchar2 sql_macro as
retval int;
begin
return 'select * from tab
where dt >= start_date and dt < end_date + 1';
end get_month;
/
create table t (
c1 int, dt date
);
insert into t
with rws as (
select level c1, add_months ( date'2019-12-25', level ) dt
from dual
connect by level <= 10
)
select * from rws;
select * from get_month (
t, date'2020-06-01', date'2020-07-01'
);
C1 DT
6 25-JUN-2020 00:00:00
select * from get_month (
t, date'2020-08-01', date'2020-09-01'
);
C1 DT
8 25-AUG-2020 00:00:00
This query return the data for the previous month, i.e. the month befort the current month at the time of the query (= sysdate).
You use the trunc with 'MM' to get the months first and the arithmetic with add_months
SELECT ID,REF_ID,BATCHNO FROM reporttbl
where POSTING_DT >= add_months(trunc(sysdate,'MM'),-1) and POSTING_DT < trunc(sysdate,'MM')
Another way to do this is using a function that retrieves the parameters from a table, thereby you don't need to manipulate any DDL. The idea here is
Using a table to store the parameters, basically you need parameter value and a parameter description.
Using a function to retrieve the value of that parameter when the input is the parameter name
Using the function call inside the view.
You can then manipulate the view automatically by modifying the values of the parameter table.
Table
create table my_param_table
( param_description varchar2(100) ,
param_value varchar2(100),
enabled varchar2(1)
) ;
Function
create or replace function f_retr_param ( p_value in varchar2 )
return varchar2
is
declare
v_value my_param_table.value_param%type;
begin
select value into v_value from my_table_of_parameters
where upper(value_param) = upper(p_value) ;
return v_value;
exception when others then raise;
end;
/
View
create or replace force view my_view as
SELECT ID,REF_ID,BATCHNO FROM reporttbl
where POSTING_DT >= f_retr_param ( p_value => 'p_start_date' );
and POSTING_DT <= f_retr_param ( p_value => 'p_end_date' );
There are ways to "parameterize" a view e.g. using Oracle contexts, but they aren't often useful and certainly not for your case.
If your query really just selects from one table with just the dates as predicates then a view doesn't add much value either. You could create a SQL script (in a file e.g. myquery.sql) using bind variables:
SELECT ID,REF_ID,BATCHNO FROM reporttbl
where POSTING_DT >= to_date(:from_date) and POSTING_DT <= to_date(:to_date);
Then every month you can just open the file and run it, and it will prompt you for the 2 dates. Or you can run as a script like this and it will also prompt you:
#myquery.sql
Or if you use substitution strings '&1.' and '&2.' instead:
SELECT ID,REF_ID,BATCHNO FROM reporttbl
where POSTING_DT >= to_date('&1.') and POSTING_DT <= to_date('&2.');
Then you can pass the dates in on the command line like this:
#myquery '06/01/2020' '06/30/2020'
(Because &1. means first parameter on command line, etc.)

Oracle lookup function in loop returns rowtype how to get fields from rowtype

I have a procedure that runs a select ( I tested that is good returns 56 records )
then when I run a cursor I want to pass 3 fields to a function ( see above ) that will
lookup/select a record from a table that contains 15 million records ( 10 years worth ).
It returns a rowtype that I want to then extract the fields from this rowtype record to
run an insert with both the records from the 1st select and the additional fields acquired
from the lookup function.
If I run the procedure the console prints out my test msgs but when I try to run
select * bulk collect into v_tab_proc_claim_recs from v_processed_claim;
it doesn't compile due to Error(97,65): PL/SQL: ORA-00942: table or view does not exist
as if either of these are not Tables.
Am I doing this right... how can I do it, why can't it see the table I'm trying to extract to ?
Should I do this some other way..
Thanks for any help/suggestions :)
The function is below....
create or replace function get_processed_claim_rec(
p_provider VARCHAR2,
p_rx VARCHAR2,
p_record_no NUMBER
)
return i_idb.processed_claim%rowtype
as
l_claim_record i_idb.processed_claim%rowtype;
begin
select * into l_claim_record from i_idb.processed_claim
where source_date = p_provider
AND rx = p_rx
AND rec_no = p_record_no;
return(l_claim_record);
end;
And the procedure is....
create or replace PROCEDURE import_mailer_data
AS
-------------------------------
/**
for the lookup table
**/
v_processed_claim i_idb.processed_claim%rowtype;
TYPE proc_claim_recs IS TABLE OF v_processed_claim%ROWTYPE INDEX BY PLS_INTEGER;
v_tab_proc_claim_recs proc_claim_recs;
--------------------------------
CURSOR myCursor
IS
SELECT *
from
(
SELECT
j.create_date as open_date,
case when (j.create_date < (sysdate - 20) )
then 'POD'
else 'REG'
end as priority,
c.division,
c.unit,
--p.refill as days_supply,
--p.din_name,
'CM_JOHN' as log_code,
c.first_name,
c.last_name,
--p.UNLISTED_compound,
--p.intervention_code,
--p.substitution,
--p.confirm,
c.PROVIDER,
c.rx,
c.DISPENSE_DATE,
c.DIN,
c.QTY,
c.DIN_COST_PAID,
c.DISP_FEE_PAID,
c.PAID_AMOUNT,
c.SOURCE_DATE,
c.RECORD_NO,
c.RELATIONSHIP,
c.INSURER_NO,
c.GROUP_NO,
c.CERTIFICATE,
c.BIRTH_DATE,
c.USER_ID,
--p.rej_code --v_seq_no
rank() over
(
partition by c.provider, c.rx, c.record_no Order by c.provider desc, c.rx desc
) as RNK
FROM AUDITCOLLECTIONS.MAILER_CLAIMS c,
AUDITCOLLECTIONS.MAILER_JOBS j
WHERE MAILER_JOB_DETAIL_ID IN
(SELECT MAILER_JOB_DETAIL_ID
FROM AUDITCOLLECTIONS.MAILER_JOB_DETAILS
WHERE MAILER_JOB_ID IN
( SELECT MAILER_JOB_ID FROM AUDITCOLLECTIONS.MAILER_JOBS
)
)
AND ( c.PROVIDER, c.rx, c.record_no ) NOT IN
( SELECT provider, rx, rec_no FROM AUDITCOLLECTIONS.COLLECTION_AUDIT_STAGING
)
AND j.create_date > (sysdate - 30)
AND c.provider = '2010500042'
) A_Latest
where A_Latest.RNK = 1;
BEGIN
v_report_id := audit_load.create_loaded_report(v_report_type_id);
FOR curRec IN myCursor
LOOP
BEGIN
dbms_output.put_line ('===>>>> PRINTING TEST1 = ');
v_processed_claim := get_processed_claim_rec(curRec.PROVIDER, curRec.RX, curRec.RECORD_NO);
select * bulk collect into v_tab_proc_claim_recs from v_processed_claim;
END LOOP;
audit_load.update_status_to_loaded(v_report_id);
END import_mailer_data;
You can do this:
FOR curRec IN myCursor
LOOP
v_processed_claim :=
get_processed_claim_rec(curRec.PROVIDER, curRec.RX, curRec.RECORD_NO);
v_tab_proc_claim_recs (v_tab_proc_claim_recs.COUNT+1) := v_processed_claim;
END LOOP;
Or simplify to:
FOR curRec IN myCursor
LOOP
v_tab_proc_claim_recs (v_tab_proc_claim_recs.COUNT+1) :=
get_processed_claim_rec(curRec.PROVIDER, curRec.RX, curRec.RECORD_NO);
END LOOP;

creating parameterized views in oracle11g

I have a big query with nesting and left join and Ineed to create a view out of it so as not to run it from the application. The issue is I need the date range and some other fields as input parameters since it will vary from the front end for each request.
I just looked up and saw some posts referring to using SYS_CONTEXT for parameterized views and need to know exactly how do I create the view for example with 2 parameters - fromdate, todate and how I invoke the view from the application.
Just for info I am using grails/groovy for developing the application.
and here is the query I want to create view out of..
select
d.dateInRange as dateval,
eventdesc,
nvl(td.dist_ucnt, 0) as dist_ucnt
from (
select
to_date(fromdate,'dd-mon-yyyy') + rownum - 1 as dateInRange
from all_objects
where rownum <= to_date(fromdate,'dd-mon-yyyy') - to_date(todate,'dd-mon-yyyy') + 1
) d
left join (
select
to_char(user_transaction.transdate,'dd-mon-yyyy') as currentdate,
count(distinct(grauser_id)) as dist_ucnt,
eventdesc
from
gratransaction, user_transaction
where gratransaction.id = user_transaction.trans_id and
user_transaction.transdate between to_date(fromdate,'dd-mon-yyyy') and to_date(todate,'dd-mon-yyyy')
group by to_char(user_transaction.transdate, 'dd-mon-yyyy'), eventdesc
) td on td.currentdate = d.dateInRange order by d.dateInRange asc
The context method is described here: http://docs.oracle.com/cd/B28359_01/network.111/b28531/app_context.htm
e.g. (example adapted from the above link)
CREATE CONTEXT dates_ctx USING set_dates_ctx_pkg;
CREATE OR REPLACE PACKAGE set_dates_ctx_pkg IS
PROCEDURE set(d1 in date, d2 in date);
END;
/
CREATE OR REPLACE PACKAGE BODY set_dates_ctx_pkg IS
PROCEDURE set(d1 in date, d2 in date) IS
BEGIN
DBMS_SESSION.SET_CONTEXT('dates_ctx', 'd1', TO_CHAR(d1,'DD-MON-YYYY'));
DBMS_SESSION.SET_CONTEXT('dates_ctx', 'd2', TO_CHAR(d2,'DD-MON-YYYY'));
END;
END;
/
Then, set the dates in your application with:
BEGIN set_dates_ctx_pkg.set(mydate1, mydate2); END;
/
Then, query the parameters with:
SELECT bla FROM mytable
WHERE mydate
BETWEEN TO_DATE(
SYS_CONTEXT('dates_ctx', 'd1')
,'DD-MON-YYYY')
AND TO_DATE(
SYS_CONTEXT('dates_ctx', 'd2')
,'DD-MON-YYYY');
The advantage of this approach is that it is very query-friendly; it involves no DDL or DML at runtime, and therefore there are no transactions to worry about; and it is very fast because it involves no SQL - PL/SQL context switch.
Alternatively:
If the context method and John's package variables method are not possible for you, another one is to insert the parameters into a table (e.g. a global temporary table, if you're running the query in the same session), then join to that table from the view. The downside is that you now have to make sure you run some DML to insert the parameters whenever you want to run the query.
I have just made a workaround for this annoying Oracle disadvantage. Like this
create or replace package pkg_utl
as
type test_record is record (field1 number, field2 number, ret_prm_date date);
type test_table is table of test_record;
function get_test_table(prm_date date) return test_table pipelined;
end;
/
create or replace package body pkg_utl
as
function get_test_table(prm_date date) return test_table pipelined
is
begin
for item in (
select 1, 2, prm_date
from dual
) loop
pipe row (item);
end loop;
return;
end get_test_table;
end;
/
it still requires a package, but at least i can use it in more convinient way:
select *
from table(pkg_utl.get_test_table(sysdate))
i am not sure about performance...
To use parameters in a view one way is to create a package which will set the values of your parameters and have functions that can be called to get those values. For example:
create or replace package MYVIEW_PKG as
procedure SET_VALUES(FROMDATE date, TODATE date);
function GET_FROMDATE
return date;
function GET_TODATE
return date;
end MYVIEW_PKG;
create or replace package body MYVIEW_PKG as
G_FROM_DATE date;
G_TO_DATE date;
procedure SET_VALUES(P_FROMDATE date, P_TODATE date) as
begin
G_FROM_DATE := P_FROMDATE;
G_TO_DATE := P_TODATE;
end;
function GET_FROMDATE
return date is
begin
return G_FROM_DATE;
end;
function GET_TODATE
return date is
begin
return G_TO_DATE;
end;
end MYVIEW_PKG;
Then your view can be created thus:
create or replace view myview as
select
d.dateInRange as dateval,
eventdesc,
nvl(td.dist_ucnt, 0) as dist_ucnt
from (
select
MYVIEW_PKG.GET_FROMDATE + rownum - 1 as dateInRange
from all_objects
where rownum <= MYVIEW_PKG.GET_FROMDATE - MYVIEW_PKG.GET_TODATE + 1
) d
left join (
select
to_char(user_transaction.transdate,'dd-mon-yyyy') as currentdate,
count(distinct(grauser_id)) as dist_ucnt,
eventdesc
from
gratransaction, user_transaction
where gratransaction.id = user_transaction.trans_id and
user_transaction.transdate between MYVIEW_PKG.GET_FROMDATE and MYVIEW_PKG.GET_TODATE
group by to_char(user_transaction.transdate, 'dd-mon-yyyy'), eventdesc
) td on td.currentdate = d.dateInRange order by d.dateInRange asc;
And to run it you must set the values first:
exec MYVIEW_PKG.SET_VALUES(trunc(sysdate)-1,trunc(sysdate));
And then calls to it will use these values:
select * from myview;

Resources