How to obtaining remaining available days in a month for booking? - mysql

I have a table for slots that have the following Schema
SlotId
FromDate
ToDate
I want to get the remaining days in that given month available for slot booking.
To be clear, I am trying to retrieve all the dates apart from the dates stored in the database(as those are already booked) for a given month.
For example, if a record have FromDate is equal to 2014-04-02 and ToDate is equal to 2014-04-06 I am expecting the following result:
2014-04-01
2014-04-07
...
2014-04-30
Although i am scripting in PHP, I am little curious about the query to accomplish this.

So it is not an easy thing to do in mysql but here is something that should work. this gets the dates in any given month that are not booked... see fiddle for working example
SELECT *, union_month.day_date
FROM (
SELECT 1 AS day_date UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 8 UNION ALL
SELECT 9 UNION ALL
SELECT 10 UNION ALL
SELECT 11 UNION ALL
SELECT 12 UNION ALL
SELECT 13 UNION ALL
SELECT 14 UNION ALL
SELECT 15 UNION ALL
SELECT 16 UNION ALL
SELECT 17 UNION ALL
SELECT 18 UNION ALL
SELECT 19 UNION ALL
SELECT 20 UNION ALL
SELECT 21 UNION ALL
SELECT 22 UNION ALL
SELECT 23 UNION ALL
SELECT 24 UNION ALL
SELECT 25 UNION ALL
SELECT 26 UNION ALL
SELECT 27 UNION ALL
SELECT 28 UNION ALL
SELECT 29 UNION ALL
SELECT 30 UNION ALL
SELECT 31
) AS union_month
LEFT JOIN myTable AS t ON union_month.day_date <> DAY(t.to_date) OR union_month.day_date <> DAY(t.from_date)
WHERE union_month.day_date <= DAY(LAST_DAY(t.to_date))
AND union_month.day_date NOT BETWEEN DAY(t.from_date) AND DAY(t.to_date)
GROUP BY union_month.day_date
for multiple dates in a month change the WHERE clause to this
WHERE
union_month.day_date <= DAY(LAST_DAY(t.to_date))
AND union_month.day_date not BETWEEN (select DAY(from_date) from myTable limit 0,1) AND (select DAY(to_date) from myTable limit 0,1)
AND union_month.day_date not BETWEEN (select DAY(from_date) from myTable limit 1,1) AND (select DAY(to_date) from myTable limit 1,1)
AND union_month.day_date not BETWEEN (select DAY(from_date) from myTable limit 2,1) AND (select DAY(to_date) from myTable limit 2,1)
GROUP BY union_month.day_date
working fiddle for multiple dates

You need remaining days count or dates which are free?

I have tried this. May it will help you.
You need to use this query in loop. with some variables.
In example I have consider April month only. You can do it for all months.
SELECT distinct * FROM (SELECT DATE_ADD('2014-04-01', INTERVAL t4+t16+t64+t256+t1024 DAY) freedays FROM
(SELECT 0 t4 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 ) t4,
(SELECT 0 t16 UNION ALL SELECT 4 UNION ALL SELECT 8 UNION ALL SELECT 12 ) t16,
(SELECT 0 t64 UNION ALL SELECT 16 UNION ALL SELECT 32 UNION ALL SELECT 48 ) t64,
(SELECT 0 t256 UNION ALL SELECT 64 UNION ALL SELECT 128 UNION ALL SELECT 192) t256,
(SELECT 0 t1024 UNION ALL SELECT 256 UNION ALL SELECT 512 UNION ALL SELECT 768) t1024
) b
WHERE freedays not between (select FrmDate from slotbooking limit 1) and (select ToDate from
slotbooking limit 1) and freedays < '2014-04-30';

Related

Return count of rows for all month, even though there might not be an entry for the month. Return 0 for month/d with no entry

