I want a MySQL query that will fetch all Fridays with date for the year 2017.
I know that SQL query for the same is:
SELECT Fridays = DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), n.num)
FROM (SELECT TOP 366 num = ROW_NUMBER() OVER(ORDER BY a.NAME)-1 FROM dbo.syscolumns a, dbo.syscolumns b) n
WHERE DATENAME(weekday, DATEADD(yy, DATEDIFF(yy, 0, GETDATE()), n.num)) = 'Friday'
I am looking for a MySQL alternative for the same.
This is an exact conversion of your sql logic in mysql
SELECT DATE_ADD(MAKEDATE(year(now()),1), INTERVAL #num:=#num+1 DAY)
From (select #num:=-1) num
where DAYNAME(DATE_ADD(MAKEDATE(year(now()),1), INTERVAL #num:=#num+1 DAY))="Friday"
limit 365
Mysql does not have row_number function until version 8. Prior to version 8 row_number can be simulated using variables.
select s.num , date_add(str_to_date(concat('2016','/','12','/','31'),'%Y/%m/%d'), interval s.num day) dt,
dayname(date_add(str_to_date(concat('2016','/','12','/','31'),'%Y/%m/%d'), interval s.num day)) dy
from
(
select #num:=#num + 1 num from information_schema.columns,(select #num:=0) n
limit 35
) s
where dayname(date_add(str_to_date(concat('2016','/','12','/','31'),'%Y/%m/%d'), interval s.num day)) = 'Friday';
information_schema.columns is similar to dbo.syscolumns.
------+------------+--------+
| num | dt | dy |
+------+------------+--------+
| 6 | 2017-01-06 | Friday |
| 13 | 2017-01-13 | Friday |
| 20 | 2017-01-20 | Friday |
| 27 | 2017-01-27 | Friday |
| 34 | 2017-02-03 | Friday |
+------+------------+--------+
5 rows in set, 2 warnings (0.26 sec)
SELECT * FROM table WHERE ([date] BETWEEN date_start and date_end) AND
DAYNAME([date])='Friday'
Source of the query: Get data for every friday between two dates
Related
I have a table like a table below.
I want to select count and group by day.
But the data in 1 day will start counts at 7:00:00 until tomorrow at 6:59:59 (24hr.).
For example
Day 1 data between '2019/06/01 7:00:00' and '2019/06/02 06:59:59'
Day 2 data between '2019/06/02 7:00:00' and '2019/06/03 06:59:59'
How can I code the where condition?
id | create_date | judge |
-----+---------------------+---------+
1 | 2019-06-02 8:00:00 | ok |
2 | 2019-06-02 9:00:00 | ok |
3 | 2019-06-02 10:00:00 | ok |
4 | 2019-06-02 11:00:00 | ok |
5 | 2019-06-02 15:00:00 | ok |
6 | 2019-06-03 4:00:00 | ok |
7 | 2019-06-03 5:00:00 | ok |
8 | 2019-06-03 8:00:00 | ok |
9 | 2019-06-03 9:00:00 | ok |
10 | 2019-06-03 9:00:00 | fail |
I've tried below but the result is not as expected.
SELECT COUNT(*),DAY(create_date)
FROM mytable
WHERE judge = 'ok' and MONTH(create_date) = '6' and YEAR(create_date) = '2019' and TIME(create_date) > '07:00:00'
Group by DAY(create_date) order by DAY(create_date) ASC
Expected results
COUNT(*) | DAY(create_date) |
-----------+---------------------+
7 | 2 | (from id 1 to 7)
2 | 3 | (from id 8 and 9)
You could subtract seven hours from each date, truncate them to show the date only and then group them:
SELECT DATE(DATE_SUB(create_date, INTERVAL 7 HOUR)), COUNT(*)
FROM mytable
-- Where clause if you need it...
GROUP BY DATE(DATE_SUB(create_date, INTERVAL 7 HOUR))
Just subtract 7 hours for the aggregation and the date/time comparisons:
SELECT DATE(create_date - interval 7 hour) as dte, COUNT(*)
FROM mytable
WHERE judge = 'ok' and
create_date >= '2019-06-01 07:00:00' AND
create_date < '2019-07-01 07:00:00'
GROUP BY DATE(create_date - interval 7 hour)
ORDER BY dte;
Try this-
SELECT
CAST(DATE_SUB(create_date, INTERVAL 7 HOUR) AS DATE),
COUNT(*)
FROM YOUR_TABLE
GROUP BY CAST(DATE_SUB(create_date, INTERVAL 7 HOUR) AS DATE)
I have the following stored procedure get the last 12 months from the current date in MySql
DELIMITER $$
CREATE
PROCEDURE `Calendar`()
BEGIN
CREATE TEMPORARY TABLE IF NOT EXISTS `Cal` (
`month` INT(11) ,
`year` INT(11)
);
TRUNCATE TABLE Cal;
SET #counter := -1;
WHILE (DATEDIFF(DATE(NOW()), DATE(DATE_SUB(NOW(),INTERVAL #counter MONTH))) < DATEDIFF(DATE(NOW()), DATE(DATE_SUB(NOW(),INTERVAL 12 MONTH)))) DO
INSERT INTO Cal SELECT DATE_FORMAT(DATE_ADD(DATE_SUB(NOW(),INTERVAL 12 MONTH), INTERVAL #counter:=#counter + 1 MONTH),'%m'),DATE_FORMAT(DATE_ADD(DATE_SUB(NOW(),INTERVAL 12 MONTH), INTERVAL #counter + 1 MONTH),'%Y');
END WHILE;
SELECT * FROM `Cal`;
END$$
DELIMITER ;
DROP PROCEDURE IF EXISTS Calendar;
The output table is
9 2017
10 2017
11 2017
12 2018
1 2018
2 2018
3 2018
4 2018
5 2018
6 2018
7 2018
8 2018
9 2018
In the month 12, the year is 2018. It should be 2017 and also the row 9 2017 shouldn't be there since its the 13th month including the current month. How can I make the 12th month's year to 2017 and remove the 9 2017 Thanks in advance
Here's a stored procedure that will give you the output you desire. It's longer but simpler than what you were attempting.
DELIMITER //
DROP PROCEDURE IF EXISTS Calendar //
CREATE PROCEDURE Calendar ()
BEGIN
DECLARE month INT DEFAULT MONTH(CURDATE());
DECLARE year INT DEFAULT YEAR(CURDATE()) - 1;
CREATE TEMPORARY TABLE CAL (month INT, year INT);
REPEAT
SET month = month + 1;
IF (month = 13) THEN
SET month = 1;
SET year = year + 1;
END IF;
INSERT INTO CAL VALUES (month, year);
UNTIL month = MONTH(CURDATE())
END REPEAT;
SELECT * FROM CAL;
DROP TABLE CAL;
END //
DELIMITER ;
CALL Calendar();
Output:
month year
10 2017
11 2017
12 2017
1 2018
2 2018
3 2018
4 2018
5 2018
6 2018
7 2018
8 2018
9 2018
You can also do it without a creating a stored procedure, temporary table, inserting into temporary table and dropping the stored procedure after
By using a number generator in MySQL code.
Query
SELECT
MONTH(NOW() - INTERVAL number_generator.number MONTH) AS `month`
, YEAR(NOW() - INTERVAL number_generator.number MONTH) AS `year`
FROM (
SELECT
(#NUMBER := #NUMBER + 1) AS number
FROM (
SELECT 1 AS number UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10
) AS records_1
CROSS JOIN (
SELECT 1 AS number UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10
) AS records_2
CROSS JOIN (SELECT #NUMBER := 0) AS init_user_params
) AS number_generator
WHERE
number_generator.number BETWEEN 1 AND 12
AND
MONTH(NOW()) <> MONTH(NOW() - INTERVAL number_generator.number MONTH)
ORDER BY
YEAR(NOW() - INTERVAL number_generator.number MONTH) ASC
, MONTH(NOW() - INTERVAL number_generator.number MONTH) ASC
Results
| month | year |
|-------|------|
| 10 | 2017 |
| 11 | 2017 |
| 12 | 2017 |
| 1 | 2018 |
| 2 | 2018 |
| 3 | 2018 |
| 4 | 2018 |
| 5 | 2018 |
| 6 | 2018 |
| 7 | 2018 |
| 8 | 2018 |
see demo http://sqlfiddle.com/#!9/340e01/584
Note if you use MySQL 8.0+ the syntax is more clear using common table expressions.
Query
WITH RECURSIVE number_generator (number) AS
(
SELECT 1 AS number
UNION ALL
SELECT number + 1 FROM number_generator WHERE number < 12
)
SELECT
MONTH(NOW() - INTERVAL number_generator.number MONTH) AS `month`
, YEAR(NOW() - INTERVAL number_generator.number MONTH) AS `year`
FROM
number_generator
WHERE
MONTH(NOW()) <> MONTH(NOW() - INTERVAL number_generator.number MONTH)
ORDER BY
YEAR(NOW() - INTERVAL number_generator.number MONTH) ASC
, MONTH(NOW() - INTERVAL number_generator.number MONTH) ASC
Results
| month | year |
|-------|------|
| 10 | 2017 |
| 11 | 2017 |
| 12 | 2017 |
| 1 | 2018 |
| 2 | 2018 |
| 3 | 2018 |
| 4 | 2018 |
| 5 | 2018 |
| 6 | 2018 |
| 7 | 2018 |
| 8 | 2018 |
see demo https://www.db-fiddle.com/f/chJ4qS2ocXS79vGkFBg7PR/13
I have "alerts" table with date field - targetDate.
I would like to select all data that past 45 days.
I tried the code below but it's not return any results...
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon'
AND DATEDIFF( CURDATE( ) , targetDate ) > 45
Table
id userID type refID createDate targetDate lastSendDate sent valid
1 26 travelSoon NO 2018-05-02 13:54:25 0000-00-00 2018-05-02 00:00:00 0 1
2 26 travelSoon NO 2018-05-02 13:55:50 2018-06-01 0000-00-00 00:00:00 0 1
3 26 travelSoon DK 2018-05-02 13:56:12 2018-12-01 0000-00-00 00:00:00 0 1
4 26 travelSoon 2018-05-02 13:59:50 0000-00-00 0000-00-00 00:00:00 0 1
5 26 travelSoon 2018-05-02 14:00:09 2018-08-01 0000-00-00 00:00:00 0 1
6 26 travelSoon DK 2018-05-02 14:00:48 2018-08-01 0000-00-00 00:00:00 0 1
7 26 travelSoon 2018-05-02 16:45:18 2018-05-01 0000-00-00 00:00:00 0 1
8 26 travelSoon RO 2018-05-02 16:45:45 2018-04-01 0000-00-00 00:00:00 0 1
Using DATEDIFF() is a bad idea. It blocks the ability to use indexes, and there is an alternative that doesn't...
SELECT *
FROM alerts
WHERE type = 'travelSoon'
AND targetDate >= DATEADD(DAY, -45, GETDATE()) -- SQL Server
AND targetDate >= CURDATE() - INTERVAL 45 DAY -- MySQL
http://www.sqlfiddle.com/#!9/4ecdc0/6
In MSSQL DATEDIFF(interval, date1, date2) returns interval of date2 - date1.
Interval should be selected from this list:
- year, yyyy, yy = Year
- quarter, qq, q = Quarter
- month, mm, m = month
- dayofyear = Day of the year
- day, dy, y = Day
- week, ww, wk = Week
- weekday, dw, w = Weekday
- hour, hh = hour
- minute, mi, n = Minute
- second, ss, s = Second
- millisecond, ms = Millisecond`
Then use:
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon'
AND DATEDIFF(day, targetDate, GETDATE() ) > 45
For MySQL you can use TIMESTAMPDIFF(unit,date1,date2) which returns interval of date1 - date2.
unit can be selected from MICROSECOND (microseconds), SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, or YEAR.
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon'
AND TIMESTAMPDIFF(DAY, CURDATE( ), targetDate) > 45
The ANSI Standard syntax would be:
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon' AND
targetDate >= CURRENT_DATE - interval '45 day' AND
targetDate <= CURRENT_DATE;
In MySQL (which your syntax suggests:
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon' AND
targetDate >= CURRENT_DATE - interval 45 day AND
targetDate <= CURRENT_DATE;
Try this...
SELECT userid, refid, `targetdate`
FROM alerts
WHERE type = 'travelSoon'
AND Datediff(Curdate(), targetDate) < 45 -- or <=45
AND Datediff(Curdate(), targetDate) > 0
Online Demo: http://www.sqlfiddle.com/#!9/4ecdc0/4/0
If you only use Datediff(Curdate(), targetDate) < 45 condition, it may return both past and future dates. Please refer the below table.
Today: May 10, 2018
+----+-------------+
| id | targetDate | DATEDIFF(CURDATE(), targetDate)
+----+-------------+
| 2 | 𝟮𝟬𝟭𝟴-𝟬𝟲-𝟬𝟭 | -22
| 3 | 𝟮𝟬𝟭𝟴-𝟭𝟮-𝟬𝟭 | -205
| 5 | 𝟮𝟬𝟭𝟴-𝟬𝟴-𝟬𝟭 | -83
| 6 | 𝟮𝟬𝟭𝟴-𝟬𝟴-𝟬𝟭 | -83
| 7 | 2018-05-01 | 9
| 8 | 2018-04-01 | 39
+----+-------------+
To avoid this, you can use another condition like this...
Datediff(Curdate(), targetDate) > 0
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon'
AND targetDate >= ( CURDATE() - INTERVAL 45 DAY )
I have this kind of table with time based data:
| entity_id | ttime | value |
-------------------------------------------
| 1 | 2014-11-01 00:00:04 | 553 |
| 1 | ... | 600 |
| 2 | ... | 234 |
I want to get the average of the value grouped by week and entity_id. But I would like also the starting day of the week to appear in the results. Additional complexity is that the week starts on wednesday.
I can group by YEAR(ttime + INTERVAL 3 DAY), WEEK(ttime + INTERVAL 3 DAY) but is it possible to print the first day of the group (wednesday) in the results?
Thanks
maybe something like this:
SELECT
`entity_id`,
DATE_SUB(ttime, INTERVAL WEEKDAY(ttime)-2 DAY),
SUM(`value`)
FROM `table`
GROUP BY `entity_id`, YEARWEEK(ttime + INTERVAL 4 DAY)
SqlFiddle
I found this solution:
SELECT
str_to_date(CONCAT(YEAR(ttime + INTERVAL -3 DAY),
WEEK(ttime + INTERVAL -3 DAY), 'Wednesday'), '%X%V %W') as WeekCommencing,
entity_id, AVG(value),
FROM `table`
GROUP BY WeekCommencing, entity_id
Is there a way in mysql to group by month, but with custom starting dates.
Say I want to count logins in a monthly basis, but with the condition that the month starts when a user register.
So for example user A registered on January 30th and user B on January 15th
I should group the logins as follow:
* User A: January 30th - February 28th, March 1st - March 30th, March 31 - April 30 and so on and so forth
* User B: January 15th - February 14th, February 15th - March 14th and so on and so forth
I guess I need to use something like DATE_ADD('2013-01-30', INTERVAL 1 MONTH); but I can not seem to find a way to make the grouping.
UPDATE
#GarethD: You are right that was a typo
In general the month should start at the same day of the next month or the last day of the next month in case that the first is not possible, so if you registered in day 31, the month period would start in day 30 for months that does not have 31 days and the last day of February either 28 or 29
Example:
Given that
id 1 registered on 2012-12-16
id 2 registered on 2013-01-29
and the following table
+----+------------+
| id | date |
+----+------------+
| 1 | 2013-01-15 |
| 1 | 2013-01-16 |
| 1 | 2013-01-17 |
| 1 | 2013-01-17 |
| 2 | 2013-03-20 |
| 2 | 2013-03-21 |
| 2 | 2013-03-28 |
| 2 | 2013-03-29 |
| 2 | 2013-03-30 |
+----+------------+
the results should be
+----+----------------------------+-------+
| id | range | count |
+----+----------------------------+-------+
| 1 | 2012-12-16, 2013-01-15 | 1 |
| 1 | 2013-01-16, 2013-02-15 | 3 |
| 2 | 2013-02-2[8|9], 2013-03-28 | 3 |
| 2 | 2013-03-29, 2013-04-28 | 2 |
+----+----------------------------+-------+
I hope the intent is clearer now.
For the following I am assuming you already have a numbers table, If you don't have a numbers table, then I'd recommend you make one then, but if you don't want to then you can create a number list on the fly
You can get a list of all boundaries by cross joining your userID and registered dates with your numbers table:
SELECT u.ID,
DATE_ADD(RegisteredDate, INTERVAL n.Number MONTH) PeriodStart,
DATE_ADD(RegisteredDate, INTERVAL n.Number + 1 MONTH) PeriodEnd
FROM User u
CROSS JOIN Numbers n;
This gives a table like:
ID PERIODSTART PERIODEND
1 2012-12-16 2012-12-16
2 2013-01-29 2013-01-29
1 2013-01-16 2013-01-16
2 2013-02-28 2013-02-28
Example on SQL Fiddle
You then need to join this to your main table, and do the count:
SELECT u.ID,
u.PeriodStart,
DATE_ADD(PeriodEnd, INTERVAL -1 DAY) PeriodEnd,
COUNT(*) AS `COUNT`
FROM ( SELECT u.ID,
DATE_ADD(RegisteredDate, INTERVAL n.Number MONTH) PeriodStart,
DATE_ADD(RegisteredDate, INTERVAL n.Number + 1 MONTH) PeriodEnd
FROM User u
CROSS JOIN Numbers n
) u
INNER JOIN T
ON T.ID = u.ID
AND T.Date >= u.PeriodStart
AND T.Date < PeriodEnd
GROUP BY u.ID, u.PeriodStart, u.PeriodEnd;
Giving a final result of:
ID PERIODSTART PERIODEND COUNT
1 2012-12-16 2013-01-15 1
1 2013-01-16 2013-02-15 3
2 2013-02-28 2013-03-28 3
2 2013-03-29 2013-04-28 2
Full Example on SQL-Fiddle
You can obviously concatenate your period start and end dates to make a 'range' string, but this is probably best handled in your application layer.
EDIT
This can be achieved with no subqueries which is likely to perform better:
SELECT u.ID,
DATE_ADD(u.RegisteredDate, INTERVAL n.Number MONTH) PeriodStart,
DATE_ADD(DATE_ADD(u.RegisteredDate, INTERVAL n.Number + 1 MONTH), INTERVAL -1 DAY) PeriodEnd,
COUNT(*) AS `COUNT`
FROM User u
CROSS JOIN Numbers n
INNER JOIN T
ON T.ID = u.ID
AND T.Date >= DATE_ADD(u.RegisteredDate, INTERVAL n.Number MONTH)
AND T.Date < DATE_ADD(u.RegisteredDate, INTERVAL n.Number + 1 MONTH)
GROUP BY u.ID, u.RegisteredDate, n.Number;
Example with no subquery on SQL-Fiddle
EDIT 2
This will get you all periods for all users up until the current period (i.e. where today falls within the date range)
SELECT u.ID,
DATE_ADD(u.RegisteredDate, INTERVAL n.Number MONTH) PeriodStart,
DATE_ADD(DATE_ADD(u.RegisteredDate, INTERVAL n.Number + 1 MONTH), INTERVAL -1 DAY) PeriodEnd,
COUNT(T.ID) AS `COUNT`
FROM User u
CROSS JOIN Numbers n
LEFT JOIN T
ON T.ID = u.ID
AND T.Date >= DATE_ADD(u.RegisteredDate, INTERVAL n.Number MONTH)
AND T.Date < DATE_ADD(u.RegisteredDate, INTERVAL n.Number + 1 MONTH)
WHERE DATE_ADD(u.RegisteredDate, INTERVAL n.Number + 1 MONTH) <= CURRENT_TIMESTAMP
GROUP BY u.ID, u.RegisteredDate, n.Number;
Example on SQL Fiddle