SQL get data between the last four weeks - sql

I would like to get data of the last four weeks. Now, I usually run the query on a Tuesday, therefore I have this code:
AND datatime between dateadd(day,-30,getdate()) and dateadd(day,-2,getdate())
I would like to run the query whenever I want (not just on Tuesday), but obtaining also the data from the same period (last four weeks from Monday to Sunday).
I have tried to do the following
AND datatime between dateadd(week,-4,getdate()) and dateadd(week,-1,getdate())
but it does not work, as the data obtained is not from the previous weeks (Monday to Sunday) but from the 7 days before the date in which I run it.
Any ideas on how to get the data, using that structure, so that I get data from the 4 previous weeks (Monday to Sunday) no matter when I run the query?
P.S I am using DBeaver.
Thank you.

If you want all data from the first monday in the last 4 weeks to today, you can use this :
AND datatime >= DATE_ADD(
DATE_ADD(CURDATE(), INTERVAL - WEEKDAY(CURDATE()) DAY),
INTERVAL - 4 WEEK)
But, if you want all the data from the first monday of the last 4 weeks to the last sunday, you can use this :
AND datatime >= DATE_ADD(
DATE_ADD(CURDATE(), INTERVAL - WEEKDAY(CURDATE()) DAY),
INTERVAL - 4 WEEK)
AND datatime < DATE_SUB(
DATE(NOW()), INTERVAL DAYOFWEEK(NOW())-2 DAY)
What's happening :
You can find Date and Time Functions Here. In this query, we are using:
DATE_ADD(start, Interval) : Add time values (Interval) to a date value (start),
DATE_SUB(end, Interval) : Subtract a time value (Interval) from a date (end),
CURDATE() : Returns the current date,
WEEKDAY(date) : Returns the weekday index for date (0 = Monday, 1 = Tuesday, … 6 = Sunday),
DAYOFWEEK(date) : Returns the weekday index for date (1 = Sunday, 2 = Monday, …, 7 = Saturday)
So :
DATE_ADD(
DATE_ADD(----------------------------------------------------------
CURDATE(), |
INTERVAL - WEEKDAY(CURDATE()) DAY //<== Return Today Index |-> Will Return the date of this week's monday : (25-01-2018 - 3 DAY) = 22-01-2018
(WEEKDAY(25-01-2018) = 3)|
),-----------------------------------------------------------------
INTERVAL - 4 WEEK //<== Will go 4 weeks back and start from monday
)
A working case Scenario.

Related

Get all days in a month excluding weekends postgresql

I want to write a query sql for postgresql that can basically return me all days of a month excluding weekends.
For example (For 11/2019) :
First Week: 11/1
Second Week : 11/4 -> 11/8
Third Week : 11/11 -> 11/15
Fouth Week : 11/18 -> 11/22
Fifth Week : 11/25 -> 11/29
I can't find any postgresql request that can help, this should be automatic instead of putting each time a new date manually.
One method is:
select dt
from generate_series(date'2019-11-01', date'2019-11-30', interval '1' day) as t(dt)
where extract(dow from dt) between 1 and 5
generate_series() produces a list of all days in the month, then the where clause filters on week days only.

How to subtract 13 weeks from a date in PL SQL?

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).

SQL Count days till first of the month

How would I count the days from a date till the first of the following month
Example:
--Start Date
07-07-2011
How many days till:
-- The 1st of the succeeding month of the start date above
08-01-2011
Expected Result (in days):
25
So if I counted the day I get 25, so running this query gets me the desired timestamp:
SELECT CURRENT_DATE + INTERVAL '25 DAYS'
Results:
2011-08-01 00:00:00
just can't think of a way to get the number of days, any suggestions?
Or start date, end date, number of days between?
I don't have a PostgreSQL server handy, so this is untested, but I would try:
SELECT (DATE_TRUNC('month', CURRENT_DATE) + INTERVAL '1 MONTH') - CURRENT_DATE

Group SQL results by week and specify "week-ending" day

