MySQL count(*) each day for next 7 days - mysql

I have the following query which counts the number of bookings for a selected day
select count(*)
from isBooked inner join booking
on isbooked.BookingID = booking.bookingID
where '2015-08-09' between booking.startDate and booking.endDate;
I want to run this query for the next 7 days and display the count for each day, for example
day count
1 10
2 9
3 18
4 6
5 1
6 9
7 14

Basically it can be an UNION of 7 queries :
(
SELECT CURDATE() AS Date, COUNT(*) AS Available
FROM isBooked INNER JOIN booking
ON isbooked.BookingID = booking.bookingID
WHERE CURDATE() BETWEEN booking.startDate and booking.endDate;
)
UNION ALL
(
SELECT DATE_ADD(CURDATE(), INTERVAL 1 DAY) AS Date, COUNT(*) AS Available
FROM isBooked INNER JOIN booking
ON isbooked.BookingID = booking.bookingID
WHERE DATE_ADD(CURDATE(), INTERVAL 1 DAY) BETWEEN booking.startDate and booking.endDate;
)
UNION ALL
(
SELECT DATE_ADD(CURDATE(), INTERVAL 2 DAY) AS Date, COUNT(*) AS Available
FROM isBooked INNER JOIN booking
ON isbooked.BookingID = booking.bookingID
WHERE DATE_ADD(CURDATE(), INTERVAL 2 DAY) BETWEEN booking.startDate and booking.endDate;
)
UNION ALL
(
SELECT DATE_ADD(CURDATE(), INTERVAL 3 DAY) AS Date, COUNT(*) AS Available
FROM isBooked INNER JOIN booking
ON isbooked.BookingID = booking.bookingID
WHERE DATE_ADD(CURDATE(), INTERVAL 3 DAY) BETWEEN booking.startDate and booking.endDate;
)
UNION ALL
(
SELECT DATE_ADD(CURDATE(), INTERVAL 4 DAY) AS Date, COUNT(*) AS Available
FROM isBooked INNER JOIN booking
ON isbooked.BookingID = booking.bookingID
WHERE DATE_ADD(CURDATE(), INTERVAL 4 DAY) BETWEEN booking.startDate and booking.endDate;
)
UNION ALL
(
SELECT DATE_ADD(CURDATE(), INTERVAL 5 DAY) AS Date, COUNT(*) AS Available
FROM isBooked INNER JOIN booking
ON isbooked.BookingID = booking.bookingID
WHERE DATE_ADD(CURDATE(), INTERVAL 5 DAY) BETWEEN booking.startDate and booking.endDate;
)
UNION ALL
(
SELECT DATE_ADD(CURDATE(), INTERVAL 6 DAY) AS Date, COUNT(*) AS Available
FROM isBooked INNER JOIN booking
ON isbooked.BookingID = booking.bookingID
WHERE DATE_ADD(CURDATE(), INTERVAL 6 DAY) BETWEEN booking.startDate and booking.endDate;
)
But if you have a large amount of data to manage, you shoud consider processing it regularily on your database and putting it in some cache file or other table.
If you want to do this for a lot of days, an UNION of N SELECT's will not be efficient. In this case I would recommand defining a (temporary) table containing the days, and doing a sigle query with a JOIN on the dates, for example :
CREATE TEMPORARY TABLE dates (day DATE); -- not necessarily temporary
INSERT INTO dates (day) values ('2015-01-01'), ....
SELECT dates.day AS Date, COUNT(*) AS Available
FROM isBooked INNER JOIN booking
ON isbooked.BookingID = booking.bookingID
WHERE dates.day BETWEEN booking.startDate and booking.endDate
GROUP BY dates.day;

Related

How to select same column of a table twice as two different columns seperated date wise

