Why did MySQL SUM IF quit working in 2021 - mysql

This code quit working in 2021 after working in 2020:
SELECT name, SUM(CASE WHEN YEARWEEK(prod_date) = YEARWEEK(now()- INTERVAL 1 WEEK) THEN gas_prod ELSE NULL END) AS LastWeek FROM daily_prod GROUP BY name
The code now calculates a larger number than occurred last week. Any reason why a new year would cause this code to work differently?

First, using the sum( case/when ) is a bad choice here. Your query is going against your ENTIRE daily_prod table, EVERY RECORD, but only summing when within the given yeardate() period based on whatever the current date is. Instead of doing that, build out a WHERE clause to just get the records you care about. The YearWeek() function is based on a Sunday to Saturday week schedule. So, for example this week is from Jan 31 at 12:00am (midnight) up to, but not including Feb 7th at 12:00am midnight. This includes everything up to Feb 6th at 11:59:59pm.
So, by doing embedded MySQL variables you can build as a "from" table alias as I will show. First, lets get whatever the current NOW() is which is inclusive of hour/minute/second and strip down to just the date portion. Ex: 2021-02-04 # 01:42am truncates down to just 2021-02-04 12:00am
select cast( now() as date )
From that, now, we need to go to the first of the week, represented as Sunday. For this, we use the day of the week and subtract 1 so it is a zero-based value where Sunday = 0, Saturday = 6. So Thursday, normally returns 5, subtract 1 = 4. February 4 - 4 days = Jan 31 which is the first of the current week.
select date_sub( [result of sql above], interval dayofweek( [result of sql above] ) -1 day )
Now, since you want the entire week of data, in this scenario, your data would be from Jan 31 at 12:00am in the morning up to Feb 6th (Saturday) at 11:59:59PM. So, take the above beginning of the week and add 1 week to it bringing you to Feb 7th.
select date_add( [result of 2nd query], interval 1 week )
Yes, this is intentional because now in your final query you can query LESS THAN Feb 7th.
So, by using a where on your data source, you only get those records, AND, if your table has an index on the transaction date, can be optimized for the query. To use inline variables, we can just use the results of these as part of the query such as:
SELECT
dp.name,
SUM( dp.gas_prod ) CurrwntWeekGas
FROM
daily_prod dp,
( select
-- first, variable #nd = Now as a date only
#nd := cast( now() as date ),
-- from the #nd, subtract 0-based day of week
-- example: Thursday is 5th day of week -1 = 4 days to subtract
#fow := date_sub( #nd, interval dayofweek( #nd ) -1 day ),
-- finally from the first of the week, add 1 week to the END point for the query
#nextWeek := date_add( #fow, interval 1 week )
) sqlvars
where
-- only grab records greater or equal to the first day of the week
dp.prod_date >= #fow
-- and ALSO LESS then the the beginning of NEXT week
AND dp.prod_date < #nextWeek
GROUP BY
dp.name
So, as the from clause alias "sqlvars" is processed, it creates the 3 "#" variables to define the beginning of week and beginning of NEXT week values. Those can then be applied to the where clause and limit just the records you need, not the full table.
If you really want the results of the prior week from the week you are in... Ex: the total from Jan 24th to Jan 30th since the current week has not completed, then just change your dayofweek() -1 to dayofweek() -8 to get the entire prior week, not the CURRENT week you are in the middle of now.

Related

How to get week number and date of that week between two dates using MySQL