I'm trying to select data grouped by week, which I have working, but I need to be able to specify a different day as the last day of the week. I think something needs to go near INTERVAL (6-weekday('datetime')) but not sure. This kind of SQL is above my pay-grade ($0) :P
SELECT
sum(`value`) AS `sum`,
DATE(adddate(`datetime`, INTERVAL (6-weekday(`datetime`)) DAY)) AS `dt`
FROM `values`
WHERE id = '123' AND DATETIME BETWEEN '2010-04-22' AND '2010-10-22'
GROUP BY `dt`
ORDER BY `datetime`
Thanks!
select
sum(value) as sum,
CASE WHEN (weekday(datetime)<=3) THEN date(datetime + INTERVAL (3-weekday(datetime)) DAY)
ELSE date(datetime + INTERVAL (3+7-weekday(datetime)) DAY)
END as dt
FROM values
WHERE id = '123' and DATETIME between '2010-04-22' AND '2010-10-22'
GROUP BY dt
ORDER BY datetime
This does look pretty evil but, this query will provide you with a sum of value grouped by a week ending on a Thursday (weekday() return of 3).
If you wish to change what day the end of the week is you just need to replace the 3's in the case statement, ie if you wanted Tuesday you would have it say
CASE WHEN (weekday(datetime)<=1) THEN date(datetime + INTERVAL (1-weekday(datetime)) DAY)
ELSE date(datetime + INTERVAL (1+7-weekday(datetime)) DAY)
I hope this helps.
Simple solution that I like. This will return the date for the start of the week assuming the week ends Sunday and starts Monday.
DATE(`datetime`) - INTERVAL WEEKDAY(`datetime`) AS `dt`
This can easily be adjusted to have a week ending on Thursday because Thursday is 3 days earlier than Sunday
DATE(`datetime`) - INTERVAL WEEKDAY(`datetime` + INTERVAL 3 DAY) AS `dt`
this returns for the start of the week that starts on Friday and ends on Thursday.
You can group on this no problem. If you want to use get the end of the week based on the start you do this
DATE(`datetime`) - INTERVAL -6 + WEEKDAY(`datetime` + INTERVAL 3 DAY) AS `dt`
I think you must choose between Sunday and Monday? When you can use DATE_FORMAT for grouping by string format of date, and use %v for grouping by Mondays and %v for grouping by Sundays.
SELECT
sum(`value`) AS `sum`,
DATE_FORMAT(`datetime`,'%v.%m.%Y') AS `dt`
FROM `values`
WHERE id = '123' AND DATETIME BETWEEN '2010-04-22' AND '2010-10-22'
GROUP BY DATE_FORMAT(`datetime`,'%v.%m.%Y')
ORDER BY `datetime`
How to use DATE_FORMAT
I don't remember the exact math, but you can get WEEKDAY to wrap around on different days of the week by adding or subtracting days to its argument. You'll need to tinker with different values of x and y in the expression:
x-weekday(adddate(`datetime`, INTERVAL y DAY))

Function for week of the month in mysql

