Mysql: grouping by day and hours of day - mysql

I have a database, and I've been trying for a while, but I'm not getting the results I want. Here's a sample of what I have:
+---------+---------------------+
| Ammount | Date |
+---------+---------------------+
| 1 | 2015-08-25 14:07:00 |
| 1 | 2015-08-25 14:12:00 |
| 1 | 2015-08-25 15:17:00 |
| 2 | 2015-08-25 15:22:00 |
| 1 | 2015-08-25 14:27:00 |
| 6 | 2015-08-25 14:32:00 |
| 1 | 2015-08-26 14:37:00 |
| 5 | 2015-08-26 14:42:00 |
| 1 | 2015-08-26 16:47:00 |
| 2 | 2015-08-26 16:52:00 |
+---------+---------------------+
And this is my query:
select Ammount, Date from table;
What I want to do is group by the day AND the hours of each day, and sum it, pretty much like this:
select sum(Ammount), Date from table group by hour(day(Date));
Except it groups everything together.

SELECT SUM(Ammount), Date
FROM TABLE
GROUP BY YEAR(Date),
MONTH(Date),
DAY(Date),
HOUR(Date);

select sum(Ammount), Date from table group by month(Date), day(Date), hour(Date);

Related

MySQL Merge two queries based on mutual column

I have two queries that retrieve records from 2 different tables that are almost alike and I need to merge them together.
Both have created_date which is of type datetime and I'm casting this column to date because I want to group and order them by date only, I don't need the time.
First query:
select cast(created_date as date) the_date, count(*)
from question
where user_id = 2
group by the_date
order by the_date;
+------------+----------+
| the_date | count(*) |
+------------+----------+
| 2021-01-02 | 1 |
| 2021-02-10 | 1 |
| 2021-02-14 | 5 | -- this line contains a mutual date
| 2021-03-16 | 1 |
| 2021-03-26 | 3 |
| 2021-03-27 | 23 |
| 2021-03-28 | 5 |
| 2021-03-29 | 1 |
+------------+----------+
Second query:
select cast(created_date as date) the_date, count(*)
from answer
where user_id = 2
group by the_date
order by the_date;
+------------+----------+
| the_date | count(*) |
+------------+----------+
| 2021-02-08 | 2 |
| 2021-02-14 | 1 | -- this line contains a mutual date
| 2021-04-05 | 5 |
| 2021-04-06 | 2 |
+------------+----------+
What I need is to merge them like this:
+------------+---------------+---------------+
| the_date | count(query1) | count(query2) |
+------------+---------------+---------------+
| 2021-01-02 | 1 | 0 | -- count(query2) is 0 bc. it's not in the second query
| 2021-02-08 | 0 | 2 | -- count(query1) is 0 bc. it's not in the first query
| 2021-02-10 | 1 | 0 |
| 2021-02-14 | 5 | 1 | -- mutual date
| 2021-03-16 | 1 | 0 |
| 2021-03-26 | 3 | 0 |
| 2021-03-27 | 23 | 0 |
| 2021-03-28 | 5 | 0 |
| 2021-03-29 | 1 | 0 |
| 2021-04-05 | 0 | 5 |
| 2021-04-06 | 0 | 2 |
+------------+---------------+---------------+
Basically what I need is to have all dates together and for each date to have the corresponding values from those two queries.
try something like this.
SELECT the_date , max(cnt1) , max(cnt2)
FROM (
select cast(created_date as date) the_date, count(*) AS cnt1 , 0 as cnt2
from question
where user_id = 2
group by the_date
order by the_date
UNION ALL
select cast(created_date as date) the_date, 0, count(*)
from answer
where user_id = 2
group by the_date
order by the_date
) as t1
GROUP BY the_date
ORDeR BY the_date;

Group subscription duration by month and year in mysql

