Can you please help in converting minutes to the format of ('HH24:MI').
I am getting the result in integer which is the minutes. So, how to convert them?
Thanks
Assuming, you want to convert 492 minutes:
select to_char(trunc(sysdate) + 492/24/60, 'hh24:mi') from dual;
If you want a function:
create or replace
function tq84_convert_minutes_hh24mm (p_minutes in number)
return varchar2 is
begin
return to_char (trunc(sysdate) + p_minutes / 24/60, 'hh24:mi');
end tq84_convert_minutes_hh24mm;
/
Later ...
begin
dbms_output.put_line (tq84_convert_minutes_hh24mm(492));
end;
/
Another way:
WITH c AS
(SELECT 492 AS MINUTES FROM DUAL)
SELECT TRIM(TO_CHAR(TRUNC(MINUTES / 60), '09')) || ':' ||
TRIM(TO_CHAR(TRUNC(MOD(ABS(MINUTES), 60)), '09')) AS HHMM
FROM C
This will have issues if the time exceeds 1440 minutes (24 hours) but it gives you something to start with.
Share and enjoy.
This can save you, but I had problems with time greater than 1440 minutes.
select to_char(trunc(sysdate) + [MINUTES]/24/60, 'hh24:mi') from dual;
So I did a function that verifies if minute is greater or equal 1440:
IF (QT_MINUTES_P IS NOT NULL) THEN
IF (QT_MINUTES_P >= 1440) THEN
SELECT ROUND(QT_MINUTES_P/ 60) ||':'|| MOD(QT_MINUTES_P, 60)
INTO DS_RESULT
FROM DUAL;
ELSE
SELECT TO_CHAR(TRUNC(SYSDATE) + (QT_MINUTES_P)/24/60, 'hh24:mi')
INTO DS_RESULT
FROM DUAL;
END IF;
END IF;
Related
I wrote the below code but I did not get the expected output:
SQL> select TO_CHAR(TO_CHAR(sysdate,'MM')-1,'DD-MM-YYYY') AS PREV_MON_FIRST,
TO_CHAR(TO_CHAR(sysdate,'MM'),'DD-MM-YYYY')-1 AS PREV_MON_LAST from dual;
select TO_CHAR(TO_CHAR(sysdate,'MM')-1,'DD-MM-YYYY') AS PREV_MON from dual
*
ERROR at line 1:
ORA-01481: invalid number format model
It's correct that 'DD' and 'YYYY' values are missing but when I tried to retrieve only month also it shows the same error
You can use the following solution:
SELECT
TRUNC(LAST_DAY(ADD_MONTHS(sysdate, -2))) + 1 AS first_date,
TRUNC(LAST_DAY(ADD_MONTHS(sysdate, -1))) AS last_date
FROM dual
Selecting the first day of the previous month using TO_CHAR only is easy, if YYYYMMDD format suits your needs:
SQL> select (to_char(sysdate, 'yyyymm') - 1) || '01' first_day
2 from dual;
FIRST_DAY
------------------------------------------
20180701
SQL>
But, for the last day - I have no idea.
Usual way to do it works, but involves other functions:
SQL> select trunc(add_months(sysdate, -1), 'mm') first_day,
2 trunc(sysdate, 'mm') - 1 last_day
3 from dual;
FIRST_DAY LAST_DAY
---------- ----------
2018-07-01 2018-07-31
SQL>
For LAST_DAY here is the query with hard_code of values of month without using add_months,trunc,to_date
select to_char(sysdate,'yyyy')||
to_char(sysdate,'mm')-1||
case when to_char(sysdate,'mm')-1 in (1,3,5,7,8,10,12) THEN 31
when to_char(sysdate,'mm')-1 in (4,6,9,11) THEN 30
when mod(to_char(sysdate,'yyyy'),4)=0 and to_char(sysdate,'mm')-1=2 THEN 29
when mod(to_char(sysdate,'yyyy'),4)!=0 and to_char(sysdate,'mm')-1=2 THEN 28
end
from dual
If I take your question literally one solution is
CREATE OR REPLACE FUNCTION MY_LAST_DAY RETURN TIMESTAMP AS
next_run_date TIMESTAMP;
return_date_after TIMESTAMP := SYSTIMESTAMP - INTERVAL '100' DAY;
BEGIN
LOOP
DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING('FREQ=DAILY;INTERVAL=1;BYMONTHDAY=-1', NULL, return_date_after, next_run_date);
EXIT WHEN EXTRACT(MONTH FROM next_run_date) = EXTRACT(MONTH FROM SYSTIMESTAMP);
return_date_after := next_run_date;
END LOOP;
RETURN return_date_after;
END;
/
CREATE OR REPLACE FUNCTION MY_FIRST_DAY RETURN TIMESTAMP AS
next_run_date TIMESTAMP;
return_date_after TIMESTAMP := SYSTIMESTAMP - INTERVAL '100' DAY;
BEGIN
LOOP
DBMS_SCHEDULER.EVALUATE_CALENDAR_STRING('FREQ=DAILY;INTERVAL=1;BYMONTHDAY=1', NULL, return_date_after, next_run_date);
EXIT WHEN EXTRACT(MONTH FROM next_run_date) = EXTRACT(MONTH FROM SYSTIMESTAMP);
return_date_after := next_run_date;
END LOOP;
RETURN return_date_after;
END;
/
It returns the first and last date of previous month without using TO_DATE (or TO_TIMESTAMP), TRUNC or ADD_MONTHS
But I hope you agree that your question is rather stupid.
For LAST_DAY here is the query with hard_code of values of month without using add_months,trunc,to_date
select to_char(sysdate,'yyyy')||
to_char(sysdate,'mm')-1||
case when to_char(sysdate,'mm')-1 in (1,3,5,7,8,10,12) THEN 31
when to_char(sysdate,'mm')-1 in (4,6,9,11) THEN 30
when mod(to_char(sysdate,'yyyy'),4)=0 and to_char(sysdate,'mm')-1=2 THEN 29
when mod(to_char(sysdate,'yyyy'),4)!=0 and to_char(sysdate,'mm')-1=2 THEN 28
end
from dual
New Query to handle Janurary and Leap year :
select decode(to_char(sysdate,'mm'),1,to_char(sysdate,'yyyy')-1 ,to_char(sysdate,'yyyy'))||
decode(to_char(sysdate,'mm'),1,12, to_char(sysdate,'mm')-1)||
CASE WHEN to_char(sysdate,'mm')-1 in (1,3,5,7,8,10,0) THEN 31
WHEN to_char(sysdate,'mm')-1 in (4,6,9,11) THEN 30
WHEN to_char(sysdate,'mm')-1=2 AND mod(to_char(sysdate,'yyyy'),4)!=0 AND mod(to_char(sysdate,'yyyy'),400)!=0 THEN 29
ELSE 28
END
FROM dual
I've been trying to get the duration between two dates in my question's particular format. It would be easy for duration in days as we can subtract or if it's in months, we can use the months_between function..
i would need it to convert in weeks and days.. the other one would be months and days.. is there a function that can do this in oracle? i've been searching online if anyone is doing something similar like i need to achieve to, but cant seem to find a close one.
Could anyone help me on this?
im using oracle 11g.
examples : range between 2014/12/16 and 2014/12/01 is 2weeks 1day
Thanks.
Converting a raw number of days into a number of weeks and days is pretty simple. Just divide the total by 7 to get the weeks and then calculate the remaining days
SQL> ed
Wrote file afiedt.buf
1 declare
2 total_days integer;
3 total_weeks integer;
4 remaining_days integer;
5 begin
6 total_days := date '2014-12-16' - date '2014-12-01';
7 total_weeks := total_days/ 7;
8 remaining_days := total_days - total_weeks * 7;
9 dbms_output.put_line( total_weeks || ' weeks, ' || remaining_days || ' days.' );
10* end;
SQL> /
2 weeks, 1 days.
PL/SQL procedure successfully completed.
Months and days is a bit more complicated because that's not very well defined-- you'll have to define a number of rules. For example, months_between returns 1 for the number of months between November 30 and December 30. It also returns 1 for the number of months between November 30 and December 31. Is that the result that you would want? Or do you always want your displayed difference to increase if the number of days between the two dates increases?
In SQL, you could achieve this using TRUNC and ROUND with the logic that difference in days needs to be divided by 7 to get the number of weeks.
SQL> WITH DATA AS
2 ( SELECT DATE '2014-12-16' start_dt, DATE '2014-12-01' end_dt FROM dual
3 )
4 SELECT TRUNC((start_dt -end_dt)/7)
5 ||' weeks '
6 || ROUND(((start_dt -end_dt)/7 - TRUNC((start_dt -end_dt)/7))*7)
7 || ' days' week_days
8 FROM DATA
9 /
WEEK_DAYS
--------------
2 weeks 1 days
SQL>
You can try this one it may help you, you can rearrange the output as per your need, as its display is not much clear to me with reference to your question,generally we consider month as of 30 days so i used divide by 30.
create or replace function timediff(fdate date,tdate date)
return varchar2
as
tot_duration varchar2(25);
begin
select floor(days/30)||' Month '||floor(mod(days,30)/7)||' Week '
||mod(mod(days,30),7)||' Day ' into tot_duration
from
(
select (tdate-fdate) days from dual
);
return tot_duration;
end;/
Call the above function:-
select timediff('10.nov.2014','30.dec.2014') duration from dual;
Output result:-
Duration
1 Month 2 Week 6 Day
This query give you months/weeks/days of a range of dates:
SELECT trunc(MONTHS_BETWEEN (end_date, start_date)) months,
floor((end_date - add_months( start_date, trunc(MONTHS_BETWEEN (end_date, start_date)) ))/7) weeks,
mod(end_date - add_months( start_date, trunc(MONTHS_BETWEEN (end_date, start_date)) ),7) days
FROM (SELECT TO_DATE ('2014/12/16', 'yyyy/mm/dd') end_date,
TO_DATE ('2014/12/01', 'yyyy/mm/dd') start_date
FROM DUAL);
You can also format it as you want. For example:
SELECT trunc(MONTHS_BETWEEN (end_date, start_date)) || 'months ' ||
floor((end_date - add_months( start_date, trunc(MONTHS_BETWEEN (end_date, start_date)) ))/7) ||'weeks '||
mod(end_date - add_months( start_date, trunc(MONTHS_BETWEEN (end_date, start_date)) ),7) ||'days' date_values
FROM (SELECT TO_DATE ('2014/12/16', 'yyyy/mm/dd') end_date ,
TO_DATE ('2014/12/01', 'yyyy/mm/dd') start_date
FROM DUAL);
Returns:
DATE_VALUES
--------------------
0months 2weeks 1days.
I hope this helps you. Sorry for late answer.
CREATE FUNCTION time_spell(p_days VARCHAR2) RETURN VARCHAR2 IS
v_y NUMBER;
v_yd NUMBER;
v_m NUMBER;
v_md NUMBER;
v_d NUMBER;
v_w NUMBER;
v_wd NUMBER;
v_days NUMBER;
v_yi NUMBER;
v_mi NUMBER;
v_wi NUMBER;
BEGIN
SELECT p_days/365 INTO v_y FROM dual;
SELECT trunc(v_y) INTO v_yi FROM dual;
select substr(v_y,(select instr(v_y,'.') from dual)) INTO v_yd from dual;
SELECT v_yd * 12 INTO v_m FROM dual;
SELECT trunc(v_m) INTO v_mi FROM dual;
select substr(v_m,(select instr(v_m,'.') from dual)) INTO v_md from dual;
SELECT v_md * 30 INTO v_d FROM dual;
SELECT v_d / 7 INTO v_w FROM dual;
SELECT trunc(v_w) INTO v_wi FROM dual;
select substr(v_w,(select instr(v_w,'.') from dual)) INTO v_wd from dual;
SELECT trunc(v_wd * 7) INTO v_days FROM dual;
RETURN v_yi||' Year(s) '||v_mi||' Month(s) '||v_wi||' Week(s) '||v_days||' Day(s) ';
END;
select time_spell(sysdate- to_date('27-Jul-2014','DD-Mon-YYYY')) from dual;
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)
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
I have a query that I run everyday that requires a StartDate and EndDate value. The StartDate and EndDate used to be a manual input but I am trying to get away from that and calculate the StartDate and EndDate to be used in the query. I've developed code to capture the StartDate and EndDate in variables:
DECLARE
c_DateMask VARCHAR2(20) := 'DD-Mon-YYYY';
c_TimeMask VARCHAR2(20) := 'HH24:MI';
v_Month char(4) := 'Prev';
v_StartDate date;
v_EndDate date;
v_Environment char(7) := 'Prod';
BEGIN
if v_MONTH = 'Prev'
THEN
select TO_DATE ('01-' || TO_CHAR (ADD_MONTHS (SYSDATE, -1),'mon-yyyy')) into v_StartDate from dual;
select Last_day(TO_DATE('01-' || TO_CHAR (ADD_MONTHS (SYSDATE, -1),'mon-yyyy'))) into v_EndDate from dual;
ELSE
select TO_DATE ('01-' || TO_CHAR (ADD_MONTHS (SYSDATE, 0),'mon-yyyy')) into v_StartDate from dual;
CASE
WHEN v_Environment = 'Prod'
THEN
-- Production Environment --
select
to_char(sysdate, 'dd-Mon-yyyy ') ||
case
when to_char(sysdate, 'mi') between 00 and 20
then to_char(sysdate, 'hh24')-1||':58'||':00'
when to_char(sysdate, 'mi') between 21 and 40
then to_char(sysdate, ' hh24')||':18'||':00'
when to_char(sysdate, 'mi') between 41 and 60
then to_char(sysdate, ' hh24')||':38'||':00'
END
into v_EndDate from dual;
WHEN v_Environment = 'OldTest'
THEN
-- Test Environment --
select
to_char(sysdate, 'dd-Mon-yyyy ') ||
case
when to_char(sysdate, 'mi') between 10 and 30
then to_char(sysdate, 'hh24')||':08'||':00'
when to_char(sysdate, 'mi') between 31 and 50
then to_char(sysdate, ' hh24')||':28'||':00'
when to_char(sysdate, 'mi') between 51 and 60
then to_char(sysdate, ' hh24')||':48'||':00'
END
into v_EndDate from dual;
end case;
end if;
I then want to use the variables in my select statement below:
-----------------
/* KPI Figures */
-----------------
SELECT
SysDate as RunTime
, v_StartDate
, v_EndDate
, TTM_OFF_CONTRIBUTOR
, SUM(NVL(TTM_PER_OFF_FEE,0)) Fees
FROM [Table]
Where
TTM_PROCESSED_DATE >= v_StartDate
AND TTM_PROCESSED_DATE <= v_EndDate
group by SysDate, v_StartDate, v_EndDate, TTM_OFF_CONTRIBUTOR
END;
It all works up until when I try to use the variable values in the KPI Figures query. What am I missing?
Update:
Regarding Phil's answer: I tried but it didn't work and I get the following error:
PLS-00428: an INTO clause is expected in this SELECT statement.
I am sure I saw another response yesterday which is now gone relating to being able to assign multiple values to variables or something.
Is that what I need and how would I do that?
The variables v_StartDate and v_EndDate are only in scope within the PL/SQL block where they are declared. It looks like you are then trying to use them outside the block in a separate query. To do that you will need to create SQL Developer bind variables outside the PL/SQL block like this:
var v_start_date varchar2(11)
var v_end_date varchar2(11)
Then reference these as bind variables in both the PL/SQL block and the SQL query:
declare
...
begin
....
:v_start_date := '01-' || TO_CHAR (ADD_MONTHS (SYSDATE, -1), 'mon-yyyy');
:v_end_date := TO_CHAR(Last_day(TO_DATE('01-'
|| TO_CHAR (ADD_MONTHS (SYSDATE, -1),'mon-yyyy'))));
-- (NB No need to select from dual)
...
end;
SQL:
...
Where
TTM_PROCESSED_DATE >= TO_DATE(:v_StartDate)
AND TTM_PROCESSED_DATE <= TO_DATE(:v_EndDate)
Note that these variables cannot be declared with a type of DATE, so they need to be converted back to dates in the query (using the correct format mask).
I suggest you wrap the startdate/enddate in two functions (stored procedures)
create or replace function get_startdate(p_month in varchar2, p_env in varchar2)
return date
is
l_return date;
begin
... logic goes here ...
return l_return;
end;
Similar for enddate.
Then use those functions in your query:
SELECT
SysDate as RunTime
, get_startdate('Prev', 'Prod')
, get_enddate('Prev', 'Prod')
, TTM_OFF_CONTRIBUTOR
, SUM(NVL(TTM_PER_OFF_FEE,0)) Fees
FROM [Table]
Where
TTM_PROCESSED_DATE >= get_startdate('Prev', 'Prod')
AND TTM_PROCESSED_DATE <= get_enddate('Prev', 'Prod')
group by SysDate, get_startdate('Prev', 'Prod'), get_enddate('Prev', 'Prod'), TTM_OFF_CONTRIBUTOR
Sidenote: please consider a more concise version to determine first and last day of a month. You also can just assign a value to a variable without using SELECT INTO, e.g.
startdate := trunc(sysdate, 'MM'); -- first day of current month
enddate := last_day(trunc(sysdate, 'MM')); -- last day of current month
To calculate with hours, you could add/subtract a number of minutes, that's more readable than all the to_date/to_char conversions you do, e.g.
enddate := trunc(sysdate, 'HH24');
case
when to_char(sysdate, 'mi') between 00 and 20 then enddate := enddate - 2/(24*60);
when to_char(sysdate, 'mi') between 21 and 40 then enddate := enddate + 18/(24*60);
when to_char(sysdate, 'mi') between 41 and 60 then enddate := enddate + 38/(24*60);
end;
Good luck, Martin
I think the issue is with your query and aliases. Try
SELECT
SysDate as RunTime
, v_StartDate AS StartDate
, v_EndDate AS EndDate
, TTM_OFF_CONTRIBUTOR
, SUM(NVL(TTM_PER_OFF_FEE,0)) Fees
FROM [Table]
Where
TTM_PROCESSED_DATE >= v_StartDate
AND TTM_PROCESSED_DATE <= v_EndDate
group by RunTime, StartDate, EndDate, TTM_OFF_CONTRIBUTOR