Timestamp subtraction - oracle

I am getting the error
PL/SQL: ORA-00932: inconsistent datatypes: expected NUMBER got INTERVAL DAY TO SECOND
When I try to run this procedure. The column EXPIRES in the code below is of type TIMESTAMP(6) in the SESSIONS table. As far code is concerned I am having two consistent datatypes being subtracted from each other. So why this error?
CREATE OR REPLACE PROCEDURE demo_restrict_multlogin (p_login varchar2,
p_out OUT NUMBER,
p_msg OUT VARCHAR2)
IS
vl_val NUMBER;
vl_diff TIMESTAMP(6);
BEGIN
p_out := 0;
SELECT SYStimestamp-EXPIRES
INTO vl_diff
FROM sessions
WHERE LOGIN_DETAILS = p_login;
SELECT CASE WHEN COUNT (1) > 0 THEN 1 ELSE 2 END
INTO vl_val
FROM sessions
WHERE LOGIN_DETAILS = p_login AND TO_CHAR (vl_diff, 'mi') > 30;
IF vl_val = 1
THEN
p_msg := 'Pass expired';
ELSE
p_msg := 'Pass not expired ';
END IF;
EXCEPTION
WHEN OTHERS
THEN
p_out := 1;
END;

When you subtract one value that is of timestamp data type from another one, which is also of timestamp data type, result of that subtraction will be of interval data type, not timestamp, so when you are trying to select/assign a value of interval data type into/to a variable that is of timestamp data type, you will inevitably receive the PL/SQL: ORA-00932:/PLS-00382 error message. A simple solution is to assign the result of timestamp subtraction to a variable of interval data type. To that end you:
Re-declare your vl_diff variable as a variable of interval data type:
vl_diff interval day to second;
use extract() function to extract minutes from the value assigned to vl_diff variable:
extract(minute from vl_diff)
Your second query might look like this:
select case when count(1) > 0 then 1 else 2 end
into vl_val
from sessions
where login_details = p_login
and extract(minute from vl_diff) > 30

Have you tried:
SELECT TRUNC(SYStimestamp - EXPIRES)
INTO vl_diff
FROM sessions
WHERE LOGIN_DETAILS = p_login;

Related

Wrong number or types of arguments in call to '<=' when comparing Timestamp in oracle apex

DECLARE FUNCTION CHECKTIME (APP_TIME IN TIMESTAMP)
RETURN BOOLEAN IS
BEGIN
DECLARE
START_TIME TIMESTAMP;
END_TIME TIMESTAMP;
CURSOR ST IS
SELECT APP_START_TIME FROM APPOINTMENTS WHERE DOC_ID = :P24_DOC_ID;
CURSOR ET IS
SELECT APP_END_TIME FROM APPOINTMENTS WHERE DOC_ID = :P24_DOC_ID;
BEGIN
OPEN ST;
OPEN ET;
LOOP
FETCH ST INTO START_TIME;
FETCH ET INTO END_TIME;
EXIT WHEN ST%NOTFOUND;
IF APP_TIME <= ST AND APP_TIME >= ET
THEN RETURN FALSE;
END IF;
END LOOP;
CLOSE ST;
CLOSE ET;
RETURN TRUE;
END;
END;
BEGIN
RETURN CHECKTIME(:P42_APP_START_TIME);
END;
I was trying to put the above code in PL/SQL Function Body Returning Boolean in validation section of an item field. This is the error I got.
ORA-06550: line 18, column 25: PLS-00306: wrong number or types of arguments in call to '<='
What is the real cause of the error?
ST and ET are cursors, which means they are data structures. To make a comparison you need to refer to the individual elements of the projections, that is
IF APP_TIME <= ST.APP_START_TIME AND APP_TIME >= ET.APP_END_TIME
By the way. It seems odd to compare separate cursors with no apparent ordering. Either you're comparing values from the same record in which case you need only the one cursor, or there are multiple rows for DOC_ID in which case some form of ordering or joining would seem to be necessary.
DECLARE FUNCTION CHECKTIME (APP_TIME IN TIMESTAMP, DOCID IN VARCHAR2)
RETURN BOOLEAN IS
BEGIN
DECLARE
START_TIME TIMESTAMP;
END_TIME TIMESTAMP;
CURSOR T IS
SELECT APP_START_TIME, APP_END_TIME FROM APPOINTMENTS WHERE DOC_ID = DOCID;
BEGIN
OPEN T;
LOOP
FETCH T INTO START_TIME, END_TIME;
EXIT WHEN T%NOTFOUND;
IF APP_TIME <= END_TIME AND APP_TIME >= START_TIME
THEN RETURN FALSE;
END IF;
END LOOP;
CLOSE T;
RETURN TRUE;
END;
END;
BEGIN
RETURN CHECKTIME(:P42_APP_START_TIME, :P42_DOC_ID);
END;
It seems like the error occurs because I use 2 different cursors which might return different number of rows and hence I can't use the comparison operator. After I change it to a single cursor, the error disappeared.

