how to show week over week in sql - mysql

I need to display two calculated columns of the sum of GP for one week and in the other column previous
I think my way is right but i am missing something
I am trying to get my result to be like this::
|WEEK|JP|AUS|GB|
|PREV|22|32|23|
|CUR|12|15|12|
my sql is this I believe I may need to pivot the data
SELECT
(SUM(`GP`), `gptw`)
FROM
(SELECT
`GP`,
`Country`
FROM
`Finance`
WHERE DATE >= CURDATE() - INTERVAL DAYOFWEEK(CURDATE()) + 6 DAY
AND DATE < CURDATE() - INTERVAL DAYOFWEEK(CURDATE()) - 1 DAY
GROUP BY `Country`) AS lastweeek
INNER JOIN
(SELECT
SUM(`GP`) AS `gptw`,
`Country`
FROM
Finance
WHERE DATEDIFF(NOW(), `date`) < 4
GROUP BY `Country`) AS `thisweek`

Nitpick: DATE is a poor choice of column name because it's a reserved SQL word.
There are four levels of logic here.
First, you need to have working logic to figure out which week each day is in.
Second, you need to select the correct range of DATE values.
Third, you need to get the GROUP BY logic right.
Fourth, you need to pivot your result set to get this week and last week into the same row. This pivoting should be the subject of another question.
First, this expression will turn any DATETIME value into the preceding Sunday.
FROM_DAYS(TO_DAYS(value) -MOD(TO_DAYS(value) -1, 7))
Second, here's what you need to select two weeks (last week and this week)
WHERE `DATE` >= FROM_DAYS(TO_DAYS(NOW()) -MOD(TO_DAYS(NOW()) -1, 7)) - INTERVAL 7 DAY
AND `DATE` < FROM_DAYS(TO_DAYS(NOW()) -MOD(TO_DAYS(NOW()) -1, 7)) + INTERVAL 7 DAY
This gets the range of dates starting with the Sunday before last Sunday, and ending the moment before the Sunday after this Sunday, a two week range.
Third, you need to group correctly. In your schema, group by week and by country. It would go something like this:
SELECT SUM(`GP`) AS GP,
`Country`,
FROM_DAYS(TO_DAYS(`DATE`) -MOD(`DATE`) -1, 7)) AS week_beginning
FROM `Finance`
WHERE `DATE` >= FROM_DAYS(TO_DAYS(NOW()) -MOD(TO_DAYS(NOW()) -1, 7)) - INTERVAL 7 DAY
AND `DATE` < FROM_DAYS(TO_DAYS(NOW()) -MOD(TO_DAYS(NOW()) -1, 7)) + INTERVAL 7 DAY
GROUP BY `Country`, FROM_DAYS(TO_DAYS(`DATE`) -MOD(`DATE`) -1, 7))
ORDER BY `Country`, FROM_DAYS(TO_DAYS(`DATE`) -MOD(`DATE`) -1, 7))
This will give you two rows per country, one for the week_beginning a week ago, and one for the current week. This could do the trick for you. If not, you can pivot this query's results to arrange the rows the way you want them. That should be the subject of another question.
Finally, if you can define stored functions in your MySQL instance, you should define the function TRUNC_SUNDAY. Then you can write your query more readably like this:
SELECT SUM(`GP`) AS GP,
`Country`,
TRUNC_SUNDAY(`DATE`) AS week_beginning
FROM `Finance`
WHERE `DATE` >= TRUNC_SUNDAY(NOW()) - INTERVAL 7 DAY
AND `DATE` < TRUNC_SUNDAY(NOW()) + INTERVAL 7 DAY
GROUP BY `Country`, TRUNC_SUNDAY(`DATE`)
ORDER BY `Country`, TRUNC_SUNDAY(`DATE`)
Here is SQL code to define the stored TRUNC_SUNDAY function you need:
DELIMITER $$
DROP FUNCTION IF EXISTS `TRUNC_SUNDAY`$$
CREATE FUNCTION `TRUNC_SUNDAY`(datestamp DATETIME)
RETURNS DATE
NO SQL
DETERMINISTIC
COMMENT 'returns preceding Sunday'
RETURN FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -1, 7))$$
DELIMITER ;
You can find a detailed writeup of this technique, including a TRUNC_MONDAY function, here. You'll need the TRUNC_MONDAY function if your weekdays are defined as starting on Monday rather than Sunday. http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/

