I have a table with 3 columns: user, value, and date. The main query returns the values for a specific user based on a date range:
SELECT date, value FROM values
WHERE user = '$user' AND
date BETWEEN $start AND $end
What I would like is for the results to also have a column indicating the week number relative to the date range. So if the date range is 1/1/2010 - 1/20/2010, then any results from the first Sun - Sat of that range are week 1, the next Sun - Sat are week 2, etc. If the date range starts on a Saturday, then only results from that one day would be week 1. If the date range starts on Thursday but the first result is on the following Monday, it would be week 2, and there are no week 1 results.
Is this something fairly simple to add to the query? The only ideas I can come up with would be based on the week number for the year or the week number based on the results themselves (where in that second example above, the first result always gets week 1).
Example:
$start = "05/27/2010";
$end = "06/13/2010";
//Query
Result:
week date user value
1 05/28/2010 joe 123
3 06/07/2010 joe 123
3 06/08/2010 joe 123
4 06/13/2010 joe 123
This can easily be done with this simple and obvious expression :)
SELECT ...,
FLOOR(
(
DATEDIFF(date, $start) +
WEEKDAY($start + INTERVAL 1 DAY)
) / 7
) + 1 AS week_number;
Some explanation: this expression just calculates difference between the given date and the start date in days, then converts this number to weeks via FLOOR("difference in days" / 7) + 1. That's simple, but since this works only when $start is Sunday, we should add an offset for other week days: WEEKDAY($start + INTERVAL 1 DAY) which equals to 0 for Sun, 1 for Mon, ..., 6 for Sat.
Related
I have the following postgresql table;
id | date_slot
------+-------------------------
1 | [2023-02-08,2023-02-15)
2 | [2023-02-20,2023-02-26)
3 | [2023-02-27,2023-03-29)
I want to make a query that return rows contained in these ranges but exclude weekends
for example the query I made return the following but does not exclude the weekends.
SELECT * FROM table where '2023-02-11'::date <# date_slot;
id | date_slot
------+-------------------------
1 | [2023-02-08,2023-02-15)
2023-02-11 is a weekend so it must not return a result. How can I do that?
Selecting workday-only dateranges (without weekends):
You can check what day of the week it is on the first day in the range using extract() and knowing its length from upper()-lower(), determine if it'll cross a weekend: online demo
select *
from test_table
where '2023-02-11'::date <# date_slot
and extract(isodow from lower(date_slot)
+ (not lower_inc(date_slot))::int)
+( (upper(date_slot) - (not upper_inc(date_slot))::int)
-(lower(date_slot) + (not lower_inc(date_slot))::int) ) < 6 ;
Cases where your ranges have varying upper and lower bound inclusivity are handled by lower_inc() and upper_inc() - their boolean result, when cast to int, just adds or subtracts a day to account for whether it's included by the range or not.
The range is on or includes a weekend if it starts on a weekend day or continues for too long from any other day of the week:
4 days, if it starts on a Monday (isodow=1)
3 days, if it starts on a Tuesday (isodow=2)
2 days, if it starts on a Wednesday (isodow=3)
1 days, if it starts on a Thursday (isodow=4)
0 days, if it starts on a Friday (isodow=5)
This means the isodow of range start date and the range length cannot sum up to more than 5 for the range not to overlap a weekend.
You can also enumerate the dates contained by these ranges using generate_series() and see if they include a Saturday (day 6) or a Sunday (0 as dow, 7 as isodow):
select *
from test_table
where '2023-02-11'::date <# date_slot
and not exists (
select true
from generate_series(
lower(date_slot) + (not lower_inc(date_slot))::int,
upper(date_slot) - (not upper_inc(date_slot))::int,
'1 day'::interval) as alias(d)
where extract(isodow from d) in (6,7) );
Selecting records based on workday-only dates:
First comment got it right
select *
from table_with_dateranges dr,
table_with_dates d
where d.date <# dr.date_slot
and extract(isodow from d.date) not in (6,7);
How to get data of last 6 quarter in Oracle including current quarter. I mean if I run the query today so data between 01-JAN-2018 to 30-JUN-2019 should come in the query.
You could do something like this:
SELECT
*
FROM
DUAL
WHERE
DATE_FIELD >= (SYSDATE - (30*(3*6)))
What this query is doing is taking the current date (SYSDATE), and grabbing all values from the previous 6 quarters. The rationale is:
30 = days in a month | 3 = months in a quarter | 6 = quarters specified by OP
You can use add_months and trunc functions for date value with Q(quarter) argument
select t.*
from tab t
where insert_date between
trunc(add_months(sysdate,-3*5),'Q')
and trunc(add_months(sysdate,3),'Q')-1;
Demo for verification
for the starting date, -3*(6-1) = -3*5 considered, starting from 5 quarter back to be able to count 6 quarter including the current quarter. 3 is
obvious as being the number of months in each quarter.
I have a date in sql which will always fall on a Monday and I'm subtracting 13 weeks to get a weekly report. I am trying to get the same 13 week report but for last year's figures as well.
At the moment, I'm using the following:
calendar_date >= TRUNC(sysdate) - 91
which is working fine.
I need the same for last year.
However, when I split this into calendar weeks, there will also be a partially complete week as it will include 1 or 2 days from the previous week. I need only whole weeks.
e.g. the dates that will be returned for last year will be 14-Feb-2015 to 16-May-2015. I need it to start on the Monday and be 16-Feb-2015. This will change each week as I am only interested in complete weeks...
I would do this:
Get the date by substracting 91 days as you're already doing.
Get the number of the day of the week with TO_CHAR(DATE,'d')
Add the number of days until the next monday to the date.
Something like this:
SELECT TO_DATE(TO_DATE('16/05/2015','DD/MM/YYYY'),'DD/MM/YYYY')-91 + MOD(7 - TO_NUMBER(TO_CHAR(TO_DATE(TO_DATE('16/05/2015','DD/MM/YYYY'),'DD/MM/RRRR')-91,'d'))+1,7) d
FROM dual
next_day - returns date of first weekday named by char.
with dates as (select to_date('16/05/2015','DD/MM/YYYY') d from dual)
select
trunc(next_day( trunc(d-91) - interval '1' second,'MONDAY'))
from dates;
I want to get next monday from calculated date. In situation when calculated date is monday i have to move back to previous week ( -1 second).
I want to perform an operation in crystal report.
I have a db table contains a date column.
I want to filter and get the rows having data created in last week(last sunday to last saturday = 7 days).For example if today is 24th August Wednesday, then I need data from 14th August(Sunday) to 20th August(Saturday).
Basically I want to find 2 dates and filter the date column.
Date1 = Date(CurrentDate)-Day(7 + WeekDayinNum(CurrentDate)) ; (Ex:for my example it will be 10)
Date2 = Date(CurrentDate)-Day(WeekDayinNum(CurrentDate))
I do not know the Date APIs properly,can anybody help me in this.
This is a common enough date range that CR provides it for you. In your record selection formula, you can just add {table.date} in LastFullWeek
From CR, "LastFullWeek specifies a range of Date values that includes all dates from Sunday to Saturday of the previous week."
Add this to the record selection formula:
{table.date_field} IN Last7Days
If today is Sunday(1) you want rows that are between 7 and 1 days old,
If today is Monday(2) you want rows that are between 8 and 2 days old,
If today is Tuesday(3) you want rows that are between 9 and 3 days old,
etc.
SELECT *
FROM `tablename`
WHERE `somedatefield` >= DATE_SUB(NOW(),INTERVAL (DAYOFWEEK(NOW()) + 6) DAY)
AND `somedatefield` <= DATE_SUB(NOW(),INTERVAL (DAYOFWEEK(NOW())) DAY)
I am using Oracle's to_char() function to convert a date to a week number (1-53):
select pat_id,
pat_enc_csn_id,
contact_date,
to_char(contact_date,'ww') week,
...
the 'ww' switch gives me these values for dates in January of this year:
Date Week
1-Jan-10 1
2-Jan-10 1
3-Jan-10 1
4-Jan-10 1
5-Jan-10 1
6-Jan-10 1
7-Jan-10 1
8-Jan-10 2
9-Jan-10 2
10-Jan-10 2
11-Jan-10 2
12-Jan-10 2
a quick look at the calendar indicates that these values should be:
Date Week
1-Jan-10 1
2-Jan-10 1
3-Jan-10 2
4-Jan-10 2
5-Jan-10 2
6-Jan-10 2
7-Jan-10 2
8-Jan-10 2
9-Jan-10 2
10-Jan-10 3
11-Jan-10 3
12-Jan-10 3
if I use the 'iw' switch instead of 'ww', the outcome is less desirable:
Date Week
1-Jan-10 53
2-Jan-10 53
3-Jan-10 53
4-Jan-10 1
5-Jan-10 1
6-Jan-10 1
7-Jan-10 1
8-Jan-10 1
9-Jan-10 1
10-Jan-10 1
11-Jan-10 2
12-Jan-10 2
Is there another Oracle function that will calculate weeks as I would expect or do I need to write my own?
EDIT
I'm trying to match the logic used by Crystal Reports. Each full week starts on a Sunday; the first week of the year starts on whichever day is represented by January 1st (e.g. in 2010, January 1st is a Friday).
When using IW, Oracle follows the ISO 8601 standard regarding week numbers (see http://en.wikipedia.org/wiki/ISO_8601). That is the same standard than the one we generally use in Europe here.
Your problem is also mentioned on the Oracle forum: http://forums.oracle.com/forums/thread.jspa?threadID=947291 and http://forums.oracle.com/forums/message.jspa?messageID=3318715#3318715. Maybe you can find a solution there.
I know this is old, but still a common question.
This should give you the correct results in the smallest amount of effort:
select pat_id,
pat_enc_csn_id,
contact_date,
to_char(contact_date + 1,'IW') week,
...
Since it looks like you are using your own special definition of the week number you'll need to write your own function.
It might be helpful that NLS_TERRITORY affects the day with which a week starts as used by the D Format Model
see also:
http://download.oracle.com/docs/cd/B19306_01/server.102/b14200/sql_elements004.htm#SQLRF00210
and
http://www.adp-gmbh.ch/ora/sql/to_char.html
Based on this question, How do I calculate the week number given a date?, I wrote the following Oracle logic:
CASE
--if [date field]'s day-of-week (e.g. Monday) is earlier than 1/1/YYYY's day-of-week
WHEN to_char(to_date('01/01/' || to_char([date field],'YYYY'),'mm/dd/yyyy'), 'D') - to_char([date field], 'D') > 1 THEN
--adjust the week
trunc(to_char([date field], 'DDD') / 7) + 1 + 1 --'+ 1 + 1' used for clarity
ELSE trunc(to_char([date field], 'DDD') / 7) + 1
END calendar_week