Group by date range on weeks/months interval - mysql

I'm using MySQL and I have the following table:
| clicks | int |
| period | date |
I want to be able to generate reports like this, where periods are done in the last 4 weeks:
| period | clicks |
| 1/7 - 7/5 | 1000 |
| 25/6 - 31/7 | .... |
| 18/6 - 24/6 | .... |
| 12/6 - 18/6 | .... |
or in the last 3 months:
| period | clicks |
| July | .... |
| June | .... |
| April | .... |
Any ideas how to make select queries that can generate the equivalent date range and clicks count?

SELECT
WEEKOFYEAR(`date`) AS period,
SUM(clicks) AS clicks
FROM `tablename`
WHERE `date` >= CURDATE() - INTERVAL 4 WEEK
GROUP BY period
SELECT
MONTH(`date`) AS period,
SUM(clicks) AS clicks
FROM `tablename`
WHERE `date` >= CURDATE() - INTERVAL 3 MONTH
GROUP BY period

For the last 3 months you can use:
SELECT MONTH(PERIOD), SUM(CLICKS)
FROM TABLE
WHERE PERIOD >= NOW() - INTERVAL 3 MONTH
GROUP BY MONTH(PERIOD)
or for the last 4 weeks:
SELECT WEEK(PERIOD), SUM(CLICKS)
FROM TABLE
WHERE PERIOD >= NOW() - INTERVAL 4 WEEK
GROUP BY WEEK(PERIOD)
Code not tested.

Related

MySql select 1 week & 1 Month advance date from Current date

i want to get the list of date which is going to expire in a week or a month...
MySQL, and field is TIMESTAMP.
i tried below queries...
SELECT CERT_ID, date(CERTVALIDITY)
FROM mst_CERT WHERE date(CERTVALIDITY) > now() - interval 1 week ORDER BY CERTVALIDITY;
This returns all the dates from current date
SELECT date(CERTVALIDITY) from mst_CERT where curdate() =
DATE(DATE_SUB(CERTVALIDITY, INTERVAL 1 week));
returns nothing...
SELECT date(DATE_SUB(CERTVALIDITY, INTERVAL 1 MONTH)) from mst_CERT;
returns nothing...
| CT00078 | 2020-02-28 |
| CT00098 | 2020-02-29 |
| CT00011 | 2020-03-31 |
| CT00012 | 2020-03-31 |
| CT00013 | 2020-03-31 |
| CT00014 | 2020-03-31 |
| CT00075 | 2020-03-31 |
| CT00055 | 2020-03-31 |
| CT00056 | 2021-03-31 |
This will show CERT_ID's outside of a 7 day period.
SELECT CERT_ID, date(CERTVALIDITY)
FROM mst_CERT
WHERE date(CERTVALIDITY) > (NOW() + INTERVAL 7 day)
ORDER BY date(CERTVALIDITY);
This will show you valid CERT_ID's within a 7 day period.
SELECT CERT_ID, date(CERTVALIDITY)
FROM mst_CERT
WHERE date(CERTVALIDITY) BETWEEN NOW() AND (NOW() + INTERVAL 7 day)
ORDER BY date(CERTVALIDITY);
Your first query is almost right. You made a mistake of subtracting interval of 1 week but you actually had to add interval.
SELECT CERT_ID, DATE(CERTVALIDITY)
FROM ms_CERT
WHERE DATE(CERTVALIDITY) < NOW() + INTERVAL 1 WEEK AND DATE(CERTVALIDITY) > NOW()
ORDER BY CERTVALIDITY;
Above Query will return following result
| CT00078 | 2020-02-28 |
| CT00098 | 2020-02-29 |

select item from table and order by amount on 2 days?

For example I have below table:
---------------------
| amount | date |
---------------------
| 50 | Day 1 |
| 60 | day 2 |
| 20 | day 3 |
| 25 | day 3 |
| 23 | day 4 |
| 26 | day 4 |
| 15 | day 5 |
---------------------
What I basically want to do is to retrieve item from last 2 days and order the row by max amount. so the result would be like:
---------------------
| amount | date |
---------------------
| 26 | day 4 |
| 23 | day 4 |
| 15 | day 5 |
---------------------
FYI: the date input is in 2018-06-10 14:37:44 mode above is just an example
I tried: SELECT AMOUNT FROM table WHERE AMOUNT=(SELECT MAX(AMOUNT) FROM table) ORDER BY DATE;
But the result I am getting is only | 60 | day 2 | which is the max amount and is not from 2 recent dates;
You could use the DATE_ADD function that gives you a date after a certain time alongside CURDATE() that gives you current date;
SELECT * FROM table WHERE date >= DATE_ADD(CURDATE(), INTERVAL -2 DAY)
ORDER BY amount DESC;
Try this:
SELECT amount, date FROM table WHERE date >= DATE_ADD(CURDATE(), INTERVAL -2 DAY) ORDER BY amount DESC;
-- date > DATE_ADD(CURDATE(), INTERVAL -2 DAY) will give you all rows within 2 days
-- ORDER BY amount DESC will give you ordered by amount descending