I am trying to get month count even if there isn't a entry for a month. I have looked at few examples and failed to make it work. I have a 3 tables as below
$tablename="appointments"
timeIn
status
2020-10-25 13:00:00
Completed
2020-11-25 13:00:00
Completed
2020-12-25 13:00:00
Completed
I am able to get the following count for each month as below by using the following select statement:
select MONTHNAME(timeIn),Count(*) from ".$tablename." WHERE timeIn!=:null group by MONTHNAME(timeIn) order by MONTHNAME(timeIn) DESC"
{October:"1", November: "1", December: "1"}
By searching online i found this select which i have modified to work with my table. It is not working and i am not sure what is wrong with this as i am not well verse with mysql.
SELECT Months.id AS `month` , COUNT(`".$tablename."`.timeIn) AS `count` FROM (SELECT 'January' as ID UNION SELECT 'February' as ID UNION SELECT 'March' as ID UNION SELECT 'April' as ID UNION SELECT 'May' as ID UNION SELECT 'June' as ID UNION SELECT 'July' as ID UNION SELECT 'August' as ID UNION SELECT 'September' as ID UNION SELECT 'October' as ID UNION SELECT 'November' as ID UNION SELECT 'December' as ID) as Months LEFT JOIN `".$tablename."` on Months.id=monthname(`".$tablename."`.timeIn) AND (status = 'Completed') GROUP BY Months.
I would like to get results as below, where it would return 0 if there is no entry for the month. Please be able to provide some explanation as it is a bit challenging to understand mysql statement. Appreciate any help i can get
{January: "0",.....,November:"0",October:"1", November: "1", December: "1"}
UPDATE FROM AKINA's ANSWER
$sql="SELECT MONTHNAME(CONCAT('2020-', cte.num, '-1')) `Month`, COUNT(".$tablename.".timeIn) `Count` FROM (SELECT 1 NUM UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9 UNION SELECT 10 UNION SELECT 11 UNION SELECT 12) cte LEFT JOIN ".$tablename." ON cte.num = MONTH(".$tablename.".timeIn) GROUP BY 1;";
$data=$con->prepare($sql);
$data->execute();
$rows=$data->fetchAll(PDO::FETCH_ASSOC);
WITH RECURSIVE
cte AS ( SELECT 1 num
UNION ALL
SELECT num + 1 FROM cte WHERE num < 12 )
SELECT MONTHNAME(CONCAT('2020-', cte.num, '-1')) `Month`,
COUNT(appointments.timeIn) `Count`
FROM cte
LEFT JOIN appointments ON cte.num = MONTH(appointments.timeIn)
GROUP BY 1;
fiddle
PS. MySQL 8 needed.
working with version 5.6.32 – Ahsan
SELECT MONTHNAME(CONCAT('2020-', cte.num, '-1')) `Month`, COUNT(appointments.timeIn) `Count`
FROM (SELECT 1 NUM UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION
SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION
SELECT 9 UNION SELECT 10 UNION SELECT 11 UNION SELECT 12) cte
LEFT JOIN appointments ON cte.num = MONTH(appointments.timeIn)
GROUP BY 1;
fiddle

get result of non-existent dates too