How to select max value from a temporary table whose field is unnamed

I'm writing a PL/SQL function that needs to get dates from a few different tables and return the most recent one. My approach has been this:
Create a temporary table to hold the dates:
CREATE TYPE t_dates IS TABLE OF DATE;
/
Create a few local variables:
l_date DATE;
l_dates t_dates := t_dates();
l_idx INTEGER := 0;
l_output_date DATE;
Then select into a variable each date I'm interested in, and add it to the temporary table:
SELECT it.date
INTO l_date
FROM interesting_table it
WHERE it.id = 1
;
l_dates.extend;
l_idx := l_idx + 1;
l_dates(l_idx) := l_date;
After the temporary table has been populated, now I just want to select the max value from it. How do I do that? Something like...
SELECT max(*)
INTO l_output_date
FROM l_dates
;
RETURN l_output_date;
But I'm not sure I can reference my temporary table like that, nor how to find the max of a column that is unnamed.
Edit: When I test the above, I get an error: PL/SQL: ORA-00936: missing expression related to the line where I have SELECT max(*). However, I don't believe that this is my only error, because if I change the last block to:
SELECT *
INTO l_output_date
FROM l_dates
WHERE rownum = 1
;
which, just for testing, should select the first date in the temp table, then the error becomes PL/SQL: ORA-00942: table or view does not exist, which indicates to me that I can't refer to my temp table in this manner.
I have since learned from the comments that I should have max(column_value) rather than max(*), but using this, I still get the table or view does not exist error.
It is not possible to select from the nested tables, like you have tried.
You may have to use a logic to find the maximum value
l_idx := l_idx + 1;
l_dates(l_idx) := l_date;
if(l_date > l_dates(l_idx-1) OR l_idx =1) THEN
max_date :=l_date;
End if;
For your case, Rather than fetching a list of dates then getting the max date, you can find the max date in your query itself
SELECT max(it.date)
INTO l_date
FROM interesting_table it
WHERE it.id = 1
;

database update on large table oracle taking huge time