Related

How to get current week data till today's date using query?

How to fetch current week data till today's date ?
Example - Current week start from 2017-08-01 to 2017-08-07 and today date is 2017-08-03. I want to fetch data from 2017-08-01 to 2017-08-03 using query.
This is query -
SELECT *
FROM user_data
WHERE YEARWEEK(`date`, 1) = YEARWEEK(CURDATE(), 1)
I have resolved my problem.
SELECT *
FROM current_week
WHERE YEARWEEK(`dt`, 1) = YEARWEEK(CURDATE(), 1) and dt <= curdate() order by dt
This is working fine.
This one gets tricky in the last and first weeks of calendar years.
First you need to decide whether your week starts on Sunday or Monday. That is based on your national jurisdiction's business practices. In North America, it's generally Sunday. In Europe it is sometimes Monday.
You need an expression that computes the first moment of a calendar week based on a date.
This is such an expression for weeks starting Sunday.
FROM_DAYS(TO_DAYS(CURDATE()) -MOD(TO_DAYS(CURDATE()) -1, 7))
This is it for weeks starting Monday. (Notice the -2.)
FROM_DAYS(TO_DAYS(CURDATE()) -MOD(TO_DAYS(CURDATE()) -2, 7))
Now you can select stuff from your table that's in the current week.
SELECT whatever, whatever
FROM user_data
WHERE `date` >= FROM_DAYS(TO_DAYS(CURDATE()) -MOD(TO_DAYS(CURDATE()) -1, 7))
If you want stuff from the week before the current week use this
SELECT whatever, whatever
FROM user_data
WHERE `date` >= FROM_DAYS(TO_DAYS(CURDATE()) -MOD(TO_DAYS(CURDATE()) -1, 7)) - INTERVAL 7 DAY
AND `date` < FROM_DAYS(TO_DAYS(CURDATE()) -MOD(TO_DAYS(CURDATE()) -1, 7))

MySQL-Select start and end of week along with average of a column (week-by-week)

I am trying to get average of a column player_count week-by-week over the past 6 weeks. But the problem is that I also want the start and the end of the week date which corresponds to a specific average.
What I have tried:
SELECT AVG(player_count) as average,
updated_at,
updated_at + INTERVAL WEEKDAY(updated_at) + 7 DAY as EndDate
FROM `gtan_servers`
WHERE server_short_name = 'FiveRP'
GROUP BY WEEK(updated_at)
ORDER BY updated_at DESC
LIMIT 6
The updated_at column is intended to be taken as start of the week and EndDate is going to be taken as end of the week in which a specific average of the player count is provided.
But this query is not working correctly regarding the week dates. I can fetch the average yes but the week dates are not being fetched correctly. Any help would be highly appreciated.
You need an expression that truncates an arbitrary date to the first day of the week in which it occurs. That is, it returns 2017-05-21 (Sunday) if you give it 2017-05-24
This expression does that, assuming your weeks start on Sundays. Here's an explanation.
FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -1, 7))
Then you need to use that as a GROUP BY expression and a WHERE expression.
SELECT AVG(player_count) as average,
FROM_DAYS(TO_DAYS(updated_at) -MOD(TO_DAYS(updated_at) -1, 7)) week_beginning,
FROM_DAYS(TO_DAYS(updated_at) -MOD(TO_DAYS(updated_at) -1, 7)) + INTERVAL 6 DAY week_ending
FROM `gtan_servers`
WHERE server_short_name = 'FiveRP'
AND updated_at >= FROM_DAYS(TO_DAYS(NOW()) -MOD(TO_DAYS(NOW()) -1, 7)) - INTERVAL 6 WEEK
GROUP BY FROM_DAYS(TO_DAYS(updated_at) -MOD(TO_DAYS(updated_at) -1, 7))
ORDER BY 2 DESC
LIMIT 6
The WHERE automatically filters out records from your table that are too old for your report.
This query gets a little repetitive, but it works nicely.
You could create a stored function like this:
DELIMITER $$
DROP FUNCTION IF EXISTS TRUNC_SUNDAY$$
CREATE
FUNCTION TRUNC_SUNDAY(datestamp DATETIME)
RETURNS DATE DETERMINISTIC NO SQL
COMMENT 'returns preceding Sunday'
RETURN FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -1, 7))$$
DELIMITER ;
Then your query becomes more readable:
SELECT AVG(player_count) as average,
TRUNC_SUNDAY(updated_at) week_beginning,
TRUNC_SUNDAY(updated_at) + INTERVAL 6 DAY week_ending
FROM `gtan_servers`
WHERE server_short_name = 'FiveRP'
AND updated_at >= TRUNC_SUNDAY(NOW()) - INTERVAL 6 WEEK
GROUP BY TRUNC_SUNDAY(updated_at)
ORDER BY TRUNC_SUNDAY(updated_at) DESC
LIMIT 6
If your weeks start on Mondays change the -1 to a -2.