I have a MySql table containing events having a DATETIME timestamp. I want to count each day's events. On some days, e.g. on Sundays, events are missing. The result should contain these days too with a count of zero.
My query is the following:
SELECT
COUNT(1) AS mycount,
DATE_FORMAT(DATE(evaluations.timestamp),"%a, %d.%m.%Y") AS date
FROM Events
GROUP BY DATE(timestamp)
ORDER BY DATE(timestamp) DESC
Can I modify the query without using a helper table containing all dates?
A single query (no procedere, no function) would be fine.
The query would somehow look like this if you don't have any calendar table:
SELECT
dateTable.day,
COALESCE(t.mycount,0) AS cnt
FROM
(
SELECT ADDDATE((SELECT MIN(DATE(timestamp)) FROM Events), INTERVAL #i:=#i+1 DAY) AS DAY
FROM (
SELECT a.a
FROM (SELECT 0 AS a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS a
CROSS JOIN (SELECT 0 AS a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS b
CROSS JOIN (SELECT 0 AS a UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS c
) a
JOIN (SELECT #i := -1) r1
WHERE
#i < DATEDIFF((SELECT MAX(DATE(timestamp)) FROM Events), (SELECT MIN(DATE(timestamp)) FROM Events))
) AS dateTable
LEFT JOIN
(
SELECT
COUNT(1) AS mycount,
DATE_FORMAT(DATE(evaluations.timestamp),"%a, %d.%m.%Y") AS date
FROM Events
GROUP BY DATE(timestamp)
ORDER BY DATE(timestamp) DESC
) AS t
ON dateTable.day = t.date
ORDER BY dateTable.day DESC;
Note:
If you think you will need this kind of query too often then you can create a table where all the dates would reside. Newer dates can be added through mysql event periodically .
Then the work is simple. Just need to make a LEFT JOIN between the calendar table and the result of your query.

MYSQL: Get zero values in group by clause

I need zero values also in group by clause .Have read almost all question related to this on Stackoverflow, but none of the solutions have worked.
My Table is
Need to get sum of score grouped by day of month.But I am not getting zero against the days not present in the table
SELECT SUM(engagement_score), DAY(creation_dt)
FROM qee_emp_engagement_index
RIGHT JOIN (
SELECT 1 AS index1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 8 UNION ALL
SELECT 9) AS a ON a.index1 = DAY(creation_dt)
WHERE org_id = 1
GROUP BY a.index1
ORDER BY a.index1 ASC
I would write the query using a left join rather than a right join (the logic of left join makes more sense to me: keep all the rows in the first table). But, your problem is the where clause. That logic should go in the on clause:
SELECT COALESCE(SUM(eei.engagement_score), 0), a.index1
FROM (SELECT 1 AS index1 UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
SELECT 5 UNION ALL
SELECT 6 UNION ALL
SELECT 7 UNION ALL
SELECT 8 UNION ALL
SELECT 9
) a LEFT JOIN
qee_emp_engagement_index eei
ON a.index1 = DAY(eei.creation_dt) AND eei.org_id = 1
GROUP BY a.index1
ORDER BY a.index1 ASC;
In addition, the GROUP BY and SELECT should use the column from the driving table.
You can do right join between your table and a temp table containing all dates in a range.
Dates can be generated using below query
select a.Date
from (
select curdate() - INTERVAL (a.a + (10 * b.a) + (100 * c.a)) DAY as Date
from (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as a
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as b
cross join (select 0 as a union all select 1 union all select 2 union all select 3 union all select 4 union all select 5 union all select 6 union all select 7 union all select 8 union all select 9) as c
) a
where a.Date between '2015-01-01' and '2017-01-01'
SELECT DATE(creation_date), SUM(engagement_score) FROM table_name GROUP BY DATE(creation_date)
There is no need to do that weird right join in your code
If you want to group it by the day of month and not by the date, use DAY() instead of DATE()
EDIT:
If you want a zero when the day of month doesn't exist, try this:
SELECT d, SUM(score) FROM
((SELECT creation_date AS d, engagement_score AS score FROM table_name)
UNION ALL
(SELECT d, 0 AS engagement_score FROM table_name WHERE d BETWEEN 1 AND 31))
GROUP BY d

Sort data from MYSQL database by hour

I have users in db that I want to sort by hour and display count of users registered at that hour.
select
date_format(create_time, '%Y-%m-%d %h%p') as date,
count(id) as 'Number of registrations'
from users
group by 1
order by 1 desc
;
The above code will work; however, what I am trying to do is display 0's for the hours that have no user registrations. For example, if there were no registrations at 5pm, this will skip row for 5pm, which is logical. Is there a way to achieve what I am trying?
You could use a query like this:
select
date_format(t.d + INTERVAL t.h HOUR, '%Y-%m-%d %h%p') as date,
count(id) as 'Number of registrations'
from (
SELECT *
FROM
(SELECT DISTINCT DATE(create_time) d FROM users) dates,
(SELECT 0 h UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11
UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15
UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19
UNION ALL SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23) hours
) t LEFT JOIN users
ON DATE(users.create_time)=t.d AND HOUR(users.create_time)=t.h
group by t.d, t.h
order by t.d, t.h
Please see fiddle here.
You need to generate all possible day and hour combinations.
Assuming that you have at least one record on each day and one record for each hour, you can do:
select concat(d.theday, ' ', h.thehour) as date,
count(id) as 'Number of registrations'
from (select distinct date_format(create_time, '%Y-%m-%d') as theday from users
) d cross join
(select distinct date_format(create_time, '%h%p') as thehour from users
) h left outer join
users u
on date_format(u.create_time, '%Y-%m-%d %h%p) = concat(d.theday, ' ', h.thehour)
group by concat(d.theday, ' ', h.thehour)
order by 1 desc;

Mysql hourly report query

I want to get an hourly report of conversions for all 24 hours.
I have this query but it returns only 19rows instead of 24
can anyone plz tell me wats wrong in this?
Thanks in advance.
SELECT HOUR( `date_time` ) AS Hours, COUNT(conversion_id) AS `conversion` FROM conversions
RIGHT JOIN (SELECT 0 AS Hour UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT
5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11
UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL
SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23) AS AllHours
ON HOUR(date_time) = Hour
WHERE DATE(date_time) = CURDATE() OR date_time IS NULL
GROUP BY Hour
ORDER BY Hour
If there are not entries for this hour, it is never selected. You have to query the other way round.
I think it should be something like this (hard to test without your database):
select * from (SELECT 0 AS Hour UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23) as AllHours
left outer join
(select COUNT(conversion_id) as cnt, HOUR(date_time) as h
FROM conversions
WHERE DATE(date_time) = CURDATE() OR date_time IS NULL
group by h) as c
on Hour = e.h
The right join is almost correct. I prefer that the where condition be in the on clause (rather than checking for NULL values. The key, though, is using the AllHours table in the select and group by:
SELECT AllHours.Hour AS Hours, COUNT(conversion_id) AS `conversion`
FROM conversions RIGHT JOIN
(SELECT 0 AS Hour UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT
5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11
UNION ALL SELECT 12 UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL
SELECT 18 UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21 UNION ALL SELECT 22 UNION ALL SELECT 23
) AS AllHours
ON HOUR(conversions.date_time) = AllHours.Hour and DATE(conversions.date_time) = CURDATE()
GROUP BY AllHOurs.Hour
ORDER BY AllHours.Hour