I want to copy one column data into another column in a large table containing 10 millions records.
I am using sys refcursor to copy data from one column into another column. It will taking more than 30 min to copy the data. I am using ORACLE 11gR2.
Is there any others alternative to do the same. Below is the scripts
CREATE OR REPLACE PROCEDURE tblCursor(org_mig OUT SYS_REFCURSOR)
IS
BEGIN
OPEN org_mig FOR
select id from tbl;
END;
/
DECLARE
org_mig SYS_REFCURSOR;
t_id organization.id%TYPE;
loop_var number(10);
commit_interval number(10);
BEGIN
loop_var :=1;
commit_interval:=10000;
tblCursor(org_mig);
LOOP
FETCH org_mig INTO t_id;
EXIT WHEN org_mig%NOTFOUND;
update tbl set col1=col2 where id=t_id;
IF mod(loop_var,commit_interval)=0 THEN
Commit;
End if;
loop_var :=loop_var+1;
END LOOP;
Commit;
CLOSE org_mig;
END;
/
You're doing this for every row in tbl, right? If so, you should just do this:
update tbl
set col1 = col2
/
Updating ten million rows will take some time, but a set operation will be way faster than the Row By Agonizing Row approach you've implemented. Plus, batching up your commits like that is bad practice. Not only does it slow things down, that approach can lead to ORA-01555: Snapshot too old exceptions. Find out more.
Still it has been taken long time to update.
I am trying with different one but getting error.
-----------------------------------
Error starting at line : 43 in command -
SELECT *
FROM TABLE(test_parallel_update(CURSOR(SELECT * FROM organization)))
Error report -
SQL Error: ORA-12801: error signaled in parallel query server P003
ORA-00932: inconsistent datatypes: expected - got -
ORA-06512: at "QA249.TEST_PARALLEL_UPDATE", line 21
12801. 00000 - "error signaled in parallel query server %s"
*Cause: A parallel query server reached an exception condition.
*Action: Check the following error message for the cause, and consult
your error manual for the appropriate action.
*Comment: This error can be turned off with event 10397, in which
case the server's actual error is signaled instead.
---------------------------
Here is the script:
CREATE OR REPLACE TYPE test_num_arr AS TABLE OF NUMBER;
CREATE OR REPLACE FUNCTION test_parallel_update (
test_cur IN SYS_REFCURSOR
)
RETURN test_num_arr
PARALLEL_ENABLE (PARTITION test_cur BY ANY)
PIPELINED
IS
PRAGMA AUTONOMOUS_TRANSACTION;
test_rec organization%ROWTYPE;
TYPE num_tab_t IS TABLE OF NUMBER(10,0);
TYPE vc2_tab_t IS TABLE OF number(1,0);
id NUM_TAB_T;
org_type_old NUM_TAB_T;
IS_DELETED_old VC2_TAB_T;
cnt INTEGER := 0;
BEGIN
LOOP
FETCH test_cur BULK COLLECT INTO id, org_type_old, IS_DELETED_old LIMIT 1000;
EXIT WHEN id.COUNT() = 0;
FORALL i IN id.FIRST .. id.LAST
UPDATE organization
SET org_type = org_type_old(i)
, IS_DELETED = IS_DELETED_old(i)
WHERE id = id(i);
cnt := cnt + id.COUNT;
END LOOP;
CLOSE test_cur;
COMMIT;
PIPE ROW(cnt);
RETURN;
END;
/
show error;
---- To Execute ----
SELECT *
FROM TABLE(test_parallel_update(CURSOR(SELECT * FROM organization)));
Note:
Table
organization
(
id number(10,0),
org_type number(10,0),
org_type_old number(10,0),
IS_DELETED number(1,0),
IS_DELETED_OLD number(1,0)
);
where id is a primary key, Now I want copy org_type_old and IS_DELETED_OLD into org_type and IS_DELETED respectively.

Casting Varchar2 to interval