Select all records that were created on a given day N months ago

Given a table orders:
+-----+---------+-------------------------+
| id | price | created_at |
+-----+---------+-------------------------+
| 1 | 16.50 | 2017-02-28 12:52:00.824 |
| 2 | 22.00 | 2017-10-03 15:12:39.107 |
| 3 | 50.00 | 2017-12-03 12:54:42.658 |
| 4 | 12.00 | 2018-01-02 07:21:47.808 |
| . | . | . |
| . | . | . |
| . | . | . |
+-----+---------+-------------------------+
and current date:
+---------------------+
| NOW() |
+---------------------+
| 2018-01-03 10:33:14 |
+---------------------+
I'd like to select all records that were created on current day any months ago. So for above data my query should return:
+-----+---------+-------------------------+
| id | price | created_at |
+-----+---------+-------------------------+
| 2 | 22.00 | 2017-10-03 15:12:39.107 |
| 3 | 50.00 | 2017-12-03 12:54:42.658 |
+-----+---------+-------------------------+
But there are some edge cases for the last day of month:
if it's 31-days month, it's trivial
if it's 30-days month, the query should return all records created on 30th and 31st day of month
if it's February in a leap year, the query should return all records created on 29th, 30th and 31st day of month
if it's February in a normal year, the query should return all records created on 28th, 29th, 30th and 31st day of month
What I have tried is something like this:
SELECT * FROM orders
JOIN (
SELECT id, PERIOD_DIFF(
DATE_FORMAT(NOW(), "%Y%m"),
DATE_FORMAT(created_at, "%Y%m")
) AS diff
FROM orders
) AS periods
ON orders.id = periods.id
WHERE DATEDIFF(created_at + INTERVAL diff MONTH, NOW()) = 0;
But it doesn't cover the edge cases and I believe there is a smarter way (maybe without a subquery) to achieve the expected results.
EDIT:
To give you more context - what I need is a kind of a loop. I have a cron job scheduled to run once a day at midnight. This job should select all ids of orders that were created on this day any months ago and then refresh some other data associated with those ids. The important part is to refresh this data exactly once every month - that's why the last day of months is so crucial.
For example, given following creation dates:
DATES = [
2015-05-30, 2016-02-29, 2016-10-03,
2016-12-31, 2017-05-28, 2018-01-03
]
+---------------+------------------------------------+
| NOW() | SHOULD BE INCLUDED |
+---------------+------------------------------------+
| 2018-01-03 | 2016-10-03, 2018-01-03 |
| 2018-02-28 | 2016-02-29, 2016-12-31, 2017-05-28 |
| 2018-04-30 | 2015-05-30, 2016-12-31 |
| 2018-10-31 | 2016-12-31 |
+---------------+------------------------------------+
Can I suggest a slight simplification Walerian?
SELECT
*
FROM
orders
WHERE
(
DAYOFMONTH(created_at) = DAYOFMONTH( NOW() ) --monthdays that match
)
OR
(
( DAYOFMONTH( LAST_DAY( NOW() ) ) = DAYOFMONTH( NOW() ) ) --base date is end of month
AND
( DAYOFMONTH(created_at) > DAYOFMONTH( NOW() ) ) --target monthdays are beyond base monthday
)
Incidentally I don't have a MySQL environment, so I'm just taking it on trust that these are the correct functions in MySQL.
Use DAYOFMONTH() function to compare the day of the NOW() and the created_at.
Like this:
SELECT * FROM ORDERS
WHERE (DAYOFMONTH(NOW() < LAST_DAY(NOW()) -- if not last day of month
AND DAYOFMONTH(created_at) = DAYOFMONTH(NOW())
OR (LAST_DAY(NOW()) = DAYOFMONTH(NOW()) -- if last day of month
AND DAYOFMONTH(NOW()) BETWEEN DAYOFMONTH(created_at) AND LAST_DAY(created_at)) --
Inspired by suresubs's answer, I figured out how the query should look like.
SELECT * FROM orders
-- (1)
WHERE (DAYOFMONTH(NOW) < DAYOFMONTH(LAST_DAY(NOW()))
AND DAYOFMONTH(created_at) = DAYOFMONTH(NOW()))
-- (2)
OR (DAYOFMONTH(LAST_DAY(NOW())) = DAYOFMONTH(NOW())
AND DAYOFMONTH(created_at) BETWEEN DAYOFMONTH(NOW()) AND DAYOFMONTH(LAST_DAY(created_at)));
It's using DAYOFMONTH() and LAST_DAY() functions and it's divided into two cases:
Today is not the last day of current month.
Today is the last day of current month.

Get sales per hour

I want to get the number of sales per hour in a specific date:
table : invoices
+-------+-----------+-----------+--------------------------+
| id | name | amout | date |
+-------+-----------+-----------+--------------------------+
| 1 | John | 12313 | 2017-05-20 13:50:08 |
| 2 | Mary | 5335 | 2017-05-17 22:21:35 |
| 3 | Jeff | 23 | 2017-05-17 22:32:13 |
| 4 | Bill | 132 | 2017-05-17 23:25:55 |
| 5 | Bob | 853 | 2017-05-17 24:52:37 |
+-------+-----------+-----------+--------------------------+
So, I want to get this output:
9 a.m. to 10 a.m. we generated X invoices.
10 a.m. to 11 a.m. we generated Y invoices.
11 a.m. to 12 a.m. we generated Z invoices.
X,Y and Z it's the number of invoices generated in these interval.
How I can do that? I use MySQL.
Thanks!
Three SQL concepts will help you get an answer to this question.
"Truncating" a datestamp; that is, extracting the hour from it. For example, 2017-05-27 14:37.20 is truncated to 2017-05-27 14:00:00.
Selecting rows from your table with datestamps on a particular day.
Using COUNT(*) and GROUP BY.
You can truncate like this.
SELECT DATE_FORMAT(`date`, '%y-%m-%d %H:00:00') hour_starting
FROM invoices
You can add to that query to select rows for a particular date like this
SELECT DATE_FORMAT(`date`, '%y-%m-%d %H:00:00') hour_starting
FROM invoices
WHERE `date` >= '2017-05-27`
AND `date` < '2017-05-27' + INTERVAL 1 DAY
Notice that this chooses all rows with date values on or after (>=) midnight on 2017-05-27. and before but not including (<) midnight on the next day.
Finally, you can use COUNT like this.
SELECT DATE_FORMAT(`date`, '%y-%m-%d %H:00:00') hour_starting,
COUNT(*) invoice_count
FROM invoices
WHERE `date` >= '2017-05-27`
AND `date` < '2017-05-27' + INTERVAL 1 DAY
GROUP BY DATE_FORMAT(`date`, '%y-%m-%d %H:00:00')

Format grouped date as readable date

So I have a query that correctly displays the number of registrations for the last 12 months. Here is display:
Registrations per month for last 2 years
1--17
2--12
3--17
4--8
5--9
6--8
7--15
8--20
9--12
10--14
11--13
12--14
But since im running this in say June, the last mont I need to say the readable date May and not '1'. I want instead:
May--17
Apr--12
March--17
.
.
.
Here is my current MYSQL:
SELECT MONTH(create_date) as month , COUNT(create_date) as count
FROM `users`
WHERE create_date >= NOW() - INTERVAL 1 YEAR
GROUP BY MONTH(create_date)
I assumed I just have to use FORMAT_DATE() on the GROUP By as:
GROUP BY FORMAT_DATE(MONTH(create_date, '%M'))
And that would give me my readable month, but the sql statement reports it is not correct. Anyone know how to accomplish this?
Try this:
SELECT DATE_FORMAT(create_date, '%M') AS month, COUNT(create_date) AS count
FROM users
WHERE create_date >= NOW() - INTERVAL 1 YEAR
GROUP BY MONTH(create_date);
The result will be:
+-----------+-------+
| month | count |
+-----------+-------+
| January | 1 |
| February | 1 |
| March | 1 |
| April | 1 |
| May | 2 |
| June | 2 |
| July | 1 |
| August | 1 |
| September | 1 |
| November | 1 |
| December | 1 |
+-----------+-------+
You can use STR_TO_DATE() to convert the number to a date, and then back with MONTHNAME()
SELECT MONTHNAME(create_date(6, '%m')) as month , COUNT(create_date) as count
FROM `users`
WHERE create_date >= NOW() - INTERVAL 1 YEAR
GROUP BY MONTH(create_date)