I was looking for a simple function to get the week of the month (rather than the easy week of the year) in a mysql query.
The best I could come up with was:
WEEK(dateField) - WEEK(DATE_SUB(dateField, INTERVAL DAYOFMONTH(dateField)-1 DAY)) + 1
I'd love to know if I'm reinventing the wheel here, and if there is an easier and cleaner solution?
AFAIK, there is no standard on the first week of month.
First week of year is the week containing Jan 4th.
How do you define first week of month?
UPDATE:
You'll need to rewrite your query like this:
SELECT WEEK(dateField, 5) -
WEEK(DATE_SUB(dateField, INTERVAL DAYOFMONTH(dateField) - 1 DAY), 5) + 1
so that the year transitions are handled correctly, and the weeks start on Monday.
Otherwise, your query is fine.
There is an alternative that's sometimes used in reporting databases. It's to create a table, let's call it ALMANAC, that has one row per date (the key), and has every needed attribute of the date that might be useful for reporting purposes.
In addition to the week of the month column, there could be a column for whether or not the date is a company holiday, and things like that. If your company had a fiscal year that starts in July or some other month, you could include the fiscal year, fiscal month, fiscal week, etc. that each date belongs to.
Then you write one program to populate this table out of thin air, given a range of dates to populate. You include all the crazy calendar calculations just once in this program.
Then, when you need to know the attribute for a date in some other table, you just do a join, and use the column. Yes, it's one more join. And no, this table isn't normalized. But it's still good design for certain very specific needs.
(Just to clarify for future readers: this answer to this old question was added because of a bounty that implied the current answer wasn't satisfying, so I added this solution/definition with additional "configuration options" for all kinds of situations.)
There is no standard definition for the Week of month, but a general solution would be the following formula, that you can configurate to your needs:
select (dayofmonth(dateField) + 6 + (7 - "min_days_for_partial_week")
- (weekday(datefield) - "weekday_startofweek" + 7) MOD 7) DIV 7 as week_of_month;
where
"weekday_startofweek" has to be replaced by the weekday that you want to be the first day of the week (0 = Monday, 6 = Sunday).
"min_days_for_partial_week" is the number of days the first week has to have to count as week 1 (values 1 to 7). Common values will be 1 (the first day of the month is always week 1), 4 (this would be similar to "iso week of the year", where the first week of the year is the week that contains the thursday, so has at least 4 days), and 7 (week 1 is the first complete week).
This formula will return values 0 to 6. 0 means that the current week is a partial week that doesn't have enough days to count as week 1, and 6 can only happen when you allow partial weeks with less than 3 days to be week 1.
Examples:
If your first day of the week is Monday ("weekday_startofweek" = 0) and week 1 should always be a whole week ("min_days_for_partial_week" = 7), this will simplify to
select (dayofmonth(dateField) + 6 - weekday(datefield)) DIV 7 as week_of_month;
E.g., for 2016-06-02 you will get 0, because June 2016 started with a Wednesday and for 2016-06-06 you will get 1, since this is the first Monday in June 2016.
To emulate your formula, where the first day of the week is always week 1 ("min_days_for_partial_week" = 1), and the week starts with Sunday ("weekday_startofweek" = 6), this will be
select (dayofmonth(dateField) + 12 - (weekday(datefield) + 1) MOD 7) DIV 7 as week_of_month;
Although you might want to comment that properly to know in 2 years where your constants came from.
My solution with a week starts on a Sunday.
SELECT ( 1 + ((DATE_FORMAT( DATE_ADD(LAST_DAY( DATE_ADD('2014-07-17',
INTERVAL -1 MONTH)), INTERVAL 1 DAY),'%w')+1) +
(DATE_FORMAT('2014-07-17', '%d')-2) ) DIV 7) "week_of_month";
Might be 12 years too late but in case anyone still looking, I am using this calculation in bigquery MySQL for calculating week in month.
I'm using Monday as first day on my calculation
case when
(case when EXTRACT(DAYOFWEEK FROM date_add(date(my_date), INTERVAL -(EXTRACT(DAY FROM my_date))+1 day)) = 1 then 7
else EXTRACT(DAYOFWEEK FROM date_add(date(my_date), INTERVAL -(EXTRACT(DAY FROM my_date))+1 day)) -1 end) > 1 then -- check first day of month to decide if it's a complete week (starts on Monday)
case when EXTRACT(DAY FROM my_date) <= 7 then -- for incomplete week
case when
(case when EXTRACT(DAYOFWEEK FROM my_date) = 1 then 7 else EXTRACT(DAYOFWEEK FROM my_date)-1 end) - EXTRACT(DAY FROM my_date) =
(case when EXTRACT(DAYOFWEEK FROM date_add(date(my_date), INTERVAL -(EXTRACT(DAY FROM my_date))+1 day)) = 1 then 7
else EXTRACT(DAYOFWEEK FROM date_add(date(my_date), INTERVAL -(EXTRACT(DAY FROM my_date))+1 day)) -1 end) -1 then 1 -- incomplete week 1
else FLOOR(( EXTRACT(DAY FROM my_date) + (case when EXTRACT(DAYOFWEEK FROM date_add(date(my_date), INTERVAL -(EXTRACT(DAY FROM my_date))+1 day)) = 1 then 7
else EXTRACT(DAYOFWEEK FROM date_add(date(my_date), INTERVAL -(EXTRACT(DAY FROM my_date))+1 day)) -1 end) -2 )/7)+1 end -- calculate week based on date
else FLOOR(( EXTRACT(DAY FROM my_date) + (case when EXTRACT(DAYOFWEEK FROM date_add(date(my_date), INTERVAL -(EXTRACT(DAY FROM my_date))+1 day)) = 1 then 7
else EXTRACT(DAYOFWEEK FROM date_add(date(my_date), INTERVAL -(EXTRACT(DAY FROM my_date))+1 day)) -1 end) -2 )/7)+1 end -- calculate week based on date
else FLOOR((EXTRACT(DAY FROM my_date)-1)/7)+1 -- for complete week
end
The idea is to add the day difference (Monday to whatever day 1st of that month is) to date so it would divide correctly for week > 1
For week 1 (< date 7), I am calculation using day of week - date to get the end of 1st incomplete week (1st not on Monday).