I am using MySQL And I have two dates "From date" and "To date", and based on these date i want to get week number and dates of that week between "To" and "From" Dates.
I have tried the following mysql query.
SELECT count(*) as count,
CONCAT(DATE_FORMAT(DATE_ADD(FROM_DAYS(TO_DAYS(FROM_UNIXTIME(`webform_submissions`.`submitted`)) -MOD(TO_DAYS(FROM_UNIXTIME(`webform_submissions`.`submitted`)) -1, 7)),INTERVAL -6 DAY),'%M %d'), ' - ' ,DATE_FORMAT(FROM_DAYS(TO_DAYS(FROM_UNIXTIME(`webform_submissions`.`submitted`)) -MOD(TO_DAYS(FROM_UNIXTIME(`webform_submissions`.`submitted`)) -1, 7)),'%M %d')) as date ,
CONCAT(YEAR(FROM_UNIXTIME(`webform_submissions`.`submitted`)), '/', WEEK(FROM_UNIXTIME(`webform_submissions`.`submitted`))) as week
FROM `webform_submissions`
where `webform_submissions`.`nid` = 121
AND DATE_FORMAT(FROM_UNIXTIME(`webform_submissions`.`submitted`), '%Y-%m-%d') between '2019-11-01' and '2019-12-03'
GROUP BY week
ORDER BY `webform_submissions`.`submitted` ASC
The following result is display according to above query.
But it seems that it gives wrong result because week number 43 lies between 21-27 Oct and i want to get result between between '2019-11-01' and '2019-12-03'.
Expected output should be like the screenshot. Because From date "2019-11-01" lies between Oct 28- Nov 03 (Week 44). so records should be start from 44 week number.
Any Idea how to get correct number of week and dates?
Here's a somewhat easier to read version of your query (using nested subqueries since MySQL 5.6 doesn't support CTEs) and using DATE_FORMAT with the %x/%v format to generate the week to match your expected result (October 28 is the start of week 44). Note I've added a MIN into the generation of date so that the query will still work in MySQL 5.7 with SQL mode ONLY_FULL_GROUP_BY.
SELECT COUNT(*) AS count,
CONCAT(DATE_FORMAT(MIN(startofweek), '%M %d'),
' - ',
DATE_FORMAT(MIN(startofweek) + INTERVAL 6 DAY, '%M %d')) AS date,
week
FROM (SELECT submitted - INTERVAL (dayofweek + 6) % 7 DAY AS startofweek,
week
FROM (SELECT nid,
DATE(FROM_UNIXTIME(submitted)) AS submitted,
DATE_FORMAT(FROM_UNIXTIME(submitted), '%w') AS dayofweek,
DATE_FORMAT(FROM_UNIXTIME(submitted), '%x/%v') AS week
FROM webform_submissions
WHERE nid = 121
AND DATE(FROM_UNIXTIME(submitted)) BETWEEN '2019-11-01' AND '2019-12-03'
) AS dates
) AS ws
GROUP BY week
Output (for my sample data)
count date week
3 October 28 - November 03 2019/44
4 November 04 - November 10 2019/45
Demo on dbfiddle
Try to set the second parameter of WEEK() according to your expected result.
With the second parameter you can set the mode with wich you specify wether the week starts with sunday or monday, the week numbers starts with 0 or 1 and the rule defining the first week of the year.
See https://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_week

MySql get rows from last months same day

I want to get records from last month's same day by keeping in mind for days which will be skipped and should be included in next day.
e.g. on 31st of march, february 31 does not exist so it should skip the query and also if i want to get records on april 30 it will give the results as required, but then on 1st of may, 31st of march will be skipped.
Currently, I am using
SELECT * FROM registrations WHERE orderdate = DATE_SUB(CURDATE(), INTERVAL 1 month)
How can I tackle this in mysql query?
Sorry if i am not able to communicate my query.
Consider the following logic, which retains a record only if subtracting one month did not result in an earlier day value:
SELECT *
FROM registrations
WHERE
orderdate = DATE_SUB(CURDATE(), INTERVAL 1 month) AND
DAY(orderdate) = DAY(DATE_SUB(CURDATE(), INTERVAL 1 month));

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;

Display week number in mysql