I have a configuration table such that I want to delete a row if the record_tm is over a certain configurable time (config_recordtrim_hrs). In other words, the variable recordtrim_config comes from another table where somebody writes a value (e.g. 24 for 24 hours) after which a record will be deleted.
DECLARE
recordtrim_flag configs_table.value%type;
recordtrim_config configs_table.value%type;
recordtrim_interval interval;
BEGIN
SELECT configs_table.value INTO recordtrim_flag FROM configs_table WHERE configs_table.name = 'enable_recordtrim';
SELECT configs_table.value INTO recordtrim_config FROM configs_table WHERE configs_table.name = 'config_recordtrim_hrs';
SELECT CAST (recordtrim_config AS interval) INTO recordtrim_interval;
IF (recordtrim_flag = 'T') THEN
DELETE FROM main_records WHERE record_tm + recordtrim_interval < current_timestamp;
END IF;
END;
This code throws an error at the line where I try to cast recordtrim_config as an interval. How do I fix this?
EDIT: My error:
Error report: ORA-06550: line 8, column 40: PL/SQL: ORA-30089: missing
or invalid ORA-06550: line 8, column 1: PL/SQL: SQL
Statement ignored
EDIT 2: New Code:
DECLARE
recordtrim_flag configs_table.value%type;
recordtrim_config configs_table.value%type;
recordtrim_interval interval;
BEGIN
SELECT configs_table.value INTO recordtrim_flag FROM configs_table WHERE configs_table.name = 'enable_recordtrim';
SELECT configs_table.value INTO recordtrim_config FROM configs_table WHERE configs_table.name = 'config_recordtrim_hrs';
recordtrim_interval := NUMTODSINTERVAL (recordtrim_config, 'HOUR');
IF (recordtrim_flag = 'T') THEN
DELETE FROM main_records WHERE record_tm + recordtrim_interval < current_timestamp;
END IF;
END;
New Error at line 11, recordtrim_interval is an invalid identifier
The immediate cause of the error is that you've got just interval instead of the type, i.e. interval day to second. You also have to select from something - which can be from dual here, though a straight assignment is cleaner.
You can't cast it directly if it's a single number. Instead you can use the numtodsinterval function:
recordtrim_interval := NUMTODSINTERVAL (recordtrim_config, 'HOUR');
e.g.
set serveroutput on
declare
recordtrim_config varchar2(4) := '24';
recordtrim_interval interval day to second;
begin
recordtrim_interval := NUMTODSINTERVAL (recordtrim_config, 'HOUR');
dbms_output.put_line(recordtrim_interval);
end;
/
anonymous block completed
+01 00:00:00.000000
Or select that straight into the variable from the table.
If you need more complicated values, not a simple value that represents a number of hours - and if that was the case you'd store it as a number I assume - then if you store it in a suitable format, you can cast it, or use the to_dsinterval function instead; any of these give the same result:
declare
recordtrim_config varchar2(10) := '0 2:30:00';
recordtrim_interval interval day to second;
begin
recordtrim_interval := TO_DSINTERVAL (recordtrim_config);
dbms_output.put_line(recordtrim_interval);
SELECT CAST (recordtrim_config AS interval day to second)
INTO recordtrim_interval from dual;
dbms_output.put_line(recordtrim_interval);
recordtrim_interval := CAST (recordtrim_config AS interval day to second);
dbms_output.put_line(recordtrim_interval);
end;
/
anonymous block completed
+00 02:30:00.000000
+00 02:30:00.000000
+00 02:30:00.000000
In your edited question, the ORA-00904: "RECORDTRIM_INTERVAL": invalid identifier is only the last error in the list. Look back a bit and you'll see:
ORA-06550: line 4, column 21:
PLS-00201: identifier 'INTERVAL' must be declared
... followed by some other errors where you try to use the variable. Your declaration of the variable has to be:
recordtrim_interval interval day to second;
There is no 'interval' data type; they are interval year to month and interval day to second.

Error while receiving date as parameter input