i have the following mysql query , but its taking approx 5 minutes to execute, how can i decrease its execution time
select
revenue_center_group.description as rc_group,
revenue_center.description as revenue_center,
count(ytd.members) as total_ytd_members,
count(lytd.members) as total_lytd_members
from revenue_center
left join revenue_center_group on revenue_center_group.id = revenue_center.revenue_center_group_id
left join (
select membership.id as members,
membership.rc_id as rc_id
from membership
where membership.status =1
and DATE(membership.join_date)>= (SELECT DATE_FORMAT((SELECT DATE_FORMAT(date(now()) - INTERVAL 1 MONTH, '%Y-%m-%d')) ,'%Y-01-01 00:00:00'))
and DATE(membership.join_date)<= (SELECT DATE_FORMAT(LAST_DAY(date(now()) - INTERVAL 1 MONTH), '%Y-%m-%d 23:59:59'))ytd on revenue_center.id = ytd.rc_id
left join (
select
membership.id as members,
membership.rc_id as rc_id
from membership
where membership.status =1
and DATE(membership.join_date)>= (SELECT DATE_FORMAT((SELECT DATE_FORMAT((SELECT DATE_FORMAT(date(now()) - INTERVAL 1 MONTH, '%Y-%m-%d'))
- INTERVAL 1 YEAR, '%Y-%m-%d')) ,'%Y-01-01 00:00:00'))
and DATE(membership.join_date)<= (SELECT DATE_FORMAT((select DATE_FORMAT(LAST_DAY(date(now()) - INTERVAL 1 MONTH), '%Y-%m-%d')
- INTERVAL 1 YEAR),'%Y-%m-%d 23:59:59'))lytd on revenue_center.id = lytd.rc_id
group by revenue_center_group.description,revenue_center.description with rollup;

Unable to JOIN the same table twice based on two conditions

I'm using MySQL with PHP. Here is my query on which I'm getting the error.
$query =
"SELECT days.day, count(myDataTable.appId) as countf, count(myDataTable.appId) as counts
FROM
(
select curdate() as day
union select curdate() - interval 1 day
union select curdate() - interval 2 day
union select curdate() - interval 3 day
union select curdate() - interval 4 day
union select curdate() - interval 5 day
union select curdate() - interval 6 day
union select curdate() - interval 7 day
union select curdate() - interval 8 day
union select curdate() - interval 9 day
) days
left join myDataTable as n1
on days.day = n1.date AND n1.appId = '$id' AND n1.status = 'ERROR'
group by days.day
left join myDataTable as n2
on days.day = n2.date AND n2.appId = '$id' AND n2.status = 'SUCCESS'
group by days.day";
The error log is:
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'left join myDataTable on days.day = myDataTable.date AND myDataTable.appId ' at line 17
It helps to properly indent your SQL so you can spot the mistakes. Indenting by the main keywords (SELECT, FROM, WHERE, HAVING, GROUP BY, and ORDER BY) will help you spot them quickly:
SELECT
days.day,
count (myDataTable.appId) as countf,
count(myDataTable.appId) as counts
FROM
(
select curdate() as day
union select curdate() - interval 1 day
union select curdate() - interval 2 day
union select curdate() - interval 3 day
union select curdate() - interval 4 day
union select curdate() - interval 5 day
union select curdate() - interval 6 day
union select curdate() - interval 7 day
union select curdate() - interval 8 day
union select curdate() - interval 9 day
) days
left join myDataTable as n1
on days.day = n1.date AND n1.appId = '$id' AND n1.status = 'ERROR'
group by
days.day
left join myDataTable as n2
on days.day = n2.date AND n2.appId = '$id' AND n2.status = 'SUCCESS'
group by
days.day
You can see that you have two GROUP BY's which won't work. Furthermore you have a LEFT JOIN hanging out in the first GROUP BY clause, which doesn't work either. Removing that first GROUP BY will get you closer:
SELECT
days.day,
count (myDataTable.appId) as countf,
count(myDataTable.appId) as counts
FROM
(
select curdate() as day
union select curdate() - interval 1 day
union select curdate() - interval 2 day
union select curdate() - interval 3 day
union select curdate() - interval 4 day
union select curdate() - interval 5 day
union select curdate() - interval 6 day
union select curdate() - interval 7 day
union select curdate() - interval 8 day
union select curdate() - interval 9 day
) days
left join myDataTable as n1
on days.day = n1.date AND n1.appId = '$id' AND n1.status = 'ERROR'
left join myDataTable as n2
on days.day = n2.date AND n2.appId = '$id' AND n2.status = 'SUCCESS'
group by
days.day
No you have a proper FROM clause. This is the first part that your Database looks at so it knows from where it is getting it's data and how it joins together. Your table aliases are set here and then used EVERYWHERE else in the query. Which leads you to the second problem.
You reference myDataTable up in your SELECT clause, but by the time the database is looking at your SELECT myDataTable isn't in context. The aliases n1 and n2 are though, so change these to reference your table aliases:
SELECT
days.day,
count (n1.appId) as countf,
count(n2.appId) as counts
FROM
(
select curdate() as day
union select curdate() - interval 1 day
union select curdate() - interval 2 day
union select curdate() - interval 3 day
union select curdate() - interval 4 day
union select curdate() - interval 5 day
union select curdate() - interval 6 day
union select curdate() - interval 7 day
union select curdate() - interval 8 day
union select curdate() - interval 9 day
) days
left join myDataTable as n1
on days.day = n1.date AND n1.appId = '$id' AND n1.status = 'ERROR'
left join myDataTable as n2
on days.day = n2.date AND n2.appId = '$id' AND n2.status = 'SUCCESS'
group by
days.day
Lastly, instead of joining your myDataTable in twice for each status, you can use a CASE statement in your SELECT:
SELECT
days.day,
SUM(CASE WHEN n1.status = 'ERROR' THEN 1 ELSE 0 END) as countf,
SUM(CASE WHEN n1.status = 'SUCCESS' THEN 1 ELSE 0 END) as counts
FROM
(
select curdate() as day
union select curdate() - interval 1 day
union select curdate() - interval 2 day
union select curdate() - interval 3 day
union select curdate() - interval 4 day
union select curdate() - interval 5 day
union select curdate() - interval 6 day
union select curdate() - interval 7 day
union select curdate() - interval 8 day
union select curdate() - interval 9 day
) days
left join myDataTable as n1
on days.day = n1.date AND n1.appId = '$id'
group by
days.day

Full join two querys

This query returned 385 strings
SELECT order_number
FROM `order`
inner join order_delivery_data on `order`.order_id = order_delivery_data.order_id
where order_status = 'delivered' AND order_statusUpdatedAt >= DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY)
and order_delivery_data_name in ('London')
this returned 74 strings
SELECT order_number
FROM `order`
inner join order_delivery_data on `order`.order_id = order_delivery_data.order_id
where order_status = 'delivered' AND order_statusUpdatedAt BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY)
AND DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY)
and order_delivery_data_name in ('London')
How join it, that in that there are two columns
try this:
SELECT t1.order_number as order_t1, t2.order_number as order_t2 FROM
( SELECT order_number,`order`.order_id
FROM `order`
inner join order_delivery_data on `order`.order_id = order_delivery_data.order_id
where order_status = 'delivered' AND order_statusUpdatedAt BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY)
AND DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY)
and order_delivery_data_name in ('London')
) as t1
LEFT JOIN
(SELECT order_number,`order`.order_id
FROM `order`
inner join order_delivery_data on `order`.order_id = order_delivery_data.order_id
where order_status = 'delivered' AND order_statusUpdatedAt BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY)
AND DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY)
and order_delivery_data_name in ('London')
) as t2
ON t1.order_id = t2.order_id
UNION ALL
SELECT t1.order_number as order_t1, t2.order_number as order_t2 FROM
( SELECT order_number,`order`.order_id
FROM `order`
inner join order_delivery_data on `order`.order_id = order_delivery_data.order_id
where order_status = 'delivered' AND order_statusUpdatedAt BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY)
AND DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY)
and order_delivery_data_name in ('London')
) as t1
RIGHT JOIN
(SELECT order_number,`order`.order_id
FROM `order`
inner join order_delivery_data on `order`.order_id = order_delivery_data.order_id
where order_status = 'delivered' AND order_statusUpdatedAt BETWEEN DATE_SUB(CURRENT_DATE, INTERVAL 10 DAY)
AND DATE_SUB(CURRENT_DATE, INTERVAL 7 DAY)
and order_delivery_data_name in ('London')
) as t2
ON t1.order_id = t2.order_id;
You need to use FULL JOIN, i.e. UNION ALL when using mysql:
first_query UNION ALL second_query

