Artificially duplicate results in mysql - mysql

I have a table like so
+---------+
| weekday |
+---------+
| 2 |
| 5 |
| 3 |
+---------+
Now I want to have a resultset in which I see the dates of this and the upcoming week like so:
+------------+---------+
| date | weekday |
+------------+---------+
| 2019-12-18 | 2 |
| 2019-12-21 | 5 |
| 2019-12-19 | 3 |
| 2019-12-25 | 2 |
| 2019-12-28 | 5 |
| 2019-12-26 | 3 |
+------------+---------+
So far I have this query
SELECT
CURDATE() + INTERVAL w.weekday - WEEKDAY(CURDATE()) DAY AS thisWeek,
CURDATE() + INTERVAL w.weekday + 7 - WEEKDAY(CURDATE()) DAY AS nextWeek,
dw.weekday
FROM
weekdays AS w
Which gives me this result
+------------+------------+---------+
| thisWeek | nextWeek | weekday |
+------------+------------+---------+
| 2019-12-18 | 2019-12-25 | 2 |
| 2019-12-21 | 2019-12-28 | 5 |
| 2019-12-19 | 2019-12-26 | 3 |
+------------+------------+---------+
How would I have to need to proceed to get the former resultset?

Use UNION ALL.
SELECT
CURDATE() + INTERVAL w.weekday - WEEKDAY(CURDATE()) DAY AS date
dw.weekday
FROM
weekdays AS w
UNION ALL
SELECT
CURDATE() + INTERVAL w.weekday + 7 - WEEKDAY(CURDATE()) DAY AS date
dw.weekday
FROM
weekdays AS w;

You could try using UNION
select thisWeek date, weekday
from (
SELECT
CURDATE() + INTERVAL w.weekday - WEEKDAY(CURDATE()) DAY AS thisWeek,
CURDATE() + INTERVAL w.weekday + 7 - WEEKDAY(CURDATE()) DAY AS nextWeek,
dw.weekday
FROM weekdays AS w
) t1
union all
select nextWeek date, weekday
from (
SELECT
CURDATE() + INTERVAL w.weekday - WEEKDAY(CURDATE()) DAY AS thisWeek,
CURDATE() + INTERVAL w.weekday + 7 - WEEKDAY(CURDATE()) DAY AS nextWeek,
dw.weekday
FROM weekdays AS w
) t2
or for avoid subquery you could create a view
create view my_view as
SELECT
CURDATE() + INTERVAL w.weekday - WEEKDAY(CURDATE()) DAY AS thisWeek,
CURDATE() + INTERVAL w.weekday + 7 - WEEKDAY(CURDATE()) DAY AS nextWeek,
dw.weekday
FROM weekdays
then the query is
select thisWeek date, weekday
from my_view
union all
select nextWeek date, weekday
from my_view

Related

How to select count today and tomorrow data less than specific time group by day?

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)

Find all fridays with date in the year 2017

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

MySQL - make a weekly report that includes starting day (twist: week starting wednesday)

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

MySQL select data from last week

I was wondering how to select all the data from last week? Between Monday to Friday, assuming that today is a Monday. I actually don't have a datetime format column.
Here's what I tried so far:
SELECT * FROM logs
WHERE WEEKDAY(CONCAT(year,'-',month,'-',day)) BETWEEN 0 AND 4
AND YEARWEEK( date_added2 ) = YEARWEEK( CURRENT_DATE( ) - INTERVAL 7 DAY) AND deleted='n';
And this is my table, table logs:
Could you try this? you can test here http://www.sqlfiddle.com/#!2/3989b1/3/0
SELECT *
FROM logs,
(SELECT (CURRENT_DATE() - INTERVAL (WEEKDAY(CURRENT_DATE()) + 1 ) % 7 DAY) AS sun_day) t1
WHERE
logs.date_added2 BETWEEN t1.sun_day - INTERVAL 6 DAY
AND t1.sun_day - INTERVAL 2 DAY;
How it works
folling query returns sunday of week
SELECT '2014-01-09' - INTERVAL (WEEKDAY('2014-01-09') + 1 ) % 7 DAY;
+--------------------------------------------------------------+
| '2014-01-09' - INTERVAL (WEEKDAY('2014-01-09') + 1 ) % 7 DAY |
+--------------------------------------------------------------+
| 2014-01-05 |
+--------------------------------------------------------------+
SELECT '2014-01-10' - INTERVAL (WEEKDAY('2014-01-10') + 1 ) % 7 DAY;
+--------------------------------------------------------------+
| '2014-01-10' - INTERVAL (WEEKDAY('2014-01-10') + 1 ) % 7 DAY |
+--------------------------------------------------------------+
| 2014-01-05 |
+--------------------------------------------------------------+
so, t1.sun_day - INTERVAL 6 holds monday of previous week, and t1.sun_day - INTERVAL 2 DAY for friday of previous week.
mysql> SELECT #sunday := (CURRENT_DATE() - INTERVAL (WEEKDAY(CURRENT_DATE()) + 1 ) % 7 DAY) AS sunday;
+------------+
| sunday |
+------------+
| 2014-01-05 |
+------------+
mysql> SELECT #sunday - INTERVAL 6 DAY, #sunday - INTERVAL 2 DAY;
+--------------------------+---------------------------+
| #sunday - INTERVAL 6 DAY | #sunday - INTERVAL 2 DAY |
+--------------------------+---------------------------+
| 2013-12-30 | 2014-01-03 |
+--------------------------+---------------------------+
So, logs.date_added2 BETWEEN t1.sun_day - INTERVAL 6 DAY AND t1.sun_day - INTERVAL 2 DAY would find logs from monday to friday.

mysql group by month with custom starting day

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