I am trying to replace DB2 with Oracle DB.
In DB2, there is a WEEK function, which returns the number of weeks of the year.
For example:
SELECT week('2021-01-04') FROM sysibm.sysdummy1
Then I get the return of 2 (as DB2 WEEK function regards Sunday as the first day of the week.)
However, in Oracle, if I make a similar query, then I get a different value.
SELECT to_char(to_date('2021-01-04', 'YYYY-MM-DD'), 'WW') FROM dual;
I get the value of 1 (not 2 as Oracle regards January 1st as the first day of the week.)
Is there any other turnaround or different function to replace DB2 WEEK function?
Oracle offers two Week of Year Number functions. As you have discovered, to_char(dt, 'WW') gives a number in which the week starts on 1st of January and increments every seven days. There is also to_char(dt, 'IW') giving the ISO week number, which runs Monday to Sunday; in this case the 1st of January is Week 53 and 2021-01-04 is the first day of week 1 of 2021.
Demo on db<>fiddle
There is no function in Oracle which increments the week number on the basis of the day of the week number. You could write your own PL/SQL function to do this.
Incidentally, the first day of the week in Oracle is determined by our NLS parameters. If we run with (say) American settings Sunday is day 1; if we run with British settings then Monday is day 1.
If you want to count the number of weeks where the first day of the week is Sunday then:
SELECT FLOOR(
(DATE '2021-01-04' - NEXT_DAY( TRUNC( DATE '2021-01-04', 'YY' ) - INTERVAL '7' DAY, 'SUNDAY'))
/ 7
) + 1
AS week
FROM your_input;
Which outputs 2.
If you run it with multiple input days:
WITH your_input ( value ) AS (
SELECT DATE '2021-01-01' + LEVEL - 1
FROM DUAL
CONNECT BY LEVEL <= 32
)
SELECT value,
FLOOR(
(value - NEXT_DAY( TRUNC( value, 'YY' ) - INTERVAL '7' DAY, 'SUNDAY'))
/ 7
) + 1
AS week
FROM your_input;
Then you get the output (with the NLS_DATE_FORMAT as YYYY-MM-DD (DY)):
VALUE
WEEK
2021-01-01 (FRI)
1
2021-01-02 (SAT)
1
2021-01-03 (SUN)
2
2021-01-04 (MON)
2
2021-01-05 (TUE)
2
2021-01-06 (WED)
2
2021-01-07 (THU)
2
2021-01-08 (FRI)
2
2021-01-09 (SAT)
2
2021-01-10 (SUN)
3
2021-01-11 (MON)
3
2021-01-12 (TUE)
3
2021-01-13 (WED)
3
2021-01-14 (THU)
3
2021-01-15 (FRI)
3
2021-01-16 (SAT)
3
2021-01-17 (SUN)
4
2021-01-18 (MON)
4
2021-01-19 (TUE)
4
2021-01-20 (WED)
4
2021-01-21 (THU)
4
2021-01-22 (FRI)
4
2021-01-23 (SAT)
4
2021-01-24 (SUN)
5
2021-01-25 (MON)
5
2021-01-26 (TUE)
5
2021-01-27 (WED)
5
2021-01-28 (THU)
5
2021-01-29 (FRI)
5
2021-01-30 (SAT)
5
2021-01-31 (SUN)
6
2021-02-01 (MON)
6
db<>fiddle here
Related
I've a table with employees and their birth date, in a column in a format string.
I cannot modify the table, so I created a view to get their birth date in a real date format (TO_DATE).
Now, I would like to get the list of the employees having theirs birthday in the last 15 days and the employees who'll have theirs birthday in the next 15 days.
So, just based with the Day and the month.
I successfully get for exemple all employees bornt in April with "Extract", but, I'm sure you've already understand, when I'll run the query the 25 April, I'd like the futures birthday in May.
How could I get that (oracle 12c)
Thank you 🙂
Using the hiredate column in table scott.emp for testing:
select empno, ename, hiredate
from scott.emp
where add_months(trunc(hiredate),
12 * round(months_between(sysdate, hiredate) / 12))
between trunc(sysdate) - 15 and trunc(sysdate) + 15
;
EMPNO ENAME HIREDATE
---------- ---------- ----------
7566 JONES 04/02/1981
7698 BLAKE 05/01/1981
7788 SCOTT 04/19/1987
This will produce the wrong result in the following situation: if someone's birthday is Feb. 28 in a non-leap year, their birthday in a leap year (calculated with the ADD_MONTHS function in the query) will be considered to be Feb. 29. So, they will be excluded if running the query on, say, Feb. 13 2024 (even though they should be included), and they will be included if running the query on March 14 (even though they should be excluded). If you can live with this - those people will be recognized in the wrong window, once every four years - then this may be all you need. Otherwise that situation will require further tweaking.
For people born on Feb. 29 (in a leap year, obviously), their birthday in a non-leap-year is considered to be Feb. 28. With this convention, the query will always work correctly for them. Whether this convention is appropriate in your locale, only your business users can tell you. (Local laws and regulations may matter, too - depending on what you are using this for.)
You can use ddd format model:
DDD - Day of year (1-366).
For example:
SQL> with v(dt) as (
2 select date'2020-01-01'+level-1 from dual connect by date'2020-01-01'+level-1<date'2021-01-01'
3 )
4 select *
5 from v
6 where
7 not abs(
8 to_number(to_char(date'&dt','ddd'))
9 -to_number(to_char(dt ,'ddd'))
10 ) between 15 and 350;
Enter value for dt: 2022-01-03
DT
-------------------
2020-01-01 00:00:00
2020-01-02 00:00:00
2020-01-03 00:00:00
2020-01-04 00:00:00
2020-01-05 00:00:00
2020-01-06 00:00:00
2020-01-07 00:00:00
2020-01-08 00:00:00
2020-01-09 00:00:00
2020-01-10 00:00:00
2020-01-11 00:00:00
2020-01-12 00:00:00
2020-01-13 00:00:00
2020-01-14 00:00:00
2020-01-15 00:00:00
2020-01-16 00:00:00
2020-01-17 00:00:00
2020-12-19 00:00:00
2020-12-20 00:00:00
2020-12-21 00:00:00
2020-12-22 00:00:00
2020-12-23 00:00:00
2020-12-24 00:00:00
2020-12-25 00:00:00
2020-12-26 00:00:00
2020-12-27 00:00:00
2020-12-28 00:00:00
2020-12-29 00:00:00
2020-12-30 00:00:00
2020-12-31 00:00:00
30 rows selected.
NB: This example doesn't analyze leap years.
Similar to mathguy's answer, but translating the current date back to the birth year (rather than translating the birth year forwards):
SELECT *
FROM employees
WHERE birth_date BETWEEN ADD_MONTHS(
TRUNC(SYSDATE),
ROUND(MONTHS_BETWEEN(birth_date, SYSDATE)/12)*12
) - INTERVAL '15' DAY
AND ADD_MONTHS(
TRUNC(SYSDATE),
ROUND(MONTHS_BETWEEN(birth_date, SYSDATE)/12)*12
) + INTERVAL '15' DAY;
Then, for the sample data:
CREATE TABLE employees (name, birth_date) AS
SELECT 'Alice', DATE '2020-02-28' FROM DUAL UNION ALL
SELECT 'Betty', DATE '2020-02-29' FROM DUAL UNION ALL
SELECT 'Carol', DATE '2021-02-28' FROM DUAL UNION ALL
SELECT 'Debra', DATE '2022-04-28' FROM DUAL UNION ALL
SELECT 'Emily', DATE '2021-03-30' FROM DUAL UNION ALL
SELECT 'Fiona', DATE '2021-03-31' FROM DUAL;
If today's date is 2022-04-16 then the output is:
NAME
BIRTH_DATE
Debra
28-APR-22
If today's date is 2022-03-15 then the output is:
NAME
BIRTH_DATE
Betty
29-FEB-20
Carol
28-FEB-21
Emily
30-MAR-21
And would get values from 28th February - 30th March in a non-leap-year and from 29th February - 30th March in a leap year.
db<>fiddle here
How can i return data from 2 days ago at 11:00:00 PM to all of yesterday ending at 11:59:59 PM?
I currently have only yesterdays date query:
SELECT *
FROM table
WHERE code = '00'
AND to_char(RQST_TMSTMP, 'yyyy-mm-dd') = to_char(sysdate-1, 'yyyy-mm-dd')
How about
select *
from table
where code = '00'
and rqst_tmstmp >= trunc(sysdate - 2) + 11/24
and rqst_tmstmp <= trunc(sysdate);
Here's what all those TRUNCs represent (so that you could follow what's going on):
SQL> select sysdate, -- today, right now
2 trunc(sysdate) ts, -- today at midnight
3 trunc(sysdate - 2) ts_2, -- 2 days ago at midnight
4 trunc(sysdate - 2) + 11/24 ts_2_11 -- 2 days ago at midnight + 11 hours
5 from dual;
SYSDATE TS TS_2 TS_2_11
---------------- ---------------- ---------------- ----------------
29.11.2018 17:07 29.11.2018 00:00 27.11.2018 00:00 27.11.2018 11:00
SQL>
If the column is capturing hours & minutes then use,
TO_CHAR(RQST_TMSTMP,'DD-MM-YY HH24:MI')
I would like to make 5 minute data of each row into quarter data where the first column shows the text of the first 5 minutes and the second column the sum of the first three 5-minute columns. Like this:
29-8-2018 00:00:00 1
29-8-2018 00:05:00 3
29-8-2018 00:10:00 5
29-8-2018 00:15:00 7
29-8-2018 00:20:00 9
29-8-2018 00:25:00 11
To
29-8-2018 00:00:00 9
29-8-2018 00:15:00 27
Thanks in advance,
Allard
Something like this on SQL Server:
select dateadd(minute, -datepart(minute, tm) % 15)), sum(val)
from T t
group by dateadd(minute, -datepart(minute, tm) % 15));
This does assume there are never any seconds to strip off.
I have two sets of Year, Month and Day values. Eg. 15 years 7 months and 23 Days and 7 years 9 months and 12 days. Can the difference between two such values be found in terms of Years, Month and Days? I have thought of converting the values to days, compute the difference and again convert the result back to Years, Months and Days? For this, I have to assume 30 days/month and 365 days/year. Will this approach be ok?
When you think about it thoroughly, you'll realize that you can't calculate a difference of two "time intervals" when months are in place; simply because a subtraction of months can result in different number of days. You can subtract years, you can subtract weeks, you can subtract days,... you can subtract days-to-seconds, you can subtract years-to-months. However, you can't subtract years-to-days.
Example:
SQL> select timestamp'1915-07-23 00:00:00' - timestamp'1907-09-12 00:00:00' as diff_day_to_second_interval from dual;
DIFF_DAY_TO_SECOND_INTERVAL
---------------------------
+000002871 00:00:00
This is your 15 years, 7 months, 23 days minus 7 years, 9 months, 12 days when based on January 1, 1900. This gave us 2871 days difference.
However, consider the following two examples, simply shifted by 1 and 6 months to past
select timestamp'1915-06-23 00:00:00' - timestamp'1907-08-12 00:00:00' as diff_day_to_second_interval from dual;
DIFF_DAY_TO_SECOND_INTERVAL
---------------------------
+000002872 00:00:00
select timestamp'1915-01-23 00:00:00' - timestamp'1907-03-12 00:00:00' as diff_day_to_second_interval from dual;
DIFF_DAY_TO_SECOND_INTERVAL
---------------------------
+000002874 00:00:00
SQL>
These now gave us 2872 and 2874 days of difference.
Now, speaking of possible subtractions...
(a) subtracting year-to-month intervals
SQL> select interval'1915-07' year(4) to month - interval'1907-09' year(4) to month as diff_year_to_month_interval from dual;
DIFF_YEAR_TO_MONTH_INTERVAL
---------------------------
+000000007-10
select interval'1915-06' year(4) to month - interval'1907-08' year(4) to month as diff_year_to_month_interval from dual;
DIFF_YEAR_TO_MONTH_INTERVAL
---------------------------
+000000007-10
select interval'1915-01' year(4) to month - interval'1907-03' year(4) to month as diff_year_to_month_interval from dual;
DIFF_YEAR_TO_MONTH_INTERVAL
---------------------------
+000000007-10
SQL>
All three correctly produce a difference of 7 years and 10 months.
(b) subtracting day-to-second intervals
SQL> select interval'15 01:02:03' day(2) to second - interval'07 02:03:04' day(2) to second as diff_day_to_second_interval from dual;
DIFF_DAY_TO_SECOND_INTERVAL
---------------------------
+000000007 22:58:59
select interval'14 00:01:02' day(2) to second - interval'06 01:02:03' day(2) to second as diff_day_to_second_interval from dual;
DIFF_DAY_TO_SECOND_INTERVAL
---------------------------
+000000007 22:58:59
select interval'09 11:12:13' day(2) to second - interval'01 12:13:14' day(2) to second as diff_day_to_second_interval from dual;
DIFF_DAY_TO_SECOND_INTERVAL
---------------------------
+000000007 22:58:59
SQL>
All three produce the same results, as all three are subtractions of day-to-second intervals with consistent offsetting of the day/hour/minute/second parts of the interval values.
(c) subtracting year-to-day intervals
As I said: Not possible. There even is no such thing as year-to-day interval in Oracle; makers of the DB server knew why they decided not to add those to the engine.
I want to display result set of Years between From date - To date using oracle SQL on dual table
e.g.
if i pass - From date as 1/1/1900 and To Date as 1/1/2000
then it shoold display
Only Years
1900
1901
1902
-
-
2000
There are two parts to this question. Generating the range of dates is quite simple: just use the trick with CONNECT BY that I demonstrated here.
edit
Generating a list of first of New Year's Days is quite simple:
SQL> select add_months(to_date('01-jan-1900'), (level-1)*12) as year
2 from dual
3 connect by level <= 101
4 /
YEAR
---------
01-JAN-00
01-JAN-01
01-JAN-02
...
01-JAN-98
01-JAN-99
01-JAN-00
101 rows selected.
SQL>
You just want the years? Well either use to_char(... , 'YYYY') on that. Or cut to the chase and just generate a list of numbers from 1900 - 2000.
The trickiest part of your request is getting the number of years. It would be easier to be given a start date and an offset, rather than an end date. Anyway ...
SQL> select to_char(add_months(to_date('&&start_date'), (level-1)*12), 'YYYY') as year
2 from dual
3 connect by level <= ( to_number(to_char(to_date('&&end_date'), 'yyyy'))
4 -to_number(to_char(to_date('&&start_date'), 'yyyy')) ) + 1
5 /
Enter value for start_date: 01-jan-1900
old 1: select add_months(to_date('&&start_date'), (level-1)*12) as year
new 1: select add_months(to_date('01-jan-1900'), (level-1)*12) as year
Enter value for end_date: 01-jan-2000
old 3: connect by level <= ( to_number(to_char(to_date('&&end_date'), 'yyyy'))
new 3: connect by level <= ( to_number(to_char(to_date('01-jan-2000'), 'yyyy'))
old 4: -to_number(to_char(to_date('&&start_date'), 'yyyy')) ) - 1
new 4: -to_number(to_char(to_date('01-jan-1900'), 'yyyy')) ) - 1
YEAR
----
1900
1901
1902
...
1998
1999
2000
101 rows selected.
SQL>