I have a table of subscriptions, storing user id, subscription end date, program id. One user can be subscribed to many programs, but for the scope of the problem the latest date is considered as the end date of the subscription. The goal is to find the number of users whose subscription is ending for each month of each year. To illustrate it:
-------------------------------------------
| user_id | program_id | end_date |
-------------------------------------------
| 1 | 1 | 2015-12-10 |
| 1 | 2 | 2017-08-27 |
| 2 | 1 | 2017-09-20 |
| 3 | 2 | 2017-10-01 |
| 2 | 3 | 2017-09-18 |
| 5 | 12 | 2017-10-22 |
| 4 | 3 | 2017-10-10 |
| 3 | 8 | 2018-11-15 |
-------------------------------------------
Intermediate result show when will the subscription end for each user (only month is needed):
------------------------------
| user_id | enddate |
------------------------------
| 1 | 2017-08 |
| 2 | 2017-09 |
| 3 | 2018-11 |
| 4 | 2017-10 |
| 5 | 2017-10 |
------------------------------
This was achieved with the query:
Select user_id, DATE_FORMAT(max(end_date), '%Y-%m') AS as enddate
From subscription
Group by user_id
Order by end_date desc;
The final result must further filter the list, showing only how many users will be left with no subscription in each month, like this:
------------------------------
| count | month, year |
------------------------------
| 1 | 2017-08 |
| 1 | 2017-09 |
| 2 | 2017-10 |
| 1 | 2018-11 |
------------------------------
This is where I am stuck with no mysql ideas. Iterating through the results and counting is out of the question.
You could try arranging the results by the enddate, like this:
select count(user_id), DATE_FORMAT(max_end_date, '%Y-%m')as enddate
from (
select user_id, max(end_date) as max_end_date
From subscription
Group by user_id
) n
group by enddate
Order by enddate desc;
Try this -
Select COUNT(*), DATE_FORMAT(MAX(end_date), '%Y-%m') AS as enddate
From subscription
Group by user_id
Order by end_date desc;

MySQL - select average of column A for first N entries from column B