Select all MySQL records in the last day that are between a time range of 7am and midnight

I have this query
SELECT * FROM `timeclock_timecard`
WHERE `clock_in_datetime` > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
which can get record in the last day but I need to limit to records created after 7AM
Any help please?
SELECT * FROM `timeclock_timecard`
WHERE `clock_in_datetime` > DATE_SUB(CURDATE(), INTERVAL 1 DAY)
and hour(`clock_in_datetime`) > 7;
Added one more filter condition to check for the hour.
Your query was almost correct, because CURDATE() only gives the date you can just subtract 17 hours to get the correct result. fiddle.
SELECT * FROM `timeclock_timecard`
WHERE `clock_in_datetime` >= DATE_SUB(CURDATE(), INTERVAL 17 HOUR)
To get the entries of the current day, we can add 7 hours (CURDATE() has time 0:00).
SELECT * FROM `timeclock_timecard`
WHERE `clock_in_datetime` >= DATE_ADD(CURDATE(), INTERVAL 7 HOUR)
To get only rows from yesterday, with a time value of 7AM or later, we can add 7 hours to the expression.
If we only up until midnight of today (just rows from yesterday), we can add another condition, the datetime is less than midnight today.
For example:
SELECT t.*
FROM `timeclock_timecard` t
WHERE t.`clock_in_datetime` >= DATE(NOW()) + INTERVAL -1 DAY + INTERVAL 7 HOUR
AND t.`clock_in_datetime` < DATE(NOW())
If you want to exclude the exact 7:00:00 AM value, change the >= to just >.
FOLLOWUP
Q: What I actually want is between about 5-6am TODAY and mindnight TODAY so anytime during today that I run the report for today I will get only timeclock data from users who clocked in/out today only and not include yesterdays data.
A: The predicates are going to be of the form
WHERE t.`clock_in_datetime` >= expr1
AND t.`clock_in_datetime` < expr2
You just need to find the expressions expr1 and expr2 that return the appropriate datetime values.
Just use a simple SELECT statement to test:
SELECT DATE(NOW()) + INTERVAL 5 HOUR AS `start`
, DATE(NOW()) + INTERVAL 1 DAY AS `end`
Q: I also modified my select to take in account my datetime is in UTC and my result needs to get todays records using local timezone.
SELECT * , CONVERT_TZ( clock_in_datetime , '+00:00', '-4:00' ) FROM `timeclock_timecard`
A: Personally, I would do the timezone conversion on the exprN values, not the column values. Having predicates on bare columns allows MySQL to make effective use of an index; wrapping the columns in expressions prevents MySQL from using an index.
If the MySQL system clock is UTC, and your datetime values stored in the table are in a different timezone, yes, use the MySQL CONVERT_TZ function.
Again, using a simple SELECT statement to develop and test the expressions:
SELECT CONVERT_TZ( DATE(NOW()) + INTERVAL 5 HOUR, '+0:00', to_tz) AS `start`
, CONVERT_TZ( DATE(NOW()) + INTERVAL 1 DAY , '+0:00', to_tz) AS `end`
Where to_tz is the timezone of the values in the table.
Once you get expressions start and end returning the values you need, then use those expressions in the predicates of the query of the timecard table.

