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')
Related
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
Storage table
| id| product_id | date_add | date_remove
------------------------------------------------------------------
| 1 | 10 |2018-04-02 08:28:43 | 2018-04-03 07:21:08
| 2 | 10 |2018-04-05 08:28:43 | 2018-04-06 08:28:50
| 3 | 10 |2018-04-01 08:28:43 | 2018-04-05 08:28:50
| 4 | 12 |2018-04-01 08:28:43 | 2018-04-03 07:21:08
| 5 | 12 |2018-04-04 08:28:43 | 2018-04-04 10:28:43
| 6 | 13 |2018-03-01 08:28:43 | 2018-03-01 10:28:43
how to find ?
how many days product was in the storage in period 2018-04-01 to 2018-04-05?
find result
| product_id | days
| 10 | 5
| 12 | 3
try
SELECT product_id, SUM(DATEDIFF(date_remove, date_add)) as days
FROM storage
where date_remove BETWEEN '2018-04-01 00:00:00'
AND '2018-04-05 23:59:59'
AND date_add BETWEEN '2018-04-01 00:00:00'
AND '2018-04-05 23:59:59'
GROUP BY product_id
but result wrong because 'SUM' sums all days
get result
| product_id | days
| 10 | 7
correct result
| product_id | days
| 10 | 5
upd
http://rextester.com/QFS96125
result 9,646805555556 but probably maximum 5 days and product_id 13 correct 0,436608796296 but result 0,87
First you want to look at all date ranges that are within or overlap with the range 2018-04-01 to 2018-04-05.
where date_add < date '2018-04-06' and date_remove >= date '2018-04-01'
Then, with the ranges found, you want to consider only their days in the range 2018-04-01 to 2018-04-05.
greatest(date_add, date '2018-04-01')
least(date_remove, date '2018-04-06')
Then you want to count days. Here you need a rule. Do you want to look at single ranges and only take their full days which you add up then? Or do you want to consider day fractions, add all up and see how many full days result? For the latter you could get durations in seconds and add these up:
select
product_id,
sum(timestampdiff(second, greatest(date_add, date '2018-04-01'),
least(date_remove, date '2018-04-06'))
) / 60 / 60 / 24 as days
from storage
where date_add < date '2018-04-06' and date_remove >= date '2018-04-01'
group by product_id
order by product_id;
This gets you
product_id | days
-----------+---------------
10 | 5,599872685185
12 | 2,036400462963
Feel free to use FLOOR, CEIL or ROUND on the resulting days.
Rextester demo: http://rextester.com/XTVU47656
To obtain such result, try
SELECT SUM(DATEDIFF(date_remove, date_add)) as days
FROM table
GROUP BY product_id
Keep in mind that this will sum all the days for the same product_id. To get the result for each id, use:
SELECT id, product_id, DATEDIFF(date_remove, date_add) as days
FROM table
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.
I have a mysql database with which contains data in 5 minute bins. I'd like to create hourly average of the data starting on the half hour.
By using mysql built-in group by:
select date,AVG(AE) from mytable group by date(date),HOUR(date);
would compute average value from say, 01:00 to 02:00. Instead I would like hourly averages to be computed from 00:30 to 01:30, were the value would then be the hourly average at 01:00.
This query fail when a new day starts:
select date, AVG(AE) from mytable group by date(date), HOUR( date ) + FLOOR( MINUTE( date ) / 30 );
+---------------------+------------------+
| date | AVG(AE) |
+---------------------+------------------+
| 1997-01-01 22:30:00 | 23 |
| 1997-01-01 23:30:00 | 28.3 |
| 1997-01-02 00:00:00 | 20.1333333333333 |
| 1997-01-02 00:30:00 | 29.3 |
| 1997-01-02 01:30:00 | 27.5666666666667 |
| 1997-01-02 02:30:00 | 43.4166666666667 |
which is the closest I've gotten :-)
In another post ( https://stackoverflow.com/a/6560742/1142735 ) it was suggested that GROUP BY FLOOR(MOD((mytimestamp-1800)/3600)) would create intervals starting on the half hour if timestamp was used. I am using datetime.
Thanks
Paul
Anything that uses the DATE() function will fail to correctly group the interval 23:30 - 00:30.
Use:
FLOOR((UNIX_TIMESTAMP(date) - 1800) / 3600)
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.