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;
Related
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.
id start_date interval period
1 2018-01-22 2 month
2 2018-02-25 3 week
3 2017-11-24 3 day
4 2017-07-22 1 year
5 2018-02-25 2 week
the above is my table data sample. start_dates will be expired based on interval and period(i.e id-1 will have due date after 2 months from the start_date, id-2 will have due after 3 weeks vice versa). period is enum of (day,week,month,year). requirement is, Client can give any period of dates. let's say 25-06-2026 to 13-07-2026 like that.. I have to return the ids whose due dates falls under that period.I hope i made my question clear.
I am using mysql 5.7. I found a way to achieve this with recursive CTE's.(not available in mysql 5.7). and there is a way to achieve this by populating virtual records by using inline sub queries along with unions but its a performance killer and we can't do populate virtual records every time a client request comes.(like given in the link Generating a series of dates) I have reached a point to get results for a single date which is very easy. Below is my query.
SELECT b.*
FROM (SELECT a.*,
CASE
WHEN period = 'week' THEN MOD(Datediff('2018-07-22', start_date), 7 * intervals)
WHEN period = 'month'
AND Day('2018-07-22') = Day(start_date)
AND MOD(Period_diff(201807, Extract(YEAR_MONTH FROM start_date)), intervals) = 0 THEN 0
WHEN period = 'year'
AND Day('2018-07-22') = Day(start_date)
AND MOD(Period_diff(201807, Extract(
YEAR_MONTH FROM start_date)) / 12,
intervals) = 0 THEN 0
WHEN period = 'day' THEN MOD(Datediff('2018-07-22', start_date) , intervals)
end filters
FROM kml_subs a)b
WHERE b.filters = 0;
But I need to do this for a period of dates not a single date. Any suggestions or solutions will be much appreciated.
My desired result shoud be like..
if i give two dates.say 2030-05-21 & 2030-05-27. due dates falls under those 6 dates between(2030-05-21 & 2030-05-27) will be shown in the result.
id
1
4
My question is different from Using DATE_ADD with a Column Name as the Interval Value . I am expecting a dynamic way to check due dates based on start_date
Thanks, Kannan
In MySQL, it would seem that a query along these lines would suffice. (Almost) everything else could and should be handled in application level code...
SELECT *
, CASE my_period WHEN 'day' THEN start_date + INTERVAL my_interval DAY
WHEN 'week' THEN start_date + INTERVAL my_interval WEEK
WHEN 'month' THEN start_date + INTERVAL my_interval MONTH
WHEN 'year' THEN start_date + INTERVAL my_interval YEAR
END due_date
FROM my_table;
How would I go about writing a statement in MySQL to return the previous start and end date of the previous quarter? Say if I wanted the start and end date of the quarter before this one (i.e. beginning of last October and end of last December).
Try this one:
SELECT
MAKEDATE(YEAR(CURDATE()), 1) + INTERVAL QUARTER(CURDATE())-1 QUARTER - INTERVAL 1 DAY,
MAKEDATE(YEAR(CURDATE()), 1) + INTERVAL QUARTER(CURDATE())-2 QUARTER
I find it helpful to use this TRUNC_QUARTER stored function. It converts any date or date/time value into midnight on the first day of the calendar quarter in which it occurs. Then you can use INTERVAL arithmetic to manipulate it.
DELIMITER $$
DROP FUNCTION IF EXISTS TRUNC_QUARTER$$
CREATE
FUNCTION TRUNC_QUARTER(datestamp DATETIME)
RETURNS DATE DETERMINISTIC NO SQL
COMMENT 'returns preceding first of the quarter'
RETURN DATE(CONCAT(YEAR(datestamp),'-', 1 + 3*(QUARTER(datestamp)-1),'-01'))$$
DELIMITER ;
With it you can write:
SELECT TRUNC_QUARTER(CURDATE()) - INTERVAL 1 QUARTER start_last_quarter,
TRUNC_QUARTER(CURDATE()) - INTERVAL 1 DAY end_last_quarter
Begin and end of quarters are fixed, so there is no need to actually “calculate” those – we know they are January 1st, April 1st, etc, and March 31st, June 30th, etc. respectively.
So all that leaves us with is to get the correct year prepended to those fixed dates – if the current quarter is Q1, then go back one year, else use the current year.
QUARTER(NOW()) will give us the current quarter (from 1 to 4), so this could be as simple as this:
SELECT
CASE QUARTER(NOW())
WHEN 1 THEN DATE_FORMAT(NOW() - INTERVAL 1 YEAR, '%Y-10-01')
WHEN 2 THEN DATE_FORMAT(NOW(), '%Y-01-01')
WHEN 3 THEN DATE_FORMAT(NOW(), '%Y-04-01')
WHEN 4 THEN DATE_FORMAT(NOW(), '%Y-07-01')
END AS previous_quarter_begin,
CASE QUARTER(NOW())
WHEN 1 THEN DATE_FORMAT(NOW() - INTERVAL 1 YEAR, '%Y-12-31')
WHEN 2 THEN DATE_FORMAT(NOW(), '%Y-03-31')
WHEN 3 THEN DATE_FORMAT(NOW(), '%Y-06-30')
WHEN 4 THEN DATE_FORMAT(NOW(), '%Y-09-30')
END AS previous_quarter_end
http://sqlfiddle.com/#!9/9eecb/1858
If you want those dates in a different format, say mm/dd/yyyy instead, then you can simply change the format specified to 10/01/%Y etc.
(And if you want to test this, to see if it actually works correctly for different dates, then you can replace each occurrence of NOW() in the above query with a fixed date, like say '2015-12-22' to see if this will give the expected result for a date in December.)
This will work such that if the date is in the first quarter, it'll roll back to the previous year in quarter 4.
SELECT #dateLastQuarter := DATE_SUB('yourdate', INTERVAL 3 MONTH);
SELECT MAKEDATE(YEAR(#dateLastQuarter), 1) + INTERVAL QUARTER(#dateLastQuarter) QUARTER - INTERVAL 1 QUARTER,
MAKEDATE(YEAR(#dateLastQuarter), 1) + INTERVAL QUARTER(#dateLastQuarter) QUARTER - INTERVAL 1 DAY;
I am trying to get one week earlier then current week of the year but my sql query is returning null. here is my query
select date_sub(yearweek('2014-01-01'),INTERVAL 1 week)
what is wrong with this query
If you want to get YEARWEEK of week prior to date, you can do this:
Note: YEARWEEK results in 6-digit number, first 4 digits are week year, trailing 2 digits are week number.
SELECT YEARWEEK('2014-01-01' - INTERVAL 1 WEEK)
If you need to get a date that is one week before a given date, then:
SELECT '2014-01-01' - INTERVAL 1 WEEK
Try this
select date_sub(date('2014-01-01'),INTERVAL 1 week)
Try this:-
DATE_SUB(date('2014-01-01'), INTERVAL 7 DAY)
or
SELECT '2014-01-01' - INTERVAL 1 WEEK
The problem is that DATE_SUB takes a date as the first arguement, but year week returns yyyyww i.e. not a date. So this:
SELECT yearweek('2014-01-01');
Returns 201352, this is then implicitly casted to a date, since it is not a date the result is null. You can replicate this by doing:
SELECT DATE(yearweek('2014-01-01'));
So if you subtract a week from NULL the result is also NULL.
The fix is to subtract the week first, then get the year week:
SELECT yearweek(date_sub('2014-01-01', INTERVAL 1 WEEK));
And the result is 201351.
Are you looking for week number??? If yes then plz try this if it will work for you
Select DatePart(Week, Date add(day,-7,convert(date time,'01-jan-2014')))
Pleas let me know if you are looking for something else.
SELECT * FROM [table] WHERE WEEKOFYEAR(date) = WEEKOFYEAR(NOW()) - 1;
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!