I am looking to filter my reports so they gather all the data to Friday.
I.e. today is the 12th and I would like my report to gather info from my date range 5th to the 11th. Then tomorrow on the 13th still get the data from the 5th to the 11th.
just a weekly report that comes out on a Friday.
I cant use date diff on the server it doesn't let me on this Oracle
I was using this where (date >= trunc(SYSDATE) - 7) but I cant seem to get it to do Friday.
I did a lot of reading and there is a post about levels....... and saw this script. It shows the Friday but I have no idea how to get it to filter on my date column.
WITH dates AS (SELECT TRUNC(SYSDATE, 'mm') + LEVEL -11 dt
FROM dual
CONNECT BY LEVEL <= 22)
SELECT dt,
to_char(dt, 'fmDay') day_of_dt,
TRUNC(dt + 3, 'iw') - 3 prev_fri_incl_today,
TRUNC(dt + 2, 'iw') - 3 prev_fri_not_incl_today
FROM dates;
If you want the values from Saturday of the previous week until Friday of the current week (which, if it is currently Monday-Friday will contain future dates) then:
SELECT *
FROM table_name
WHERE date_column >= TRUNC(SYSDATE, 'iw') - 2
AND date_column < TRUNC(SYSDATE, 'iw') + 5
If you want to include a full historic week (i.e. if it is current Monday-Friday then get last week's range):
SELECT *
FROM table_name
WHERE date_column >= TRUNC(SYSDATE - 5, 'iw') - 2
AND date_column < TRUNC(SYSDATE - 5, 'iw') + 5
fiddle
I have year and week numbers in a SQL table in one column with a format as VERSION_YEAR_WEEKNUMBER
I can use the SUBSTR function to just get the YEAR_WEEKNUMBER, but I then need to convert that to a date and I'm not sure how to convert a weeknumber to a date.
Ideally the date would be a Thursday, but right now I'd just like to convert a weeknumber to any date that's in that week and year combination.
Sample data would just be "VERSION_YEAR_WEEKNUMBER" e.g. VERSION_2020_10. Meaning, 2020, 10th week (ISO) of the year. Desired result should be one date between 02-MAR-20 and 08-MAR-20
You can extract the year and week numbers separately:
substr(your_column, instr(your_column, '_', -1, 2) + 1, 4)
substr(your_column, instr(your_column, '_', -1, 1) + 1)
Then convert the year number to the first day of the ISO year:
trunc(to_date(substr(your_column, instr(your_column, '_', -1, 2) + 1, 4), 'YYYY'), 'IYYY')
and either add the number of weeks - 1 * 7 days to get the Monday of that week, then add 3 for the Thursday; or add the number of weeks * 7 days to get the Monday of the following week and subtract four for the Thursday of the week you want:
trunc(to_date(substr(your_column, instr(your_column, '_', -1, 2) + 1, 4), 'YYYY'), 'IYYY')
+ (to_number(substr(your_column, instr(your_column, '_', -1, 1) + 1)) * 7)
- 4
YOUR_COLUMN RESULT
--------------- ----------
VERSION_2020_10 2020-03-05
1.2.3_2021_01 2021-01-07
1_2_3_2020_11 2020-03-12
2020_12 2020-03-19
ABC_2020_52 2020-12-24
DEF_2020_53 2020-12-31
GHI_2021_01 2021-01-07
JKL_2021_52 2021-12-30
MNO_2021_53 2022-01-06
db<>fiddle showing the intermediate steps so you can see what's happening.
Converting from ISO-Week to year is not trival, because the ISO year may differ from the actual year.
For example 2018-12-31 was Week 1 of 2019 according to ISO-8601.
I ended up with this function:
FUNCTION ISOWeek2Date(YEAR INTEGER, WEEK INTEGER) RETURN DATE DETERMINISTIC IS
res DATE;
BEGIN
IF WEEK > 53 OR WEEK < 1 THEN
RAISE VALUE_ERROR;
END IF;
res := NEXT_DAY(TO_DATE( YEAR || '0104', 'YYYYMMDD' ) - 7, 'MONDAY') + ( WEEK - 1 ) * 7;
IF TO_CHAR(res, 'fmIYYY') = YEAR THEN
RETURN res;
ELSE
RAISE VALUE_ERROR;
END IF;
END ISOWeek2Date;
However NEXT_DAY depends on date language of your session, so the solution by #MT0 would be better. The logic is the same.
Extract year and week values with regular expression as proposed by #Alex Poole
Converting to an ISO week is relatively simple as the 4th of January will always be in the first ISO week of the year. So:
Start with the 4th January of your year;
Truncate it back to Monday to be the start of the ISO week;
Add 3 days and you have Thursday of the first ISO week; and then
Add the number of weeks to take it from the 1st week to the required week.
Like this:
SELECT TRUNC( TO_DATE( SUBSTR( value, 9, 4 )||'01-04', 'YYYY-MM-DD' ), 'IW' )
+ INTERVAL '3' DAY
+ INTERVAL '7' DAY * ( SUBSTR( value, 14 ) - 1 )
AS thursday_of_iso_week
FROM table_name
Which, for the sample data:
CREATE TABLE table_name ( value ) AS
SELECT 'VERSION_2020_10' FROM DUAL UNION ALL
SELECT 'VERSION_2019_1' FROM DUAL
Outputs:
| THURSDAY_OF_ISO_WEEK |
| :------------------- |
| 2020-03-05 00:00:00 |
| 2019-01-03 00:00:00 |
If the values do not have fixed character positions then use INSTR to find the underscores:
SELECT TRUNC( TO_DATE( SUBSTR( value, INSTR( value, '_' ) + 1, 4 )||'01-04', 'YYYY-MM-DD' ), 'IW' )
+ INTERVAL '3' DAY
+ INTERVAL '7' DAY * ( SUBSTR( value, INSTR( value, '_', -1 ) + 1 ) - 1 )
AS thursday_of_iso_week
FROM table_name
Which outputs the same as above.
db<>fiddle here
You could join the following CTE to your query
select weekdate, to_char(weekdate, 'IW-yyyy') week_year from (
SELECT LEVEL weeknr, to_date('2020-01-02', 'yyyy-MM-dd') + numtodsinterval((LEVEL-1)*7, 'day') weekdate
FROM dual
CONNECT BY LEVEL <= 53
);
For the join, use the result of your SUBSTR and WEEK_YEAR of the CTE. WEEKDATE would be the date. This CTE generates all the week numbers for all Thursdays of 2020 (January 2nd was the first Thursday in 2020). If you need more dates, just use another limit for "LEVEL". If you have huge amounts of source data or query the data very frequently, I would recommend to put this in a table (to be done one time) by appending "create table ... as" at the beginning and create an index on WEEK_YEAR (and generating enough data so your solution works for a long time).
My query groups data from Monday to Sunday. I would like to be able to group data from Thursday to Wednesday. How can I alter the two lines starting with TO_CHAR to do it?
SELECT SOMESTUFF
,WEEKNUM
,WEEKENDING
FROM (
SELECT SOMEDETAILS
,TRXDATE
,TO_CHAR(TRXDATE, 'IW') AS WEEKNUM
,TO_CHAR(TRUNC(TRXDATE, 'IW') + 7 - 1 / 86400, 'MM/DD/YYYY') AS WEEKENDING
FROM SOMETABLE
) DETAILS
INNER JOIN SOMEOTHERTABLES
GROUP BY SOMEFIELDS
,WEEKNUM
,WEEKENDING
You can use NEXT_DAY(TRXDATE - 1, 'WED') to find the end of your week:
TO_CHAR(NEXT_DAY(TRXDATE - 1, 'WED'), 'MM/DD/YYYY')
TO_CHAR(NEXT_DAY(TRXDATE - 1, 'WED'), 'IW')
What week number do you expect?
Easiest solution is to simply add one to the weeknum when the date is Wed-Sunday (or subtract if it's Monday or Tuesday). Something like this would work
TO_CHAR(TRXDATE, 'IW') + CASE
WHEN TO_CHAR(TRXDATE, 'D') > 2 THEN 1
ELSE 0
END AS WEEKNUM
I have a table with date field and there will be multiple entries for each day. I need to get the count of weekdays having at least one entry in that weekday.
If the date range is 27-Feb-12 till 2-Apr-12 then there are 26 week days and it needs to return 26 if we have entries in all weekdays and a lesser count if there is no entry on a particular day.
Say a table Transaction with tid, type, createddate.
Can someone please suggest me a good SQL approach ? I am running on a Oracle DB
Try breaking down the problem into multiple smaller problems. Here's how..(I am using march-10 instead of april 2nd in your example, to keep the data set smaller).
Get all the dates between the two dates.
select to_date('27-feb-2012','dd-mon-yyyy') + level -1
from dual
connect by level < ( to_date('10-mar-12','dd-mon-yy') -
to_date('27-feb-2012','dd-mon-yyyy') +2
)
TO_DATE('
---------
27-FEB-12
28-FEB-12
29-FEB-12
01-MAR-12
02-MAR-12
03-MAR-12
04-MAR-12
05-MAR-12
06-MAR-12
07-MAR-12
08-MAR-12
next, get the weekdays out of this result set..
with all_days as (
select to_date('27-feb-2012','dd-mon-yyyy') + level -1 date1
from dual
connect by level < ( to_date('10-mar-12','dd-mon-yy') -
to_date('27-feb-2012','dd-mon-yyyy') +2
)
)
select date1,
to_char(date1,'D'),
to_char(date1,'Day') Day
from all_days
where to_number(to_char(date1,'D')) between 2 and 6 -- monday through friday
/
DATE1 T DAY
--------- - ---------
27-FEB-12 2 Monday
28-FEB-12 3 Tuesday
29-FEB-12 4 Wednesday
01-MAR-12 5 Thursday
02-MAR-12 6 Friday
05-MAR-12 2 Monday
06-MAR-12 3 Tuesday
07-MAR-12 4 Wednesday
08-MAR-12 5 Thursday
09-MAR-12 6 Friday
--and finally, your exists logic to check if there is any data for this date in your table.
with all_days as (
select to_date('27-feb-2012','dd-mon-yyyy') + level -1 date1
from dual
connect by level < ( to_date('10-mar-12','dd-mon-yy') -
to_date('27-feb-2012','dd-mon-yyyy') +2
)
)
select date1,
to_char(date1,'D'),
to_char(date1,'Day') Day
from all_days
where to_number(to_char(date1,'D')) between 2 and 6 -- monday through friday
and exists (
select 1
from your_table yt
where yt.date_column = all_days.date_column
)
There are 25 total business days between your dates in 2013 - I used current year. With my hypothetical data you will get 17 total working days with entries. To see all days comment SUM() part and uncomment Start_Date... and other columns. Then you can count visually to check results:
SELECT SUM(CASE WHEN Entry_Val > 0 THEN 1 ELSE 0 END) entry_days --, Start_Date, Wk_Day, Day#, entry_val, lvl
FROM
(
SELECT TO_CHAR(TO_DATE('27-FEB-12') + LEVEL-1, 'DD-MON-YYYY') Start_Date
, TO_CHAR(TO_DATE('27-FEB-12') + LEVEL-1, 'DY') Wk_Day
, TO_CHAR(TO_DATE('27-FEB-12') + LEVEL-1, 'D' ) Day#
, MOD(LEVEL, 3) Entry_Val -- Hypot. entry day - count only days with entry_val > 0 --
, LEVEL lvl -- added for clarity
FROM dual CONNECT BY LEVEL <= (SELECT to_date('02-APR-12') - to_date('27-FEB-12') days_btwn FROM dual)
)
WHERE Wk_Day NOT IN ('SAT', 'SUN') -- OR -- Day# NOT IN (1, 7)
/
ENTRY_DAYS
--------
17
I'm interpreting this question a little differently. #Pradeep, is this what you're after?
SELECT COUNT(DISTINCT TRUNC(CreatedDate))
FROM Transaction
WHERE TRUNC(CreatedDate) BETWEEN DATE '2012-02-27' AND DATE '2012-04-02'
AND TO_CHAR(CreatedDate, 'DY') NOT IN ('SAT', 'SUN')
If you have entries for all Monday-Friday days between 2/27/2012 and 4/2/2012, the result of this query will be 26. It won't matter how many entries there are for any given day, as long as each day has at least one.
If you don't have any records for two of the days (say 2/28 and 3/22) the result will be 24.
Note that I used TRUNC in the first and third lines above in case your CreatedDate column includes a time component. If it doesn't you can do without the TRUNC.
select to_date('02-04-2012','DD-MM-YYYY')-
to_date('27-02-2012','DD-MM-YYYY')-
2*(to_char(to_date('02-04-2012','DD-MM-YYYY'),'WW')-
to_char(to_date('27-02-2012','DD-MM-YYYY'),'WW'))+1 from dual;
If I write
select ename, to_char(hiredate,'fmDay') as "Day" order by "Day";
Then it sorts the result based on Day like; from Friday, then Monday and last Wednesday,
like sorting by characters.
But I want to sort it by day of the week; from Monday to Sunday.
You're getting it in the order you are because you're ordering by a string (and this wouldn't work because you're not selecting from anything).
You could order by the format model used to create the day of the week in numeric form, D, but as Sunday is 1 in this I would recommend using mod() to make this work.
i.e. assuming the table
create table a ( b date );
insert into a
select sysdate - level
from dual
connect by level <= 7;
This would work:
select mod(to_char(b, 'D') + 5, 7) as dd, to_char(b, 'DAY')
from a
order by mod(to_char(b, 'D') + 5, 7)
Here's a SQL Fiddle to demonstrate.
In your case your query would become:
select ename, to_char(hiredate,'fmDay') as "Day"
from my_table
order by mod(to_char(hiredate, 'D') + 5, 7)
SELECT
*
FROM
classes
ORDER BY
CASE
WHEN Day = 'Sunday' THEN 1
WHEN Day = 'Monday' THEN 2
WHEN Day = 'Tuesday' THEN 3
WHEN Day = 'Wednesday' THEN 4
WHEN Day = 'Thursday' THEN 5
WHEN Day = 'Friday' THEN 6
WHEN Day = 'Saturday' THEN 7
END ASC
Assuming that user has a table called classes in that table user has class_id (primary key), class name, Day.
Take a look at other formats for TO_CHAR. Instead of 'fmDay' use 'D' and it will give you the day of the week from 1 to 7. Then you can easily sort on it.
Here's a list of date formats: http://docs.oracle.com/cd/B19306_01/server.102/b14200/sql_elements004.htm
Why to complicate when you can add another column with numbers 1-7 corresponding to days and then sort by this column...
I just encountered the same requirement -- to order a query result by day of the week, but not starting with Sunday. I used the following query in Oracle to start w/ Monday. (Modify it to start the ordering w/ any day of the week, e.g., change 'MONDAY' to 'TUESDAY'.)
SELECT ename, to_char(hiredate, 'fmDAY') AS "Day"
FROM emp
ORDER BY (next_day(hiredate, 'MONDAY') - hiredate) DESC
Or:
SELECT ename, to_char(hiredate, 'fmDAY') AS "Day"
FROM emp
ORDER BY (hiredate - next_day(hiredate, 'MONDAY'))
The D format mask of to_char maps days of the week to the values 1-7.
But!
The output of this depends on the client's setting for NLS_TERRITORY. The US considers Sunday to be day 1. Whereas most of the rest of the world consider Monday to be the start:
alter session set nls_territory = AMERICA;
with dts as (
select date'2018-01-01' + level - 1 dt
from dual
connect by level <= 7
)
select to_char ( dt, 'Day' ) day_name,
to_char ( dt, 'd' ) day_number
from dts
order by day_number;
DAY_NAME DAY_NUMBER
Sunday 1
Monday 2
Tuesday 3
Wednesday 4
Thursday 5
Friday 6
Saturday 7
alter session set nls_territory = "UNITED KINGDOM";
with dts as (
select date'2018-01-01' + level - 1 dt
from dual
connect by level <= 7
)
select to_char ( dt, 'Day' ) day_name,
to_char ( dt, 'd' ) day_number
from dts
order by day_number;
DAY_NAME DAY_NUMBER
Monday 1
Tuesday 2
Wednesday 3
Thursday 4
Friday 5
Saturday 6
Sunday 7
Sadly, unlike many other NLS parameters, you can't pass NLS_TERRITORY as the third parameter of to_char:
with dts as (
select date'2018-01-01' dt
from dual
)
select to_char ( dt, 'Day', 'NLS_DATE_LANGUAGE = SPANISH' ) day_name
from dts;
DAY_NAME
Lunes
with dts as (
select date'2018-01-01' dt
from dual
)
select to_char ( dt, 'Day', 'NLS_TERRITORY = AMERICA' ) day_name
from dts;
ORA-12702: invalid NLS parameter string used in SQL function
So any solution relying on D for sorting is a bug!
To avoid this, subtract the most recent Monday from the date (if today is Monday, the most recent Monday = today). You can do this with the IW format mask. Which returns the start of the ISO week. Which is always a Monday:
with dts as (
select date'2018-01-01' + level - 1 dt
from dual
connect by level <= 7
)
select to_char ( dt, 'Day' ) day_name,
( dt - trunc ( dt, 'iw' ) ) day_number
from dts
order by day_number;
DAY_NAME DAY_NUMBER
Monday 0
Tuesday 1
Wednesday 2
Thursday 3
Friday 4
Saturday 5
Sunday 6
For Sunday-Saturday sorting, add one to the date before finding the start of the ISO week:
with dts as (
select date'2018-01-01' + level - 1 dt
from dual
connect by level <= 7
)
select to_char ( dt, 'Day' ) day_name,
( dt - trunc ( dt + 1, 'iw' ) ) day_number
from dts
order by day_number;
DAY_NAME DAY_NUMBER
Sunday -1
Monday 0
Tuesday 1
Wednesday 2
Thursday 3
Friday 4
Saturday 5
If you want Monday to be always treated as first day of week you could use:
-- Not affected by NLS_TERRITORY
-- ALTER SESSION SET NLS_TERRITORY="AMERICA"; -- Sunday is first day of week
-- ALTER SESSION SET NLS_TERRITORY="GERMANY"; -- Monday is first day of week
SELECT *
FROM tab
ORDER BY 1+TRUNC(dt)-TRUNC(dt,'IW');
db<>fiddle demo
It's simple.
SELECT last_name, hire_date,TO_CHAR(hire_date, 'DAY') DAY
FROM employees
ORDER BY TO_CHAR(hire_date - 1, 'd');
TO_CHAR(hire_date - 1, 'd') puts a 'Monday' into a box named 'Sunday'.
As it's said, there's a function for it:
SELECT *
FROM table
ORDER BY WEEKDAY(table.date);
Might be late to post the answer, But seems simple solution to sort by day of week starting from Monday.
select hiredate,dayname(hiredate) from emp order by (dayofweek(hiredate)+5)%7;
Answer for why added 5 ?
Because , In numbers Sunday has value 1, Monday has 2 ... and Saturday has 7
So, 2 + 5 = 7 and mod(%) with 7 will give us new values start from Monday.
You can change this number to start with any other day of week.
2nd option
simply use WEEKDAY() function return weekday number for a given date.
**Note:**
0 = monday
1 = tuesday
2 = wednesday
3 = thursday
4 = friday
5 = saturday
6 = sunday
select hiredate,dayname(hiredate) from emp order by WEEKDAY(hiredate);
I improved on Ben's answer by giving you a result which starts from 1 rather than 0. The query would be like this:
select
mod(to_char(b, 'D')+ 5, 7) +1 as dd,
to_char(b, 'DAY')
from a
order by mod(to_char(b, 'D')+ 5, 7);
On the other hand, if you want your week to start from Sunday, then you should use this query:
select
mod(to_char(b, 'D')+ 6, 7) +1 as dd,
to_char(b, 'DAY')
from a
order by mod(to_char(b, 'D')+ 6, 7)
Hope this helps :)
with s as (select trunc(sysdate) + level dt from dual connect by level <= 7)
select to_char(dt, 'fmDay', 'nls_date_language=English') d
from s
order by dt - trunc(dt, 'iw');
D
------------------------------------
Monday
Tuesday
Wednesday
Thursday
Friday
Saturday
Sunday
7 rows selected.
I have some simple idea hope you like it. i don't which sql you are using so please correct syntax error.
select ename, to_char(hiredate,'fmDay') as "Day" from ABC_TABLE
JOIN (VALUES (1,'Monday'),(2,'Tuesday'),(3,'Wednesday'),(4,'Thursday'),(5,'Friday'),(6,'Saturday'),(7,'Sunday')) weekdays(seq,[Days]) on
ABC_TABLE.to_char(hiredate,'fmDay') = weekdays.[Days]
order by weekdays.seq;
I you want to start next week after end a week then just find quarter of month and add in order by clouse.
just for in of find Quarter in(MSSQL): select DatePart(QUARTER, cast(cast(mydate as char(8)) as date))
This should do the trick in sql:
ORDER BY
CASE DATENAME(dw,<<enter your date variable here>>)
WHEN 'Monday' THEN 1
WHEN 'Tuesday' THEN 2
WHEN 'Wednesday' THEN 3
WHEN 'Thursday' THEN 4
WHEN 'Friday' THEN 5
WHEN 'Saturday' THEN 6
WHEN 'Sunday' Then 7
END ASC;
Of course if you want different order you just switch the values after then.
For Postgres at least, to have Monday always be day 1, use TO_CHAR(b, 'ID') to return the ISO 8601 day of the week - Monday (1) to Sunday (7).
This is the International Standard's Organization (ISO) Day of the Week hence the I in front of the D in 'ID'.
https://www.postgresqltutorial.com/postgresql-to_char/
I think this is too late, but I will leave this here if anyone else needs it.
You can just order by day of the week, but it depends on the territory of your session.
In order to show the territory, you can simply execute this query:
SELECT * FROM V$NLS_PARAMETERS WHERE parameter = 'NLS_TERRITORY';
NLS_TERRITORY specifies the name of the territory whose conventions are to be followed for day and week numbering.
Back to the previous query, if you have AMERICA as the value, then Monday has the value 2.
To order your results from Monday to Sunday, you have at least 2 roads:
1- Decrease one day from hire_date:
SELECT * FROM employees ORDER BY TO_CHAR(hire_date - 1, 'd');
2- Alter the territory to a value that has 1 as the value of Monday, for example, FRANCE:
ALTER SESSION SET NLS_TERRITORY = 'FRANCE';
SELECT * FROM employees ORDER BY TO_CHAR(hire_date, 'd');
This is my solution.
select ename, to_char(hiredate,'fmDay') "Day"
from employees
order by DECODE(to_number(to_char(hiredate ,'D'))-1,
0, 7,
to_number(to_char(hiredate ,'D'))-1);
Basically converts day to a number by using 'D' format from to_char function.
to_number(to_char(hire_date ,'D')
But that causes Sunday to be 1 and Monday to be 2 and so on so I subtract 1 to make Monday 1.
(to_number(to_char(hire_date ,'D') - 1
Then I use DECODE function to basically say if 0, then 7, else original number. That just makes Sunday be 7 instead of 0 and everything else is in its proper place(Monday is 1, Tuesday is 2...so on).
DECODE(to_number(to_char(hire_date ,'D'))-1,
0, 7,
to_number(to_char(hire_date ,'D'))-1);