how to join tables and get sum of first table if second has multiple occurences

I have two tables "temp_user_batches" and "user_activities" i am trying to find sum of user_activities for users present in temp_user_batches table.
problem is sum of user_activities is getting multiplied by number of times in ratio of occurences of user in temp_user_batches table.
Below is temp_user_batches table
This is user_activities table
it is supposed to give sum of time_spent column 649 + 364 = 1013 but instead its giving 2016
my query is:
SELECT temp_user_batches.user_id as user_id,
temp_user_batches.activity_goal as goal,
DATE_SUB(CURDATE(), INTERVAL 7 day) as min_activity_date,
CURDATE() as max_activity_date,
(sum(user_activities.time_spent)/60) as total_time_spent
FROM temp_user_batches
INNER JOIN user_activities
ON temp_user_batches.user_id = user_activities.user_id
WHERE activity_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 day) AND CURDATE()
group by user_id, goal, max_activity_date, min_activity_date
You can use a derived table that contains the DISTINCT pairs of user_id, activity_goal from table temp_user_batches:
SELECT t1.user_id as user_id,
t2.activity_goal as goal,
DATE_SUB(CURDATE(), INTERVAL 7 day) as min_activity_date,
CURDATE() as max_activity_date,
(sum(t2.time_spent)/60) as total_time_spent
FROM (
SELECT DISTINCT user_id, activity_goal
FROM temp_user_batches) AS t1
INNER JOIN user_activities AS t2 ON t1.user_id = t2.user_id
WHERE activity_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 day) AND CURDATE()
group by user_id, goal, max_activity_date, min_activity_date
From my understanding, you should try to GROUP_BY the temp_user_batches on user_id, last_activity before joining it with user_activities. This is because now you join user_activities on 2 rows instead of 1 row the way you want (from what I understand).
Something like:
SELECT
temp_user_batches.user_id AS user_id,
temp_user_batches.activity_goal AS goal,
DATE_SUB(CURDATE(), INTERVAL 7 DAY) AS min_activity_date,
CURDATE() AS max_activity_date,
(SUM(user_activities.time_spent) / 60) AS total_time_spent
FROM
(SELECT
*
FROM
temp_user_batches
GROUP BY user_id , last_activity)
INNER JOIN
user_activities ON temp_user_batches.user_id = user_activities.user_id
WHERE
activity_date BETWEEN DATE_SUB(CURDATE(), INTERVAL 7 DAY) AND CURDATE()
GROUP BY user_id , goal , max_activity_date , min_activity_date

