Hi was trying to find out time taken for the execution of an API call: here is the code
DECLARE
l_time timestamp;
l_lapsetime VARCHAR2(100);
BEGIN
select systimestamp into l_time from dual;
--here goes my API call
select to_char((systimestamp-l_time),'HH24:MI:SS') into l_lapsetime from dual;
DBMS_OUTPUT.PUT_LINE('Time taken ' || l_lapsetime);
END;
I was expecting result to be coming in milliseconds but I’m getting output as:
Time taken +000000000 10:30:00.016938000
Isn’t this time too much??
When you substract two timestamps, the result is of type INTERVAL.
TO_CHAR is not defined on interval, what happens here is that Oracle performs a standard implicit conversion from INTERVAL to VARCHAR2, ignoring your second argument.
If you want to convert INTERVAL to characters, use EXTRACT:
SELECT extract(hour FROM v_interval) || ':'
|| extract(minute FROM v_interval) || ':'
|| extract(SECOND FROM v_interval)
FROM (SELECT ×tamp2 - ×tamp1 v_interval FROM dual)
Related
I have this procedure that goes PROCEDURE(monthday varchar2). It receives an varchar2 that represents the month and date concatenated always with the format of MMDD. I then want to create a DATE type variable that uses this month and day, and the year being the current year.
Like: desired_date DATE;
desired_date = ?
I'm using Oracle SQL Developer.
EXTRACT the year from SYSDATE and then combine using string concatenation with your input and use TO_DATE to convert to a date:
CREATE PROCEDURE test (
monthday IN VARCHAR2,
desired_date OUT DATE
)
IS
BEGIN
desired_date := TO_DATE( EXTRACT( YEAR FROM SYSDATE ) || monthday, 'YYYYMMDD' );
END;
/
then:
DECLARE
dt DATE;
BEGIN
test( '0101', dt );
DBMS_OUTPUT.PUT_LINE( '0101: ' || dt );
test( '1231', dt );
DBMS_OUTPUT.PUT_LINE( '1231: ' || dt );
BEGIN
test( '9876', dt );
DBMS_OUTPUT.PUT_LINE( '9876: ' || dt );
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE( '9876: ' || SQLERRM );
END;
END;
/
outputs:
0101: 2019-01-01 00:00:00
1231: 2019-12-31 00:00:00
9876: ORA-01843: not a valid month
db<>fiddle here
If you want to return NULL for invalid inputs then:
CREATE PROCEDURE test (
monthday IN VARCHAR2,
desired_date OUT DATE
)
IS
BEGIN
desired_date := TO_DATE( EXTRACT( YEAR FROM SYSDATE ) || monthday, 'YYYYMMDD' );
EXCEPTION
WHEN OTHERS THEN
-- In general don't catch OTHERS but in this case the only exceptions
-- are going to be from TO_DATE
desired_date := NULL;
END;
/
db<>fiddle here
Update:
You could simplify the code (as suggested by Aleksej and William Robertson) by not specifying the year value in TO_DATE and use the default which appears to be the current year; however this behaviour is not, obviously, documented in any Oracle documentation pages so I would also include inline documentation within the function so future developers reviewing your function know that you are deliberately using this behaviour:
CREATE PROCEDURE test (
monthday IN VARCHAR2,
desired_date OUT DATE
)
IS
BEGIN
-- Assumes that TO_DATE will, when not specified, default the year to the
-- current year and the time to midnight.
desired_date := TO_DATE( monthday, 'MMDD' );
END;
/
This could be a way
desired_date := to_date(monthday, 'MMDD');
According to this old post in AskTom, to_date should use the current year, if not given:
default year = current year
default month = current month
default day = 1
default hour = 0
default minute = 0
default second = 0
ops$tkyte%ORA10GR2> select sysdate, to_date( ' ', ' ' ) from dual;
SYSDATE TO_DATE('','')
-------------------- --------------------
17-aug-2012 13:41:06 01-aug-2012 00:00:00
Still unable to find this information in Oracle Docs
ORACLE (using SQL DEVELOPER).
I need to properly structure EXECUTE IMMEDIATE statement.
I do not have "create" priveleges. The task is to get number of rows per table per date for dynamic list of tables/dates.
I have the following:
DECLARE CURSOR cur_table_name IS SELECT TABLE_NAME
FROM ALL_TABLES WHERE TABLE_NAME IN ('table_a', 'table_b', 'table_c');
CURSOR cur_BEGIN_DATE IS
select to_date('2014-09-25 00:00:00', 'YYYY-MM-DD HH24:MI:SS') + rownum -1 AS BEGIN_DATE,
to_date('2014-09-26 00:00:00', 'YYYY-MM-DD HH24:MI:SS') + rownum -1 AS END_DATE from dual
Connect by level <= to_date('2014-09-30 00:00:00', 'YYYY-MM-DD HH24:MI:SS') - to_date('2014-09-25 00:00:00', 'YYYY-MM-DD HH24:MI:SS') + 1;
var_total_rows NUMBER(15);
var_table_name VARCHAR2 (50);
var_bgn_date DATE;
var_end_date DATE;
BEGIN
OPEN cur_TABLE_NAME;
LOOP
FETCH cur_TABLE_NAME INTO var_table_name;
EXIT WHEN cur_TABLE_NAME%NOTFOUND;
--testing output
DBMS_OUTPUT.PUT_LINE ('Table: '|| var_table_name);
var_total_rows :=0;
OPEN cur_BEGIN_DATE;
LOOP
FETCH cur_BEGIN_DATE INTO var_bgn_date, var_end_date;
EXIT WHEN cur_BEGIN_DATE%NOTFOUND;
--TESTING OUTPUT
DBMS_OUTPUT.PUT_LINE ('DATES ARE: ' || var_bgn_date || ', ' ||var_end_date|| ' Table IS: '||var_table_name);
--------THIS IS THE NOT WORKING STATEMENT DUE TO VARIABLES IN THE WHERE STATEMENT:
execute immediate 'SELECT COUNT(*) FROM '||var_table_name || ' where DTM >= '|| var_bgn_date ||' and DTM < '||var_end_date INTO var_total_rows;
DBMS_OUTPUT.PUT_LINE (var_table_name||' '||var_bgn_date||' '||var_end_date ||' '||var_total_rows);
END LOOP;
CLOSE cur_BEGIN_DATE;
END LOOP;
CLOSE cur_TABLE_NAME;
END;
If I remove the variables from the where statement (just do 'Select * from || var_table_name into var_total_rows; ) this works. And if there is a static value in the where clause - it works (but loops through with the same date and I need the changing dates!). But I cannot make the syntax to work for dynamic variables in the where clause.
Can this be done?
Appreciate your help!
Your var_bgn_date and var_end_date variables are of DATE type, but are being plugged into the dynamic statement as unquoted strings with implicit formatting based on the session NLS_DATE_FORMAT value. You'll get a generated statement like:
SELECT COUNT(*) FROM table_a where DTM >= 2014-09-25 00:00:00 and DTM < 2014-09-26 00:00:00
You could add escaped single quotes to turn that into a valid statement, still relying on implicit conversion back using the same NLS settings:
EXECUTE immediate 'SELECT COUNT(*) FROM '||var_table_name
|| ' where DTM >= '''|| var_bgn_date ||''' and DTM < '''||var_end_date ||''''
INTO var_total_rows;
which would generate:
SELECT COUNT(*) FROM table_a where DTM >= '2014-09-25 00:00:00' and DTM < '2014-09-26 00:00:00'
But really you should use bind variables to avoid any conversion to or from strings:
EXECUTE immediate 'SELECT COUNT(*) FROM '||var_table_name
|| ' where DTM >= :bgn_date and DTM < :end_date'
INTO var_total_rows
USING var_bgn_date, var_end_date;
I am struggling with some case...
there is table, in which I have employee attendance records, for example:
for empID=1;
empID time Type date
-------------------------------
1 9:22 in sameday
1 11:23 out sameday
1 14:35 in sameday
1 16:21 out sameday
particularly, I want some fn/procedure that will take an EmpID and DATE parameters, and then based on this data if I'll write: select proc(EmployeeID, Date) from dual(or maybe some other table?) it should do such a work:
take first couples in table (table is ordered be ascending as default order), then calculate FROM first OUT (11:23) to first IN(9:22) time, save that time somewhere (int tempResult) and then calculate second couple, and calculate second tempResult and in finalResult, it should count the total time, like finalResult+=finalResult+tempResult (if it has been an iterations in loop);
I think it would be done someway like, in foreach (or whatever it is in pl/sql oracle) take first select with top result, then, second.. and so forth... and on each iteration calculate desire goal.
so.. logics is ok with me I think :), but the problem is that I'm not that familiar with PL/SQL, if it had been written in Java it should have come easy to me.
I will pay lots of Thanks to some one who will help me...
its crucial for me to day.
thanks in advance.
I have Date and Time is separate columns, like:
date time
----------------------
11-09-2013 12:34
so, I made little change like this
FOR rec IN
( SELECT t.EID, to_char(t.devent_date, 'DD.MM.YY') ||' '|| t.RegTime, t.acttype from turnicate_ie t WHERE t.EID = i_emp_id ORDER BY t.EID
)
LOOP
but it states that package or function is in incorrect state...
(t.devent_date is 11.09.2013, t.RegTime is 16:23)
The below will give you some idea of using plsql:
you need to use many logic of calculating total working hours, like multiple inputs within same time, multiple empId etc.
create table my_test ( empId number, log_time date, type varchar2(3));
INSERT INTO my_test VALUES( 1, to_date('11/sep/2013 09:22:00 am', 'dd/mon/yyyy hh:mi:ss am'), 'in');
INSERT INTO my_test VALUES( 1, to_date('11/sep/2013 11:23:00 am', 'dd/mon/yyyy hh:mi:ss am'), 'out');
INSERT INTO my_test VALUES( 1, to_date('11/sep/2013 02:35:00 pm', 'dd/mon/yyyy hh:mi:ss pm'), 'in');
INSERT INTO my_test VALUES( 1, to_date('11/sep/2013 04:21:00 pm', 'dd/mon/yyyy hh:mi:ss pm'), 'out');
CREATE OR REPLACE
FUNCTION total_hours(
i_emp_id IN NUMBER)
RETURN VARCHAR2
IS
l_total_seconds NUMBER := 0;
in_time DATE;
l_total_time VARCHAR2(20);
BEGIN
FOR rec IN
( SELECT log_time, type FROM my_test WHERE empid = i_emp_id ORDER BY log_time
)
LOOP
IF rec.TYPE = 'in' AND in_time IS NULL THEN
in_time := rec.log_time;
END IF;
IF rec.TYPE = 'out' AND in_time IS NOT NULL THEN
l_total_seconds := (rec.log_time - in_time)*24*60*60 + l_total_seconds;
in_time := NULL;
END IF;
END LOOP;
SELECT TO_CHAR(TRUNC(l_total_seconds/3600), 'FM999999990')
|| 'hh '
|| TO_CHAR(TRUNC(mod(l_total_seconds,3600)/60), 'FM00')
|| 'mm '
|| TO_CHAR(mod(l_total_seconds,60), 'FM00')
||'ss'
INTO l_total_time
FROM dual;
RETURN l_total_time;
END;
/
SELECT total_hours(1) from dual;
I have requirement where i have to get data of particular day. So my ideal startdate should be 2013-06-07 00:00:01 AM and end date 2013-06-07 23:59:59 AM
Hence i have written this code.
create or replace
PROCEDURE checkChanges
IS
vc_startDate timestamp;
vc_endDate timestamp;
begin
vc_startDate :=to_timestamp(TO_CHAR(trunc(systimestamp)-40+((24*60*60)-1)/(24*60*60),'yyyy-mm-dd hh24:mi:ss'),'yyyy-mm-dd hh24:mi:ss');
vc_endDate :=to_timestamp(TO_CHAR(trunc(systimestamp)+1/(24*60*60),'yyyy-mm-dd hh24:mi:ss'),'yyyy-mm-dd hh24:mi:ss');
Dbms_Output.Put_Line('vc_startDate ' ||vc_startDate);
Dbms_Output.Put_Line('vc_endDate ' ||vc_endDate);
SELECT EMAIL_ADRESS FROM SOMETABLE A,B
AND A.CREATE_TS BETWEEN vc_startDate AND vc_endDate ORDER BY B.START_DT;
end checkChanges;
But the start date and end date i am getting is quite different.
start date:07-JUN-13 12.00.01.000000 AM
end date: 07-JUN-13 11.59.59.000000 PM
Here's a simple way to do this.
DECLARE
v_start TIMESTAMP;
v_end TIMESTAMP;
BEGIN
v_start := TRUNC (SYSTIMESTAMP) + NUMTODSINTERVAL (1, 'second'); --truncate the timestamp and add one second
DBMS_OUTPUT.PUT_LINE (TO_CHAR (v_start, 'yyyy-mm-dd hh24:mi:ss'));
/*alternate way
v_start := TRUNC (SYSTIMESTAMP) + INTERVAL '0 0:0:1' DAY TO SECOND; --truncate the timestamp and add one second
DBMS_OUTPUT.PUT_LINE (TO_CHAR (v_start, 'yyyy-mm-dd hh24:mi:ss AM'));*/
v_end :=
TRUNC (SYSTIMESTAMP) --trunacate the timestamp
+ INTERVAL '1 0:0:0.0' DAY TO SECOND --add a day
- NUMTODSINTERVAL (1, 'second'); --substract a second
DBMS_OUTPUT.PUT_LINE (TO_CHAR (v_end, 'yyyy-mm-dd hh24:mi:ss'));
/*alternate way
v_end := TRUNC (SYSTIMESTAMP) --trunacate the timestamp
+ INTERVAL '0 23:59:59' DAY TO SECOND; --add hours, mins, s
DBMS_OUTPUT.PUT_LINE (TO_CHAR (v_end, 'yyyy-mm-dd hh24:mi:ss AM'));*/
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.PUT_LINE (SQLERRM);
END;
Output:
2013-06-07 00:00:01 AM
2013-06-07 23:59:59 PM
UPDATE
I also printed the values without converting to to_char and it displayed similar to what u got.
DBMS_OUTPUT.PUT_LINE (v_start);
DBMS_OUTPUT.PUT_LINE (v_end);
Output:
07-JUN-13 12.00.01.000000 AM
07-JUN-13 11.59.59.000000 PM
So, it seems what you are doing is correct(but, little complicated). When displaying the timestamp it is getting displayed according to the NLS parameters. Check that using
SELECT *
FROM nls_session_parameters
WHERE parameter = 'NLS_TIMESTAMP_FORMAT';
I guess it will return this DD-MON-RR HH.MI.SSXFF AM
Do not worry about how it is being displayed. A date/timestamp variable has no format. It matters only when you want to print it. If don't use to_char function, it'll take the format mask as defined in NLS parameters. If you want to override it, use to_char function and specify the mask.But when used in the query, it'll have the correct value.
I need at some point to increment dynamically a timestamp plsql variable.
So, instead of doing this:
timestamp_ := timestamp_ + INTERVAL '1' DAY;
I would like to do thomething like this:
timestamp_ := timestamp_ + INTERVAL days_ DAY;
It doesn't really work.
My final goal is to dynamically create some scheduler jobs for some entities that have an variable expiration date, to avoid creating a single one which would be often executed.
It sounds like you want
timestamp_ := timestamp + numtodsinterval( days_, 'day' );
I would be somewhat cautious, however, about an architecture that involves creating thousands of scheduler jobs rather than one job that runs periodically to clear out expired rows. A single job is a heck of a lot easier to manage and oversee.
Special note:
1. INTERVAL YEAR TO MONTH and
2. INTERVAL DAY TO SECOND
are the only two valid interval datatypes;
Sample Example:
=============================
DECLARE
l_time INTERVAL YEAR TO MONTH;
l_newtime TIMESTAMP;
l_year PLS_INTEGER := 5;
l_month PLS_INTEGER := 11;
BEGIN
-- Notes :
-- 1. format is using "-" to connect year and month
-- 2. No need to mention any other keyword ; Implicit conversion takes place to set interval
l_time := l_year || '-' || l_month;
DBMS_OUTPUT.put_line ( l_time );
SELECT SYSTIMESTAMP + l_time INTO l_newtime FROM DUAL;
DBMS_OUTPUT.put_line ( 'System Timestamp :' || SYSTIMESTAMP );
DBMS_OUTPUT.put_line ( 'New Timestamp After Addition :' || l_newtime );
END;
=============================
Try this:
DECLARE
l_val NUMBER;
l_result VARCHAR2( 20 );
BEGIN
l_val := 1;
SELECT SYSDATE - INTERVAL '1' DAY * l_val INTO l_result FROM DUAL;
DBMS_OUTPUT.put_line( 'Current Date is ' || SYSDATE || ' minus ' || l_val || ' day(s) is ' || l_result );
END;
Output will be:
Current Date is 25-FEB-16 minus 1 day(s) is 24-FEB-16