i got financial year 1st month April and 1st week date using (:weekno) from this query
select TO_CHAR(ADD_MONTHS((Select ((TRUNC (Trunc(sysdate,'yyyy')+( :weekno)*7,'IW'))+1) fdt from duaL),+3))
from dual
Output got:03-APR-17
but i want output this format 'YYYY/DD/MM' 04/03/2017
You are using dual twice. There is no need for that.
Use to_char with yyyy/mm/dd format to get 2017/03/04:
select
to_char(
add_months(
trunc(
trunc(sysdate,'YY') + :week_no * 7, 'IW'
) + 1, 3
), 'yyyy/mm/dd'
) dt
from dual;
Use to_char with dd/mm/yyyy format to get 04/03/2017:
select
to_char(
add_months(
trunc(
trunc(sysdate,'YY') + :week_no * 7, 'IW'
) + 1, 3
), 'mm/dd/yyyy'
) dt
from dual;
If you need date value:
select to_date(TO_CHAR(ADD_MONTHS((Select ((TRUNC (Trunc(sysdate,'yyyy')+( :weekno)*7,'IW'))+1) fdt from duaL),+3)),'yyyy/mm/dd') from dual
If need string value:
select TO_CHAR(ADD_MONTHS((Select ((TRUNC (Trunc(sysdate,'yyyy')+( :weekno)*7,'IW'))+1) fdt from duaL),+3),'yyyy/mm/dd')from dual
Related
I have two int columns:
thedate - for example 20210512
thetime - for example 142342
So, i need to unite them to one column to check if the time difference is lower the 5.
I tried this:
TO_DATE(sysdate) - TO_DATE(thedate, 'YYYY-MM-DD') < 5
But it only for the date not for the time so i will be glad to know how to unite the two int columns + and convert it to date type for time difference.
You can use:
SELECT *
FROM table_name
WHERE TO_DATE( thedate * 1000000 + thetime, 'YYYYMMDDHH24MISS' ) > SYSDATE - 5;
Which, for the sample data:
CREATE TABLE table_name ( thedate INT, thetime INT );
INSERT INTO TABLE_NAME ( thedate, thetime ) VALUES (
TO_NUMBER( TO_CHAR( SYSDATE, 'YYYYMMDD' ) ),
TO_NUMBER( TO_CHAR( SYSDATE, 'HH24MISS' ) )
);
INSERT INTO TABLE_NAME ( thedate, thetime ) VALUES (
TO_NUMBER( TO_CHAR( SYSDATE - INTERVAL '4 23' DAY TO HOUR, 'YYYYMMDD' ) ),
TO_NUMBER( TO_CHAR( SYSDATE - INTERVAL '4 23' DAY TO HOUR, 'HH24MISS' ) )
);
INSERT INTO TABLE_NAME ( thedate, thetime ) VALUES (
TO_NUMBER( TO_CHAR( SYSDATE - INTERVAL '5 1' DAY TO HOUR, 'YYYYMMDD' ) ),
TO_NUMBER( TO_CHAR( SYSDATE - INTERVAL '5 1' DAY TO HOUR, 'HH24MISS' ) )
);
Outputs:
THEDATE
THETIME
20210512
131832
20210507
141832
Or, if you want to use indexes on the thedate and thetime columns (the query above would not use indexes on thedate and thetime columns but would require a function-based index) then:
SELECT *
FROM table_name
WHERE thedate > TO_NUMBER( TO_CHAR( SYSDATE - 5, 'YYYYMMDD' ) )
OR ( thedate = TO_NUMBER( TO_CHAR( SYSDATE - 5, 'YYYYMMDD' ) )
AND thetime >= TO_NUMBER( TO_CHAR( SYSDATE - 5, 'HH24MISS' ) )
)
However, the better solution is to use appropriate data-types for your data; in this case, you should store date values in a DATE data-type (which, in Oracle, contains year-second components) rather than as two INT values for date and time.
db<>fiddle here
You want to convert to a timestamp, not a date. So:
select to_timestamp(cast(20210512 * 1000000 + 142342 as varchar2(255)), 'YYYYMMDDHH24MISS')
from dual;
Here is a db<>fiddle showing that this works.
Or in a where clause:
to_timestamp(cast(thedate * 1000000 + thetime as varchar2(255)), 'YYYYMMDDHH24MISS') > sysdate - interval '5' day
(or whatever you mean by "5").
Note: You may want to add a computed column to the table that has the full timestamp:
alter table t add column timestamp generated always as
( to_timestamp(cast(thedate * 10000 + thetime as varchar2(255)), 'YYYYMMDDHH24MISS') );
For example, I have a TIMESTAMP field and a record is something like '2021-04-23 14:17:46' in my database, and I want to find the records that start with '2021-04-23', that is a part of the TIMESTAMP, in SQL. What would be the best way to filter part of the timestamp?
And the other way around? If I have something like '2021-03-04' and I want to find all the timestamps that are yyyy-MM-dd HH: mm: ss
If your parameter is a valid date string, use date arithmetic. For example
-- parameter
with prm as (
select '2021-04-23' pd from dual
),
-- sample data
tbl(Code, Dt) as (
select 'c1', TIMESTAMP '2021-04-23 12:17:46' from dual union all
select 'c2', TIMESTAMP '2021-04-24 14:17:46' from dual
)
--
select tbl.*
from tbl
cross join prm
where dt >= TO_DATE(prm.pd, 'YYYY-mm-DD') and dt < TO_DATE(prm.pd, 'YYYY-mm-DD') + 1
order by code, dt
select dttm_col
from table
where to_date(dttm_col) = '2020-01-20'
The simple way is to TRUNCate the timestamp and compare it to a literal:
SELECT *
FROM table_name
WHERE TRUNC( timestamp_column ) = DATE '2021-04-23'
This may be simple but its not the best solution as Oracle will not use any index on the timestamp_column and will perform a full table scan; to use an index you would need to create a function-based-index on TRUNC( timestamp_column ).
The better way, although slightly more complicated, is to compare on a range of values using a TIMESTAMP literal:
SELECT *
FROM table_name
WHERE timestamp_column >= TIMESTAMP '2021-04-23 00:00:00'
AND timestamp_column < TIMESTAMP '2021-04-23 00:00:00' + INTERVAL '1' DAY;
or, using DATE literals:
SELECT *
FROM table_name
WHERE timestamp_column >= DATE '2021-04-23'
AND timestamp_column < DATE '2021-04-23' + INTERVAL '1' DAY;
If you want to compare to a string, rather than a literal, then use TO_TIMESTAMP:
SELECT *
FROM table_name
WHERE timestamp_column >= TO_TIMESTAMP( '2021-04-23', 'YYYY-MM-DD' )
AND timestamp_column < TO_TIMESTAMP( '2021-04-23', 'YYYY-MM-DD' ) + INTERVAL '1' DAY;
or, to remove the time component:
SELECT *
FROM table_name
WHERE timestamp_column >= TO_TIMESTAMP( SUBSTR( '2021-04-23 14:17:46', 1, 10 ), 'YYYY-MM-DD' )
AND timestamp_column < TO_TIMESTAMP( SUBSTR( '2021-04-23 14:17:46', 1, 10 ), 'YYYY-MM-DD' ) + INTERVAL '1' DAY;
I have this common function which counts business days between two dates.
BUS_DAY := TRUNC(TO_DATE(P_START_DATE, D_FORMAT));
DATES_DIFF := TRUNC(TO_DATE(P_END_DATE, D_FORMAT)) - BUS_DAY;
SELECT MAX(RNUM) INTO T_DAYS
FROM (
SELECT ROWNUM RNUM
FROM ALL_OBJECTS
)
WHERE ROWNUM <= DATES_DIFF
AND TO_CHAR(BUS_DAY + RNUM, 'DY') NOT IN ('SAT', 'SUN');
My problem is it gives an incorrect day of the week for dates.
For example, today is Oct 7 2020 WEDNESDAY, but the function reads this date as a MONDAY, so it gives incorrect number of business days T_T
Anyone have the same issue or have any idea why oracle is reading dates incorrectly?
You can calculate the value without having to use a row generator and independent of the NLS_DATE_LANGUAGE.
Adapted from my answer here (which is the same problem but also ignoring holidays):
Get the number of days between the Mondays of both weeks (using TRUNC( datevalue, 'IW' ) as an NLS_LANGUAGE independent method of finding the Monday of the week) and multiply by 5/7 to give the week days of the full weeks; then
Add the day of the week (Monday = 1, Tuesday = 2, etc., to a maximum of 5 to ignore weekends) to count the part week for the end date; and
Subtract the day of the week of the start date to remove the counted values beforehand.
Like this:
SELECT ( TRUNC( end_date, 'IW' ) - TRUNC( start_date, 'IW' ) ) * 5 / 7
+ LEAST( end_date - TRUNC( end_date, 'IW' ) + 1, 5 )
- LEAST( start_date - TRUNC( start_date, 'IW' ) + 1, 5 )
AS WeekDaysDifference
FROM your_table
If you are just calculating for a single value in a function then you can avoid a context-switch to SQL and do it all in PL/SQL:
CREATE FUNCTION count_weekdays_between(
p_start_date IN DATE,
p_end_date IN DATE
) RETURN NUMBER DETERMINISTIC
IS
BEGIN
RETURN ( TRUNC( p_end_date, 'IW' ) - TRUNC( p_start_date, 'IW' ) ) * 5 / 7
+ LEAST( p_end_date - TRUNC( p_end_date, 'IW' ) + 1, 5 )
- LEAST( p_start_date - TRUNC( p_start_date, 'IW' ) + 1, 5 );
END;
/
and:
SELECT count_weekdays_between( DATE '2020-09-29', DATE '2020-10-07' )
AS num_week_days
FROM DUAL;
Outputs: 6
db<>fiddle here
Using SELECT ... FROM ALL_OBJECTS is a really ugly workaround.
What about this proposal?
DECLARE
BUS_DAY DATE := TRUNC(SYSDATE - 10);
DATES_DIFF INTEGER := TRUNC(SYSDATE - BUS_DAY);
T_DAYS INTEGER;
BEGIN
SELECT SUM(1)
INTO T_DAYS
FROM dual
WHERE TO_CHAR(BUS_DAY + LEVEL, 'DY', 'NLS_DATE_LANGUAGE = American') NOT IN ('SAT', 'SUN')
CONNECT BY LEVEL <= DATES_DIFF;
DBMS_OUTPUT.PUT_LINE ( 'T_DAYS = ' || T_DAYS );
END;
Looking just at why you see Monday, and ignoring whether this is a good approach - which #MTO has covered - then if p_start_date and p_end_date are dates, using to_date() on them is a bug, as #a_horse_with_no_name said in a comment.
If your NLS settings have YY or RR and d_format is using YYYY then today's date would end up as 0020-10-07, which was a Monday.
As a demo:
declare
P_START_DATE date := date '2020-10-07';
D_FORMAT varchar2(11) := 'DD-MON-YYYY';
BUS_DAY date;
begin
dbms_output.put_line(P_START_DATE || ' => ' || to_char(P_START_DATE, 'SYYYY-MM-DD Day'));
BUS_DAY := TRUNC(TO_DATE(P_START_DATE, D_FORMAT));
dbms_output.put_line(BUS_DAY || ' => ' || to_char(BUS_DAY, 'SYYYY-MM-DD Day'));
end;
/
07-OCT-20 => 2020-10-07 Wednesday
07-OCT-20 => 0020-10-07 Monday
When you do:
BUS_DAY := TRUNC(TO_DATE(P_START_DATE, D_FORMAT));
it's really:
BUS_DAY := TRUNC(TO_DATE(TO_CHAR(P_START_DATE), D_FORMAT));
and that implicit TO_CHAR(P_START_DATE) is using your NLS settings, so it's something like:
BUS_DAY := TRUNC(TO_DATE(TO_CHAR(P_START_DATE, 'DD-MON-RR'), D_FORMAT));
You end up with that intermediate string value as '07-OCT-20'. If you convert that back to a date with a YYYY year component in the format mask then the year is seen as 0020, not 2020:
select to_char(to_date('07-OCT-20', 'DD-MON-YYYY'), 'DD-MON-YYYY') from dual;
07-OCT-0020
You don't need to convert to and from a string, and you're already truncating to set any time part to midnight, so you only need that part:
BUS_DAY := TRUNC(P_START_DATE);
db<>fiddle
Some clients use their own display preferences rather than NLS settings, so you may be seeing the date as 07-Oct-2020 when you query, while the NLS setting has YY or RR. You can query nls_session_parameters to check.
I have a date field in oracle which returns
17-APR-19 12:00:00 AM
I also have a time column (VARCHAR) which returns HHMM in Military
1810
I'd like to combine these two fields to create a timestamp that is formatted to RFC 3339 standards. Preferable like this.
2019-04-17T18:10:00Z
I can convert a timestamp into the correct time using this:
SELECT
TO_CHAR(
SYSTIMESTAMP AT TIME ZONE 'UTC',
'yyyy-mm-dd"T"hh24:mi:ss"Z"'
)
FROM dual;
Is there a way to convert my date and time field into this timestamp format? The time on the date field is incorrect and needs to be replaced by the time field.
You can TRUNCate your date back to midnight and then use NUMTODSINTERVAL to add hours and minutes to it to get the correct time component:
Oracle Setup:
CREATE TABLE your_table ( your_date_column, your_time_column ) AS
SELECT DATE '2019-04-17', '1810' FROM DUAL
Query:
SELECT TO_CHAR(
TRUNC( your_date_column )
+ NUMTODSINTERVAL( SUBSTR( your_time_column, 1, 2 ), 'HOUR' )
+ NUMTODSINTERVAL( SUBSTR( your_time_column, 3, 2 ), 'MINUTE' ),
'YYYY-MM-DD"T"HH24:MI:SS"Z"'
) AS combined_date_time
FROM your_table
Output:
| COMBINED_DATE_TIME |
| :------------------- |
| 2019-04-17T18:10:00Z |
db<>fiddle here
If you want the value as a TIMESTAMP WITH TIME ZONE then:
SELECT CAST(
TRUNC( your_date_column )
+ NUMTODSINTERVAL( SUBSTR( your_time_column, 1, 2 ), 'HOUR' )
+ NUMTODSINTERVAL( SUBSTR( your_time_column, 3, 2 ), 'MINUTE' )
AS TIMESTAMP
) AT TIME ZONE 'UTC' AS combined_date_time
FROM your_table
Just do a bit of string concatenation
to_char( your_date, 'yyyy-mm-dd' ) ||
'T' ||
substr( your_time, 1, 2 ) ||
':' ||
substr( your_time, 3, 2 ) ||
':00Z'
assuming that your_time is always 4 characters long (i.e. 2 AM is represented as the string '0200' rather than '200'). This also assumes that the seconds will always be '00'.
You can achieve this by converting your_number into minutes and add it to your date, then cast it to timestamp as following:
SELECT CAST(
your_date +
(FLOOR(YOUR_TIME/100)*60 + MOD(YOUR_TIME,100)) / 1440
AS TIMESTAMP
) AT TIME ZONE 'UTC' AS YOUR_TIME_STAMP
FROM your_table;
Cheers!!
There is a column 'DateTime' that displays the Date and Time in 'YYYY/MM/DD HH24:MI:SS' format.
I need to display only the rows that has time in a particular range(2AM - 6AM) regardless of the particular date.
I have a code that only displays the time range(2AM - 6AM) for the particular day. I need to display the rows that falls in the past 7 days with the time range 2AM-6AM.
SELECT *
FROM <table_name >
WHERE DateTime BETWEEN TO_DATE (
TO_CHAR (TRUNC (SYSDATE), 'DD-MM-YYYY')
|| ' '
|| '02:00:00',
'DD-MM-YYYY HH24:MI:SS')
AND TO_DATE (
TO_CHAR (TRUNC (SYSDATE), 'DD-MM-YYYY')
|| ' '
|| '06:00:00',
'DD-MM-YYYY HH24:MI:SS')
ORDER BY 1 DESC
SELECT *
FROM your_table
WHERE TO_CHAR( DateTime, 'HH24MMSS' ) BETWEEN '020000' AND '060000'
AND DateTime >= TRUNC( SYSDATE ) - INTERVAL '7' DAY
AND DateTime < TRUNC( SYSDATE ) + INTERVAL '1' DAY
or
SELECT *
FROM your_table
WHERE DateTime BETWEEN TRUNC( DateTime ) + INTERVAL '2' HOUR
AND TRUNC( DateTime ) + INTERVAL '6' HOUR
AND DateTime >= TRUNC( SYSDATE ) - INTERVAL '7' DAY
AND DateTime < TRUNC( SYSDATE ) + INTERVAL '1' DAY
You need two conditions for that: one for the hour being between 02 and 05, and one for the day being in the last seven days:
SELECT *
FROM <table_name>
WHERE to_char(DateTime, 'HH') BETWEEN '02' and '05' AND
DateTime BETWEEN SYSDATE - 7 AND SYSDATE
ORDER BY 1 DESC
In this case, I think the easiest way is string comparison:
WHERE SUBSTR(DateTime, 12, 2) BETWEEN '02' and '05'
Note: Between is inclusive so this should get everything up to 05:59:59.
SELECT *
FROM <table_name >
WHERE DateTime BETWEEN TO_DATE (
TO_CHAR (TRUNC (SYSDATE)-7, 'DD-MM-YYYY')
|| ' '
|| '02:00:00',
'DD-MM-YYYY HH24:MI:SS')
AND TO_DATE (
TO_CHAR (TRUNC (SYSDATE), 'DD-MM-YYYY')
|| ' '
|| '06:00:00',
'DD-MM-YYYY HH24:MI:SS')
select * from table_name where to_char(datetime,'HH24')
between '2' and '6' and to_char(datetime,'dd-mon-yy')
between to_char(sysdate,'dd-mon-yy') and to_char(sysdate-7,'dd-mon-yy');