mysql join same table different result set

I would like to combine different results from the same table as one big result.
SELECT host_name,stats_avgcpu,stats_avgmem,stats_avgswap,stats_avgiowait
FROM sar_stats,sar_hosts,sar_appgroups,sar_environments
WHERE stats_host = host_id
AND host_environment = env_id
AND env_name = 'Staging 2'
AND host_appgroup = group_id
AND group_name = 'Pervasive'
AND DATE(stats_report_time) = DATE_SUB(curdate(), INTERVAL 1 DAY)
SELECT AVG(stats_avgcpu),AVG(stats_avgmem),AVG(stats_avgswap),AVG(stats_avgiowait)
FROM sar_stats
WHERE stats_id = "stat_id of the first query" and DATE(stats_report_time)
BETWEEN DATE_SUB(curdate(), INTERVAL 8 DAY) and DATE_SUB(curdate(), INTERVAL 1 DAY)
SELECT AVG(stats_avgcpu),AVG(stats_avgmem),AVG(stats_avgswap),AVG(stats_avgiowait)
FROM sar_stats
WHERE stats_id = "stat_id of the first query" and DATE(stats_report_time)
BETWEEN DATE_SUB(curdate(), INTERVAL 31 DAY) and DATE_SUB(curdate(), INTERVAL 1 DAY)
Desired output would be something like ...
host_name|stats_avgcpu|stats_avgmem|stats_avgswap|stats_avgiowait|7daycpuavg|7daymemavg|7dayswapavg|7dayiowaitavg|30daycpuavg|30daymemavg|....etc
SQL Fiddle
http://sqlfiddle.com/#!8/4930b/3
It seems like this is what you want. I updated the first query to use proper ANSI JOIN syntax and then for the additional two queries they were joined via a LEFT JOIN on the stats_host field:
SELECT s.stats_host,
h.host_name,
s.stats_avgcpu,
s.stats_avgmem,
s.stats_avgswap,
s.stats_avgiowait,
s7.7dayavgcpu,
s7.7dayavgmem,
s7.7dayavgswap,
s7.7dayavgiowait,
s30.30dayavgcpu,
s30.30dayavgmem,
s30.30dayavgswap,
s30.30dayavgiowait
FROM sar_stats s
INNER JOIN sar_hosts h
on s.stats_host = h.host_id
INNER JOIN sar_appgroups a
on h.host_appgroup = a.group_id
and a.group_name = 'Pervasive'
INNER JOIN sar_environments e
on h.host_environment = e.env_id
and e.env_name = 'Staging 2'
LEFT JOIN
(
SELECT s.stats_host,
AVG(s.stats_avgcpu) AS '7dayavgcpu',
AVG(s.stats_avgmem) AS '7dayavgmem',
AVG(s.stats_avgswap) AS '7dayavgswap',
AVG(s.stats_avgiowait) AS '7dayavgiowait'
FROM sar_stats s
WHERE DATE(stats_report_time) BETWEEN DATE_SUB(curdate(), INTERVAL 8 DAY) AND DATE_SUB(curdate(), INTERVAL 1 DAY)
GROUP BY s.stats_host
) s7
on s.stats_host = s7.stats_host
LEFT JOIN
(
SELECT s.stats_host,
AVG(s.stats_avgcpu) AS '30dayavgcpu',
AVG(s.stats_avgmem) AS '30dayavgmem',
AVG(s.stats_avgswap) AS '30dayavgswap',
AVG(s.stats_avgiowait) AS '30dayavgiowait'
FROM sar_stats s
WHERE DATE(s.stats_report_time) BETWEEN DATE_SUB(curdate(), INTERVAL 31 DAY) AND DATE_SUB(curdate(), INTERVAL 1 DAY)
GROUP BY s.stats_host
) s30
on s.stats_host = s30.stats_host
WHERE DATE(s.stats_report_time) = DATE_SUB(curdate(), INTERVAL 1 DAY);
see SQL Fiddle with Demo