I am trying to show week numbers on a query. I have the following sql:
SELECT DISTINCT TO_CHAR(TRUNC((sysdate + ROWNUM), 'IW'), 'IW' ) as dt
FROM DUAL
CONNECT BY ROWNUM <= (2-1)*7
when I try to execute it it gives me the following error: input value not long enough for date format I know this is a common error but I can't find an solution to my answer.
It returns the following when I do it witouth to_char: 20/02/2017 00:00:00
Apparently this works:
SELECT DISTINCT TO_CHAR(TRUNC((SYSDATE) + (ROWNUM), 'IW'), 'IW') as dt
FROM DUAL
CONNECT BY ROWNUM <= (3-1)*7
don't really know what changed but it works now.
Related
I have two parameters MONTH and YEAR, how get all date?
Eg. YEAR = 2021, MONTH = 8
Date
------
01.08.2021
02.08.2021
.....
31.08.2021
I'm a fan of recursive CTEs, because they are standard SQL. In Oracle, you can use one like this:
with cte(dte) as (
select to_date('2020' || '8', 'YYYYMM') -- the two parameters are '2020' and '8'
from dual
union all
select dte + interval '1' day
from cte
where dte < last_day(dte)
)
select *
from cte;
Here is a db<>fiddle.
select dt + level - 1 as date_
from (select to_date(to_char(:year , 'fm0000') ||
to_char(:month, 'fm00'), 'yyyymm') as dt from dual)
connect by level <= add_months(dt, 1) - dt
;
This is almost the same as MT0's answer, with a few minor differences and one that is not entirely minor.
The to_date function assumes a default of first day of the month, so it is not necessary to explicitly concatenate '01' to the year and month (although perhaps doing so makes the code easier to read for beginner programmers). In my opinion, that's just a matter of taste.
I separated the computation of the first day of the month into a subquery. No worries, the optimizer will merge it into the outer query, so there is no efficiency cost - but the code will be easier to maintain.
The non-trivial difference is in the connect by clause. Even though mathematically the formula is equivalent to
dt + level - 1 < add_months(dt, 1)
or, better (still equivalent!)
dt + level <= add_months(dt, 1)
in terms of processing they are not equivalent. If written in the form above (previous line of code), for each value of level, the runtime will perform a date arithmetic calculation followed by a date comparison.
On the other hand, by solving the inequality for level (as I did in my query), the date calculation is performed just once (rather than once for every row), and the comparison is simply level <= some calculated number.
Perhaps in this problem "efficiency" plays no role, but as a matter of good coding, we should "solve for level" whenever possible, for the reason I just gave.
Assuming you pass in the bind variables :year and :month, then you can use a hierarchical query:
SELECT TO_DATE(
TO_CHAR(:year, 'FM0000') || TO_CHAR(:month, 'FM00') || '01',
'YYYYMMDD'
) + LEVEL - 1 AS "Date"
FROM DUAL
CONNECT BY
TO_DATE(
TO_CHAR(:year, 'FM0000') || TO_CHAR(:month, 'FM00') || '01',
'YYYYMMDD'
) + LEVEL - 1
<
ADD_MONTHS(
TO_DATE(
TO_CHAR(:year, 'FM0000') || TO_CHAR(:month, 'FM00') || '01',
'YYYYMMDD'
),
1
)
sqlfiddle here
I want to get all dates of a month based on day name like if I want to get all FRIDAY dates from current date to a specific end date. I got a solution in SQL but can't find any solution in ORACLE.
You can use hierarchy query with next_day function as follows:
-- input variable
with dataa (start_date, end_date) as
(select date '2020-11-01', date '2020-12-15' from dual)
-- your query starts from here
select next_day((level-1)*7 + (start_date - 1), 'FRIDAY')
FROM DATAA
connect by
next_day((level-1)*7 + (start_date - 1),'FRIDAY') <= end_date
Db<>fiddle here
You can use hierarchical query as
WITH t AS
(
SELECT TRUNC(sysdate,'MM')+level-1 AS Fridays
FROM dual
WHERE TO_CHAR(TRUNC(sysdate,'MM')+level-1,'Dy','NLS_DATE_LANGUAGE=American')='Fri'
CONNECT BY level <= LAST_DAY(TRUNC(sysdate)) - TRUNC(sysdate,'MM') + 1
)
SELECT *
FROM t
WHERE Fridays BETWEEN :data1 AND :date2
Demo
Using connect by then filtering by Friday is a simple solution. In the example below I am using July 1st as the start date and December 1st as the end date.
SELECT DATE '2020-7-1' + LEVEL AS friday_dates
FROM DUAL
WHERE TO_CHAR (DATE '2020-7-1' + LEVEL, 'DY') = 'FRI'
CONNECT BY LEVEL <= DATE '2020-12-1' - DATE '2020-7-1';
I want No. of days between these 2 dates using Oracle SQL
Dates:
BETWEEN "1/1/2018" AND "6/11/2018"
How to write SQL Query?
between date '2018-01-01' and date '2018-11-06'
where DATE literal looks exactly like that: DATE 'YYYY-MM-DD'
In your example:
double quote's can't be used
even if you used single quotes, that would be a string, not DATE so you'd depend on whether Oracle is capable of converting it (implicitly) to date or not
therefore, always use dates, not strings
[EDIT]
This is how you select the whole calendar between those two dates:
select date '2018-01-01' + level - 1
from dual
connect by level <= date '2018-11-06' - date '2018-01-01' + 1;
As other answers have pointed out you can simply divide two dates, but there is also no need for any additional arithmetic.
The code:
select to_date('6/11/2018', 'DD/MM/YYYY') - to_date('1/1/2018', 'DD/MM/YYYY')
from dual;
The result: 309
you can simple do:
select date1-date2 form dual;
or
select (sysdate-to_date('01-jan-2018'))-(sysdate-to_date('10-jan-2018'))from dual;
Just use
select date'2018-11-06' - date'2018-01-01' + 1 as days_difference
from dual;
DAYS_DIFFERENCE
---------------
310
or
with t( myDate ) as
(
select date'2018-11-06' from dual union all
select date'2018-01-01' from dual
)
select max(myDate) - min(myDate) + 1 as days_difference
from t;
DAYS_DIFFERENCE
---------------
310
I have dates stored like String in database.
The format is 'yyyy-ww' (example: '2015-43').
I need to get the first day of the week.
I tried to convert this string into date but there is no 'ww' option for the function "to_date".
Do you have an idea to perform this convertion?
EDIT
Test results based on the answers -
Thanks for your anwsers, but I have many problems to apply your solutions to my context:
select
TRUNC ( 2015 + ((43 - 1) * 7), 'IW' )
from dual
==> Error : ORA-01722: invalid number
select
TRUNC(to_date('2015','YYYY')+ to_number('01') *7, 'IW')
from dual
==> 2015-02-02 00:00:00
I waited for a date in january
select
trunc(to_date(regexp_substr('2015-01', '\d+',1,2), 'YYYY') + regexp_substr('2015-01', '\d+') * 7, 'IW') dt2
from dual
==> 0039-09-14 00:00:00
select
regexp_substr('2015-01', '\d+',1,2) as res1,
regexp_substr('2015-01', '\d+') * 7 as res2
from dual
==> res1 = 01
==> res2 = 14105
try to use by truncate
with t as (
select '16-2010' dt from dual
)
--
--
select dt,
trunc(to_date(regexp_substr(dt, '\d+',1,2), 'YYYY') + regexp_substr(dt, '\d+') * 7, 'IW') dt2
from t
I have dates stored like String in database.
You should never do that. It is a bad design. you should store date as DATE and not as a string. For all kinds of requirements for date manipulations Oracle provides the required DATE functions and format models. As and when needed, you could extract/display the way you want.
I need to get the first day of the week.
TRUNC (dt, 'IW') returns the Monday on or before the given date.
Anyway, in your case, you have the literal as YYYY-WW format. You could first extract the year and week number and combine them together to get the date using TRUNC.
TRUNC ( year + ((week_number - 1) * 7)
, 'IW
)
So, the above should give you the Monday of the week number passed for that year.
SQL> WITH DATA AS
2 ( SELECT '2015-43' str FROM dual
3 )
4 SELECT TRUNC(to_date(SUBSTR(str, 1, 4),'YYYY')+ to_number(SUBSTR(str, instr(str, '-',1)+1))*7, 'IW')
5 FROM DATA
6 /
TRUNC(TO_
---------
23-NOV-15
SQL>
Similar to Lalit's, however, I think I've corrected the math (his seemed to be off a bit when I tested .. )
with w_data as (
select sysdate + level +200 d from dual connect by level <= 10
),
w_weeks as (
select d, to_char(d,'yyyy-iw') c
from w_data
)
SELECT d, c, trunc(d,'iw'),
TRUNC(
to_date(SUBSTR(c, 1, 4)||'0101','yyyymmdd')-8+to_char(to_date(SUBSTR(c, 1, 4)||'0101','yyyymmdd'),'d')
+to_number(SUBSTR(c, instr(c, '-',1)+1)-1)*7 ,'IW')
FROM w_weeks;
The extra columns help show the dates before, and after.
I would do the following:
WITH d1 AS (
SELECT '2015-43' AS mydate FROM dual
)
SELECT TRUNC(TRUNC(TO_DATE(REGEXP_SUBSTR(mydate, '^\d{4}'), 'YYYY'), 'YEAR') + (COALESCE(TO_NUMBER(REGEXP_SUBSTR(mydate, '\d+$')), 0)-1) * 7, 'IW')
FROM d1
The first thing the above query does is get the first four digits of the string 2015-43 and truncates that to the closest year (if you convert convert 2015 using TO_DATE() it returns a date within the current month; that is SELECT TO_DATE('2015', 'YYYY') FROM dual returns 01-FEB-2015; we need to truncate this value to the YEAR in order to get 01-JAN-2015). I then add the number of weeks minus one times seven and truncate the whole thing by IW. This returns a date of 01-OCT-2015 (see SQL Fiddle here).
According ISO the 4th of January is always in week 1, so your query should look like
Select
TRUNC(TO_DATE(REGEXP_SUBSTR(your_column, '^\d{4}')||'-01-04', 'YYYY-MM-DD')
+ 7*(REGEXP_SUBSTR(your_column, '\d$')-1), 'IW')
from your_table;
However, there is a problem. ISO year used for Week number can be different than actual year. For example, 1st Jan 2008 was in ISO week number 53 of 2007.
I think a proper working solution you get only when you generate ISO weeks from date value.
WITH w AS
(SELECT TO_CHAR(DATE '2010-01-04' + LEVEL * INTERVAL '7' DAY, 'IYYY-IW') AS week_number,
TRUNC(DATE '2010-01-04' + LEVEL * INTERVAL '7' DAY, 'IW') AS first_day
FROM dual
CONNECT BY DATE '2010-01-04' + LEVEL * INTERVAL '7' DAY < SYSDATE)
SELECT your_Column, first_day
FROM w your_table
JOIN w ON week_number = your_Column;
Your date range must bigger than 2010-01-04 and not bigger than current day.
This is what I used:
select
to_date(substr('2017/01',1,4)||'/'||to_char(to_number(substr('2017/01',6,2)*7)-5),'yyyy/ddd') from dual;
Does anyone know how can I calculate the number of weekdays between two date fields? I'm using oracle sql developer. I need to find the average of weekdays between multiple start and end dates. So I need to get the count of days for each record so I can average them out. Is this something that can be done as one line in the SELECT part of my query?
This answer is similar to Nicholas's, which isn't a surprise because you need a subquery with a CONNECT BY to spin out a list of dates. The dates can then be counted while checking for the day of the week. The difference here is that it shows how to get the weekday count value on each line of the results:
SELECT
FromDate,
ThruDate,
(SELECT COUNT(*)
FROM DUAL
WHERE TO_CHAR(FromDate + LEVEL - 1, 'DY') NOT IN ('SAT', 'SUN')
CONNECT BY LEVEL <= ThruDate - FromDate + 1
) AS Weekday_Count
FROM myTable
The count is inclusive, meaning it includes FromDate and ThruDate. This query assumes that your dates don't have a time component; if they do you'll need to TRUNC the date columns in the subquery.
You could do it the following way :
Lets say we want to know how many weekdays between start_date='01.08.2013' and end_date='04.08.2013' In this example start_date and end_date are string literals. If your start_date and end_date are of date datatype, the TO_DATE() function won't be needed:
select count(*) as num_of_weekdays
from ( select level as dnum
from dual
connect by (to_date(end_date, 'dd.mm.yyyy') -
to_date(start_date, 'dd.mm.yyyy') + 1) - level >= 0) s
where to_char(sysdate + dnum, 'DY',
'NLS_DATE_LANGUAGE=AMERICAN') not in ('SUN', 'SAT')
Result:
num_of_weekdays
--------------
2
Checkout my complete working function code and explanation at
https://sqljana.wordpress.com/2017/03/16/oracle-calculating-business-days-between-two-dates-in-oracle/
Once you have created the function just use the function as part of the SELECT statement and pass in the two date columns for Start and End dates like this:
SELECT Begin_Date, End_Date, fn_GetBusinessDaysInterval(Begin_Date, End_Date) AS BusinessDays FROM YOURTABLE;