I need to show the week number of month in mysql and currently I can get the week number of year when executing this query. I'm retrieving results per week and need to display the week number (as 1st week of January, 2nd week of January. 3rd week of January, etc). Can any body please help me to do this?
SELECT d.draftID_PK,
d.clientID_FK,
count(d.quoteNr) AS totalquote,
DATE_FORMAT (d.draftDate,'%u %M %Y') as draftDate,
c.clientName
FROM draft d
INNER JOIN client c ON c.clientID_PK = d.clientId_FK
WHERE d.draftDate<CURDATE() AND d.draftDate>'2013-01-05'
AND c.clientName = '{$client_name}'
GROUP BY DATE_FORMAT (d.draftDate,'%u')
ORDER BY d.draftDate DESC
I made something, maybe this can help you but the number of weeks varies according to the week begins, example:
Here in Brazil, the week starts in Sunday. So take a look if this can solve your problem.
SET #date:='2014-08-31';
SELECT
#first := date_add(
date_add(
LAST_DAY(#date),
interval 1 DAY
),
interval -1 MONTH
) AS first_day,
#date,
-- EXAMPLE 1
WeekofYear(#date),
WeekofYear(#first),
WeekofYear(#date)-(WeekofYear(#first)-1) Num_Week,
-- EXAPLE 2 - Works in Brazil perfectly
Week(#date),
Week(#first),
Week(#date)-(Week(#first)-1) Num_Week_Brazil

How to check if date is in range regardless of the year

I'm wondering what would be the easiest way in MySQL to check if given date is in range regardless of the year.
In database table I have two DATE fields: start and finish stored in YYYY-mm-dd
if start = 2013-11-01 and finish = 2014-03-01 anything between 1st of November and 1st of March of any year should be accepted.
Valid dates:
2020-01-01 1980-02-28
Invalid dates:
2013-10-30 1968-07-30
There are almost certainly cleaner ways of doing it, however this should work:
((DAYOFYEAR(finish_date) > DAYOFYEAR(start_date)
AND (DAYOFYEAR(#date) >= DAYOFYEAR(start_date)
AND DAYOFYEAR(#date) <= DAYOFYEAR(finish_date)))
OR (DAYOFYEAR(finish_date) <= DAYOFYEAR(start_date)
AND (DAYOFYEAR(#date) >= DAYOFYEAR(start_date)
OR DAYOFYEAR(#date) <= DAYOFYEAR(finish_date))))
For a start date in Oct 2012 and end date in Nov 2020 this will return all dates in the Oct-Nov range. If in fact would want it to return all Dates when the range is greater than a year (and hence covers all dates of the year) you could add:
OR DATEDIFF(Day, start_date, finish_date) > 356
before the final bracket.
use DAYOFYEAR:
When the Start Date is earlier in the year than the Finished Date:
the tested Date should lye between Start Date and Finish Date (or on Start or Finish)
When the Finished Date is earlier in the year than the Start Date:
the tested Date should lye outside the Start Date and Finish Date (or on Start or Finish)
You can use some date extract function and then check your condition..
for example.
SELECT EXTRACT(MONTH FROM TIMESTAMP '2013-11-01 20:38:40');
this will give ouput start month as 11
SELECT EXTRACT(MONTH FROM TIMESTAMP '2014-03-01 20:38:40');
this will give ouput end month as 3
now you can check the condition from above two result..
SELECT * FROM tableWithDates t WHERE month(t.start) >= 11 AND month(t.finish) < 3
if you want the first of march it will go like this:
SELECT * FROM tableWithDates t WHERE month(t.start) >= 11 AND (month(t.finish) < 3 OR month(finish) <= 3 AND day(finish)<=1)
Depending on the size of the data you will run this at. You can get into performance problems, as MySQL can't use indexes of calculated columns.
If you run into this i suggest spitting the month AND/OR day into separate columns.
Edit:
Given an one parameter input as '2008-02-29'
SELECT * FROM tableWithDates t
WHERE
month(t.start) >= month('2008-02-29') AND day(t.start) >= day('2008-02-29')
AND month(t.finish) <= month('2008-02-29') AND day(t.finish) <= day('2008-02-29')