Pl sql date initialization comparsion ++ - oracle

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.

Related

How to convert string in 'MMDD' format into a date using the current year

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

How to get the first and last date of previous month without using to_date, trunc and predefined add_months function

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

Error in increasing Datetime in Oracle/PLSQL

Here I'm using a loop from StartDateTime to EndDatetime and adding 1 hour in every iteration. Everything is working in Loop.But problem is in insert query.Please Check the insert query.
declare
StartDateTime TIMESTAMP :=to_date( '2017-01-01 00:00:00','yyyy-mm-dd
hh24:mi:ss');
EndDateTime TIMESTAMP :=to_date( '2017-12-31 00:00:00','yyyy-mm-dd
hh24:mi:ss');
dateti TIMESTAMP;
dateti2 TIMESTAMP;
StartDateTime1 TIMESTAMP;
sub INTEGER;
semester Number;
begin
sub:=( CAST( EndDateTime AS DATE ) - CAST( StartDateTime AS DATE ) ) ;
FOR i IN 0 .. 1
LOOP
StartDateTime1:=StartDateTime+i;
for idx in 0..2 loop
dateti:=to_date(StartDateTime1+(idx/24.0),'yyyy-mm-dd hh24:mi:ss');
dateti2:=to_date(StartDateTime1+((idx+1)/24.0)+ interval '-1' second,'yyyy-
mm-dd hh24:mi:ss');
case
when to_number(to_char(dateti ,'Q'))>6 then semester:=to_number(2);
else semester:=to_number(1);
end case;
Problem start from here in insert query.It saying non numeric character found.DateSlotStart and DateSlotEnd datatype is Timestramp .Please see the image and advise what should I change?
insert into DimDate1(DateSlotStart,DateSlotEnd,
"Date",SlotName,MonthName,MonthNumberOfYear,Quarter,Year,Semester) values
(to_date(dateti ,'yyyy-mm-dd hh24:mi:ss') ,to_date(dateti2 ,'yyyy-mm-dd
hh24:mi:ss') ,
to_date(dateti ,'yyyy-mm-dd'),to_char(dateti ,'hh24:mi' )||' To
'||to_char(dateti2 ,'hh24:mi' ),to_char(dateti
,'Month'),to_number(to_char(dateti ,'mm')),to_number(to_char(dateti ,'Q')),
to_number(to_char(dateti ,'YYYY')) , semester);
end loop;
END LOOP;
end;
/
Please also check the this
The problem is that you are trying to convert TIMESTAMP to date where it is not required. And it is always better to use CAST when you need to convert it to DATE. Replace your insert with this. It should work.
INSERT INTO dimdate1 (
dateslotstart,
dateslotend,
"Date",
slotname,
monthname,
monthnumberofyear,
quarter,
year,
semester
) VALUES (
dateti,
dateti2,
CAST (dateti AS DATE),
TO_CHAR(dateti,'hh24:mi')
|| ' To '
|| TO_CHAR(dateti2,'hh24:mi'),
TO_CHAR(dateti,'Month'),
to_number(TO_CHAR(dateti,'mm') ),
to_number(TO_CHAR(dateti,'Q') ),
to_number(TO_CHAR(dateti,'YYYY') ),
semester
);

PLSQL: random timestamp between 2 timestamps

I have a timestamp which represents the current timestamp:
now := CURRENT_TIMESTAMP;
tomorrow := now + INTERVAL '1' DAY;
Now I want to render a random timestamp between those 2 values,
I tried with DBMS_RANDOM but it doesn't work
Any Help?
This will generate a random timestamp between the current timestamp and sysdate+1:
SELECT TO_DATE(SYSDATE, 'MM/DD/YYYY HH24:MI:SS') +
dbms_random.value(0, TO_DATE(SYSDATE, 'MM/DD/YYYY HH24:MI:SS') -
TO_DATE(SYSDATE, 'MM/DD/YYYY HH24:MI:SS')+1)
FROM dual;
As PL/SQL:
DECLARE
l_ran_time TIMESTAMP;
BEGIN
SELECT SYSDATE +
dbms_random.value(0, SYSDATE -
SYSDATE+1)
INTO l_ran_time
FROM dual;
dbms_output.put_line(l_ran_time);
END;
/

SQL Developer - Using start and end date variables in where clause of my query

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

Resources