I am getting an error when I plug in a date to test a parameter I am working on. The error is:
01858. 00000 - "a non-numeric character was found where a numeric was expected"
*Cause: The input data to be converted using a date format model was
incorrect. The input data did not contain a number where a number was
required by the format model.
*Action: Fix the input data or the date format model to make sure the
elements match in number and type. Then retry the operation.
Here is my test script:
set serveroutput on
declare
type tempcursor is ref cursor;
v_cur_result tempcursor;
errcode number;
errmesg varchar2(1000);
p_statusmnemonic_in acts.ct_cu_act_medrecon_pg.varchararrayplstype;
p_processtypemnemonic_in transactionprocesslog.processtypemnemonic%type;
p_primarymemberplanid_in membermedicalreconcilationhdr.primarymemberplanid%type;
p_assigneduserid_in membermedicalreconcilationhdr.assigneduserid%type;
p_accountorgid_in membermedicalreconcilationhdr.accountorgid%type;
p_reconstatusmnemonic_in membermedicalreconcilationhdr.reconciliationstatusmnemonic%type;
p_estimatedenddt_in membermedicalreconcilationhdr.estimatedenddt%type;
p_actualenddt_in membermedicalreconcilationhdr.actualenddt%type;
p_inserteddate_in membermedicalreconcilationhdr.inserteddt%type;
p_insertedby_in membermedicalreconcilationhdr.insertedby%type;
p_updateddate_in membermedicalreconcilationhdr.updateddt%type;
p_updatedby_in membermedicalreconcilationhdr.updatedby%type;
begin
p_statusmnemonic_in(1) := ('OPEN');
p_statusmnemonic_in(2) := ('SUSPENDED_PRIOR_TO_COMPARE');
ct_cu_act_medrecon_pg.sps_get_patientmedrecs_hdr
(p_statusmnemonic_in,'NO','26-JAN-14',v_cur_result, errcode, errmesg);
loop
fetch v_cur_result into p_primarymemberplanid_in,p_assigneduserid_in,p_accountorgid_in,p_reconstatusmnemonic_in,
p_estimatedenddt_in,p_actualenddt_in,p_inserteddate_in,p_insertedby_in,
p_updateddate_in,p_updatedby_in,p_processtypemnemonic_in;
dbms_output.put_line(' planid '||p_primarymemberplanid_in||' userid '||p_assigneduserid_in);
exit when v_cur_result%notfound;
end loop;
dbms_output.put_line(' error code '||errcode||' message '||errmesg);
end;
I dont get an error when the date is set to '24-JAN-13' but the second I change anything I get that error. Here are the two estimated date fields in the table I am looking at:
24-JAN-13 04.29.19.989847000 PM
28-JAN-13 08.52.27.187015000 PM
Here is my proc:
procedure sps_get_patientmedrecs_hdr (
p_statusmnemonic_in in varchararrayplstype,
p_processtypemnemonic_in in transactionprocesslog.processtypemnemonic%type,
p_estimatedenddt_in in membermedicalreconcilationhdr.estimatedenddt%type,
p_return_cur_out out sys_refcursor,
p_err_code_out out number,
p_err_mesg_out out varchar2)
is
lv_varchararray varchararray := varchararray();
begin
if p_statusmnemonic_in.count > 0
then
for rec1 in 1..p_statusmnemonic_in.count
loop
lv_varchararray.extend(1);
lv_varchararray(rec1) := p_statusmnemonic_in(rec1);
end loop;
open p_return_cur_out for
select h.membermedreconciliationhdrskey,
h.primarymemberplanid,
h.assigneduserid,
h.accountorgid,
h.reconciliationstatusmnemonic,
h.estimatedenddt,
h.actualenddt,
h.inserteddt,
h.insertedby,
h.updateddt,
h.updatedby
from membermedicalreconcilationhdr h
where h.reconciliationstatusmnemonic in (select *
from table (cast(lv_varchararray as varchararray)))
and h.estimatedenddt <= nvl(p_estimatedenddt_in, h.estimatedenddt)
and not exists (select *
from transactionprocesslog tpl
where tpl.transactiontypemnemonic = 'MEDREC'
and tpl.transactionid = h.primarymemberplanid
and nvl(p_processtypemnemonic_in, tpl.processtypemnemonic) = tpl.processtypemnemonic);
else
open p_return_cur_out for
select h.membermedreconciliationhdrskey,
h.primarymemberplanid,
h.assigneduserid,
h.accountorgid,
h.reconciliationstatusmnemonic,
h.estimatedenddt,
h.actualenddt,
h.inserteddt,
h.insertedby,
h.updateddt,
h.updatedby
from membermedicalreconcilationhdr h;
end if;
p_err_code_out := 0;
exception
when others then
p_err_code_out := -1;
p_err_mesg_out := 'error in ct_cu_act_medrecon_pg.sps_get_patientmedrecs_hdr => ' || sqlerrm;
end sps_get_patientmedrecs_hdr;
I've tried the to_date function but received the same error. Any help would be appreciated, thanks in advance.
Looks like you're treating a string like it's a date, which may or may not work depending on your NLS settings. Whenever Oracle sees a string where it expects a date it tries to do an implicit date conversion based whether that string happens to match your particular session's date formatting parameter. This is a very common source of errors.
To use a proper date you can use the to_date function like so:
to_date('26-JAN-14','DD-MON-RR')
or use the native syntax for specifying date literals, which is what I would recommend:
date'2014-1-26'

Resources