MySQL: need to calculate the last Friday of a month

I'm trying to solve a task: I have a table containing information about ships' battles. Battle is made of name and date. The problem is to get the last friday of the month when the battle occurred.
WITH num(n) AS(
SELECT 0
UNION ALL
SELECT n+1 FROM num
WHERE n < 31),
dat AS (
SELECT DATEADD(dd, n, CAST(battles.date AS DATE)) AS day,
dateadd(dd, 0, cast(battles.date as date)) as fight,
name FROM num,battles)
SELECT name, fight, max(day) FROM dat WHERE DATENAME(dw, day) = 'friday'
I thought there must be a maximum of date or something, but my code is wrong.
The result should look like this:
Please, help!!
P.S. DATE_FORMAT is not available
Possible problem: as spencer7593 noticed - and as I should have done and didn't - your original query is not MySQL at all. If you're porting a query that's OK. Otherwise this answer will not be helpful, as it makes use of MySQL functions.
The day you want is number 4 (0 being Sunday in MySQL).
So you want the last day of the month if the last day of the month is a 4; if the day of the month is a 5 you want a date which is 1 day earlier; if the day of the month is a 3 you want a date which is 1 day later, but that's impossible (the month ends), so you really need a date six days earlier.
This means that if the daynumber difference is negative, you want it modulo seven.
You can then build this expression (#DATE is your date; I use a fake date for testing)
SET #DATE='2015-02-18';
DATE_SUB(LAST_DAY(#DATE), INTERVAL ((WEEKDAY(LAST_DAY(#DATE))+7-4))%7 DAY);
It takes the last day of the month (LASTDAY(#DATE)), then it computes its weekday, getting a number from 0 to 6. Adds seven to ensure positivity after subtracting; then subtract the desired daynumber, in this case 4 for Friday.
The result, modulo seven, is the difference (always positive) from the last day's daynumber to the wanted daynumber. Since DATE_SUB(date, 0) returns the argument date, we needn't use IF.
SET #DATE='1962-10-20';
SELECT DATE_SUB(LAST_DAY(#DATE), INTERVAL ((WEEKDAY(LAST_DAY(#DATE))+7-4))%7 DAY) AS friday;
+------------+
| friday |
+------------+
| 1962-10-26 |
+------------+
Your query then would become something like:
SELECT `name`, `date`,
DATE_SUB(LAST_DAY(`date`),
INTERVAL ((WEEKDAY(LAST_DAY(`date`))+7-4))%7 DAY) AS friday
FROM battles;

How do I get the first day of the week of a date in mysql?

Suppose I have 2011-01-03 and I want to get the first of the week, which is sunday, which is 2011-01-02, how do I go about doing that?
The reason is I have this query:
select
YEAR(date_entered) as year,
date(date_entered) as week, <-------This is what I want to change to select the first day of the week.
SUM(1) as total_ncrs,
SUM(case when orgin = picked_up_at then 1 else 0 end) as ncrs_caught_at_station
from sugarcrm2.ncr_ncr
where
sugarcrm2.ncr_ncr.date_entered > date('2011-01-01')
and orgin in(
'Silkscreen',
'Brake',
'Assembly',
'Welding',
'Machining',
'2000W Laser',
'Paint Booth 1',
'Paint Prep',
'Packaging',
'PEM',
'Deburr',
'Laser ',
'Paint Booth 2',
'Toolpath'
)
and date_entered is not null
and orgin is not null
AND(grading = 'Minor' or grading = 'Major')
and week(date_entered) > week(current_timestamp) -20
group by year, week(date_entered)
order by year asc, week asc
And yes, I realize that origin is spelled wrong but it was here before I was so I can't correct it as too many internal apps reference it.
So, I am grouping by weeks but I want this to populate my chart, so I can't have all the beginning of weeks looking like different dates. How do I fix this?
If the week starts on Sunday do this:
DATE_ADD(mydate, INTERVAL(1-DAYOFWEEK(mydate)) DAY)
If the week starts on Monday do this:
DATE_ADD(mydate, INTERVAL(-WEEKDAY(mydate)) DAY);
more info
If you need to handle weeks which start on Mondays, you could also do it that way. First define a custom FIRST_DAY_OF_WEEK function:
DELIMITER ;;
CREATE FUNCTION FIRST_DAY_OF_WEEK(day DATE)
RETURNS DATE DETERMINISTIC
BEGIN
RETURN SUBDATE(day, WEEKDAY(day));
END;;
DELIMITER ;
And then you could do:
SELECT FIRST_DAY_OF_WEEK('2011-01-03');
For your information, MySQL provides two different functions to retrieve the first day of a week. There is DAYOFWEEK:
Returns the weekday index for date (1 = Sunday, 2 = Monday, …, 7 = Saturday). These index values correspond to the ODBC standard.
And WEEKDAY:
Returns the weekday index for date (0 = Monday, 1 = Tuesday, … 6 = Sunday).
If week starts on Monday
SELECT SUBDATE(mydate, weekday(mydate));
If week starts on Sunday
SELECT SUBDATE(mydate, dayofweek(mydate) - 1);
Example:
SELECT SUBDATE('2018-04-11', weekday('2018-04-11'));
2018-04-09
SELECT SUBDATE('2018-04-11', dayofweek('2018-04-11') - 1);
2018-04-08
Week starts day from sunday then get First date of the Week and Last date of week
SELECT
DATE("2019-03-31" + INTERVAL (1 - DAYOFWEEK("2019-03-31")) DAY) as start_date,
DATE("2019-03-31" + INTERVAL (7 - DAYOFWEEK("2019-03-31")) DAY) as end_date
Week starts day from Monday then get First date of the Week and Last date of week
SELECT
DATE("2019-03-31" + INTERVAL ( - WEEKDAY("2019-03-31")) DAY) as start_date,
DATE("2019-03-31" + INTERVAL (6 - WEEKDAY("2019-03-31")) DAY) as end_date
select '2011-01-03' - INTERVAL (WEEKDAY('2011-01-03')+1) DAY;
returns the date of the first day of week. You may look into it.
This is a much simpler approach than writing a function to determine the first day of a week.
Some variants would be such as
SELECT DATE_ADD((SELECT CURDATE() - INTERVAL (WEEKDAY(CURDATE())+1)DAY),INTERVAL 7 DAY) (for the ending date of a query, such as between "beginning date" and "ending date").
SELECT CURDATE() - INTERVAL (WEEKDAY(CURDATE())+1) DAY (for the beginning date of a query).
This will return all values for the current week. An example query would be as follows:
SELECT b.foo FROM bar b
WHERE b.datefield BETWEEN
(SELECT CURDATE() - INTERVAL (WEEKDAY(CURDATE())+1) DAY)
AND (SELECT DATE_ADD((SELECT CURDATE() - INTERVAL (WEEKDAY(CURDATE())+1)DAY),INTERVAL 7 DAY))
This works form me
Just make sure both dates in the below query are the same...
SELECT ('2017-10-07' - INTERVAL WEEKDAY('2017-10-07') Day) As `mondaythisweek`
This query returns: 2017-10-02 which is a monday,
But if your first day is sunday, then just subtract a day from the result of this and wallah!
If the week starts on Monday do this:
DATE_SUB(mydate, INTERVAL WEEKDAY(mydate) DAY)
SELECT MIN(DATE*given_date*) FROM *table_name*
This will return when the week started at for any given date.
Keep the good work going!