I have a ratings table, where each user can add one rating a day. But each user might miss several days between ratings.
I'd like to get the average rating for each user_id's first 7 entries of created_at.
My table:
mysql> desc entries;
+------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| rating | tinyint(4) | NO | | NULL | |
| user_id | int(10) unsigned | NO | MUL | NULL | |
| created_at | timestamp | YES | | NULL | |
+------------+------------------+------+-----+---------+----------------+
Ideally I'd just get something like:
+------------+------------------+
| day | average_rating |
+------------+------------------+
| 1 | 2.53 |
+------------+------------------+
| 2 | 4.30 |
+------------+------------------+
| 3 | 3.67 |
+------------+------------------+
| 4 | 5.50 |
+------------+------------------+
| 5 | 7.23 |
+------------+------------------+
| 6 | 6.98 |
+------------+------------------+
| 7 | 7.22 |
+------------+------------------+
The closest I've been able to get is:
SELECT rating, user_id, created_at FROM entries ORDER BY user_id asc, created at desc
Which isn't very close at all...
Is it even possible? Will the performance be terrible? It's something that would need to run every time a web page is loaded, so would it be better to just run this once a day and save the results? (to another table!?)
edit - second attempt
Working towards a solution, I think this would get the rating for each user's first day:
select rating from entries where user_id in
(select user_id from entries order by created_at limit 1);
But I get:
ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
So now I'm going to play around with JOIN to see if that helps.
edit - third attempt, getting closer
I found this stackoverflow post, which is closer to what I want.
select e1.* from entries e1 left join entries e2
on (e1.user_id = e2.user_id and e1.created_at > e2.created_at)
where e2.id is null;
It gets the rating for the first day for each user.
Next step is to work out how to get days 2 to 7. I can't use 1.created_at > e2.created_at for that, so I'm really confused now.
edit - fourth attempt
Okay, I think it's not possible. Once I worked out how to turn off 'full group by' mode, I realised I'll probably need to use a subquery with limit <user_id>, <day_num>, for which I get:
ERROR 1235 (42000): This version of MySQL doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'
My current method is to just get the entire table, and use PHP to calculate the average for each day.
If I understand correctly you want to take the last 7 ratings the user gave, ordered by the date they gave the rating. The last 7 ratings of one user may fall on different days to another user, however they will be averaged together regardless of date.
First we need to order the data by user and date and give each user their own incrementing row count. I do this by adding two variables, one for the last user id and one for the row number:
select e.created_at,
e.rating,
if(#lastUser=user_id,#row := #row+1, #row:=1) as row,
#lastUser:= e.user_id as user_id
from entries e,
( select #row := 0, #lastUser := 0 ) vars
order by e.user_id asc,
e.created_at desc;
If the previous user_id is different we reset the row counter to 1. The result from this is:
+---------------------+--------+------+---------+
| created_at | rating | row | user_id |
+---------------------+--------+------+---------+
| 2017-01-10 00:00:00 | 1 | 1 | 1 |
| 2017-01-09 00:00:00 | 1 | 2 | 1 |
| 2017-01-08 00:00:00 | 1 | 3 | 1 |
| 2017-01-07 00:00:00 | 1 | 4 | 1 |
| 2017-01-06 00:00:00 | 1 | 5 | 1 |
| 2017-01-05 00:00:00 | 1 | 6 | 1 |
| 2017-01-04 00:00:00 | 1 | 7 | 1 |
| 2017-01-03 00:00:00 | 1 | 8 | 1 |
| 2017-01-02 00:00:00 | 1 | 9 | 1 |
| 2017-01-01 00:00:00 | 1 | 10 | 1 |
| 2017-01-13 00:00:00 | 1 | 1 | 2 |
| 2017-01-11 00:00:00 | 1 | 2 | 2 |
| 2017-01-09 00:00:00 | 1 | 3 | 2 |
| 2017-01-07 00:00:00 | 1 | 4 | 2 |
| 2017-01-05 00:00:00 | 1 | 5 | 2 |
| 2017-01-03 00:00:00 | 1 | 6 | 2 |
| 2017-01-01 00:00:00 | 1 | 7 | 2 |
| 2017-01-13 00:00:00 | 1 | 1 | 3 |
| 2017-01-01 00:00:00 | 1 | 2 | 3 |
| 2017-01-03 00:00:00 | 1 | 1 | 4 |
| 2017-01-01 00:00:00 | 1 | 2 | 4 |
| 2017-01-02 00:00:00 | 1 | 1 | 5 |
+---------------------+--------+------+---------+
We now simply wrap this in another statement to select the avg where the row number is less than or equal to seven.
select e1.row day, avg(e1.rating) avg
from (
select e.created_at,
e.rating,
if(#lastUser=user_id,#row := #row+1, #row:=1) as row,
#lastUser:= e.user_id as user_id
from entries e,
( select #row := 0, #lastUser := 0 ) vars
order by e.user_id asc,
e.created_at desc) e1
where e1.row <=7
group by e1.row;
This outputs:
+------+--------+
| day | avg |
+------+--------+
| 1 | 1.0000 |
| 2 | 1.0000 |
| 3 | 1.0000 |
| 4 | 1.0000 |
| 5 | 1.0000 |
| 6 | 1.0000 |
| 7 | 1.0000 |
+------+--------+

MySQL select a row from a daterange excluding the year

I'm trying to create a MySQL query to select the daily price from a table that is between a date range from another. I only want to use 'starting-ending' months and days from the table "seasons" and I want to pass the year dynamically to the query.
This is my query: (I'm giving it the Year to exclude the one on the table)
SELECT a.season, b.base_price
FROM seasons a
JOIN pricebyseason b ON a.id=b.season_id
WHERE b.prop_id='6' AND '2015-11-29' BETWEEN DATE_FORMAT(a.starting,'2015-%m-%d') AND DATE_FORMAT(a.ending,'2016-%m-%d')
ORDER BY b.base_price DESC
It works but not with all dates.
These are the tables:
seasons (these are static date values)
+----+--------------+------------+------------+
| id | season | starting | ending |
+----+--------------+------------+------------+
| 1 | Peak Season | 2015-12-11 | 2016-01-09 |
| 2 | High Season | 2015-11-27 | 2016-04-15 |
| 3 | Mid Season | 2015-04-16 | 2015-09-01 |
| 4 | Low Season | 2015-09-02 | 2015-11-26 |
| 5 | Spring Break | 2015-03-05 | 2015-03-21 |
+----+--------------+------------+------------+
pricebyseason
+----+---------+-----------+------------+
| id | prop_id | season_id | base_price |
+----+---------+-----------+------------+
| 1 | 6 | 1 | 950 |
| 2 | 6 | 2 | 750 |
| 3 | 6 | 3 | 450 |
| 4 | 6 | 4 | 400 |
| 5 | 6 | 5 | 760 |
+----+---------+-----------+------------+
What I want to achive is query the dialy price between checkin, checkout selection
I create this sqlfiddle: http://sqlfiddle.com/#!9/4a6f4
This is a previuos query that is not working either:
SELECT a.base_price,b.season,b.starting,b.ending
FROM pricebyseason a JOIN seasons b ON a.season_id=b.id
WHERE a.prop_id='6' AND
(DATE_FORMAT(b.starting,'%m-%d') <= '12-27' OR DATE_FORMAT(b.starting,'2016-%m-%d') >= '2015-12-27')
AND
(DATE_FORMAT(b.ending,'%m-%d') >= '12-27' OR DATE_FORMAT(b.ending,'2016-%m-%d') <= '2015-12-27')
ORDER BY base_price DESC
And here are some sample dates for each season: '2016-01-08','2015-12-27','2016-04-14','2015-11-29','2016-04-15','2015-09-01','2016-09-02','2015-11-26','2016-10-10','2016-03-18','2016-06-22','2015-06-15'
Thank a lot

MySQL Joining multiple select satement containing multiple values for View

I have a Table having all IN detail
+----------+------------+-----------+
| staff_id | date | time |
+----------+------------+-----------+
| 1 | 2015-02-20 | 07:00 |
| 2 | 2015-02-20 | 07:01 |
| 3 | 2015-02-20 | 07:05 |
| 1 | 2015-02-20 | 07:02 |
| 1 | 2015-02-20 | 07:04 |
+----------+--------------+---------+
another Table having all OUT detail
+----------+------------+-----------+
| staff_id | date | time |
+----------+------------+-----------+
| 1 | 2015-02-20 | 13:00 |
| 2 | 2015-02-20 | 13:45 |
| 3 | 2015-02-20 | 13:45 |
| 1 | 2015-02-20 | 13:47 |
| 1 | 2015-02-20 | 13:48 |
+----------+--------------+---------+
What required result is
Time In, min val and Time Out, max value
+----------+------------+-----------+
| staff_id | date | time IN | Time Out
+----------+------------+-----------+
| 1 | 2015-02-20 | 07:00 | 13:48
| 2 | 2015-02-20 | 07:01 | 13:45
What Im doing is
SELECT *
FROM
(SELECT sai.staff_id AS staff_id_in,
sai.date AS date_in,
sai.time AS time_in,
sai.ip4 AS ip4_in,
sai.location_id AS location_id_in,
'1' AS atd_in
FROM staff_attendance_in sai
ORDER BY staff_id ASC, time ASC) AS sub
GROUP BY staff_id_in,
date_in
UNION
SELECT *
FROM
(SELECT sao.staff_id AS staff_id_out,
sao.date AS date_out,
sao.time AS time_out,
sao.ip4 AS ip4_out,
sao.location_id AS location_id_out,
'2' AS atd_out
FROM staff_attendance_out sao
ORDER BY time DESC) AS sub
GROUP BY staff_id_out,
date_out
but I am not able to generate view from the query... neither join
Try this:-
SELECT I.staff_id, I.date, MIN(I.time_IN), MAX(O.Time Out)
FROM IN I JOIN OUT O
ON I.staff_id = O.staff_id
GROUP BY I.staff_id, I.date;
I think this can help you.