Count number of bookings between a set of specified dates - mysql

I have a table of bookings. I want to count how many bookings occur on each day, starting from specified check in date and check out date. Eg. if check in date was 10-06-2012 and check out date was 14-06-2012 I require a table like this
Date Bookings
10-06-2012 1
11-06-2012 1
12-06-2012 2
13-06-2012 4
14-06-2012 3
I am struggling to get this working. I can count bookings in between the dates but not for each date between check in date and check out date.

I am not sure I understand your question. The query below assumes:
Your bookings table has (at least) columns date, checkin, checkout.
You are looking for bookings where checkin >= 10-06-2012 and checkout <= 14-06-2012.
Here is the query:
SELECT date, COUNT(*)
FROM bookings
WHERE checkin >= '2012-06-10' AND checkout <= '2012-06-14'
GROUP BY date

Use SUM() to find total bookings between a date range.
Try Below :
SELECT Date,SUM(Bookings)
FROM tablename
WHERE Date between 'startdate' AND 'enddate'
GROUP BY Date

First thing you need is a table of dates, day by day. Now mysql is not my thing, so I will try to write down as much info on what I'm doing as I can. Please correct these examples.
Table of dates might be prepared by a job checking for the last booking date and adding missing dates to table of dates. If this is not something you would accept, other solution is to create table dynamically, but there are some perils. To my knowledge there is no way to create such a table, but you can do a practically-working surrogate by selecting distinct dates from your booking table and cross joining this with table of days made in query itself:
((select distinct checkIn from bookings union select distinct checkOut from bookings)
cross join (select 0 union select 1 union select 2 ...))
The list of days should contain as many days as the biggest gap between checkin dates and each checkin and checkout date. This is something you will have to keep an eye on, or simply make the list sufficiently large, for example a hundred days.
Now that you have a table of dates, you need to count bookings matching this date. Complete query would look like this:
select tableOfDates.date, count(bookings.checkIn) bookings
from
(
(
select distinct dates.date + INTERVAL days.day DAY -- OR HOWEVER you add days in mysql
from
(select distinct checkIn date from bookings union select distinct checkOut from bookings) dates
cross join (select 0 day union select 1 union select 2 union 3 union 4 union 5 union 6 union 7) days
)
) tableOfDates
left join bookings
on tableOfDates.date between bookings.checkIn and bookings.checkOut
where tableOfDates.date between [YOUR DATE RANGE]

Related

Getting the number of users for this year and last year in SQL

My table is like this:
root_tstamp
userId
2022-01-26T00:13:24.725+00:00
d2212
2022-01-26T00:13:24.669+00:00
ad323
2022-01-26T00:13:24.629+00:00
adfae
2022-01-26T00:13:24.573+00:00
adfa3
2022-01-26T00:13:24.552+00:00
adfef
...
...
2021-01-26T00:12:24.725+00:00
d2212
2021-01-26T00:15:24.669+00:00
daddfe
2021-01-26T00:14:24.629+00:00
adfda
2021-01-26T00:12:24.573+00:00
466eff
2021-01-26T00:12:24.552+00:00
adfafe
I want to get the number of users in the current year and in previous year like below using SQL.
Date Users previous_year
2022-01-01 10 5
2022-01-02 20 15
The code is written as follows.
select CAST(root_tstamp as DATE) as Date,
count(DISTINCT userid) as users,
count(Distinct case when CAST(root_tstamp as DATE) = dateadd(MONTH,-12,CAST(root_tstamp as DATE)) then userid end) as previous_year
FROM table1
But it returns 0 for previous_year values.
How can I fix that?
Possible solution for SQL Server:
WITH cte AS ( SELECT 2022 [year]
UNION ALL
SELECT 2021 )
SELECT cte.[year],
COUNT(DISTINCT test.userId) current_users_amount,
COUNT(DISTINCT CASE WHEN YEAR(test.root_tstamp) < cte.[year]
THEN test.userId
END) previous_users_amount
FROM test
JOIN cte ON YEAR(test.root_tstamp) <= cte.[year]
GROUP BY cte.[year]
https://dbfiddle.uk/?rdbms=sqlserver_2017&fiddle=88b78aad9acd965bdbac4c85a0b81927
This query (for MySql) returns unique number of userids where the root_timestamp is in the current year, by day, and the number of unique userids for the same day last year. If there is no record for a day in the current year nothing will be displayed for that day. If there are rows for the current year, but no rows for the same day last year, then NULL will be shown for that lastyear column.
SELECT cast(ty.root_tstamp as date) as Dte,
COUNT(DISTINCT ty.userId) as users_this_day,
count(distinct lysd.userid) as users_sameday_lastyear
FROM test ty
left join
test lysd
on cast(lysd.root_tstamp as date)=date_add(cast(ty.root_tstamp as date), interval -1 year)
WHERE YEAR(ty.root_tstamp) = year(current_date())
GROUP BY Dte
If you wish to show output rows for calendar days even if there are no rows in current year and/or last year, then you also need a calendar table to be introduced (let's hope that it is not what you need)

related to query using SQL

In oracle sql, how to get the count of newly added customers only for the month of april and may and make sure they werent there in the previous months
SELECT CUSTOMER ID , COUNT(*)
FROM TABLE
WHERE DATE BETWEEN '1-APR-2018' AND '31-MAY-2018' AND ...
If we give max (date) and min(date), we can compare the greater date to check if this customer is new , correct?
expected output is month count
april ---
may ---
should show the exact count how many new customers joined in these two months
One approach is to use aggregation:
select customer_id, min(date) as min_date
from t
group by customer_id
having min(date) >= date '2018-04-01 and
min(date) < date '2018-06-01';
This gets the list of customers (which your query seems to be doing). To get the count, just use count(*) and make this a subquery.

MySQL using count in query to search for availability of multiple rows

I am using one table, mrp to store multi room properties and a second table booking to store the dates the property was booked on.
I thus have the following tables:
mrp(property_id, property_name, num_rooms)
booking(property_id, booking_id, date)
Whenever a property is booked, an entry is made in the bookings table and because each table has multiple rooms, it can have multiple bookings on the same day.
I am using the following query:
SELECT * FROM mrp
WHERE property_id
NOT IN (SELECT property_id FROM booking WHERE `date` >= {$checkin_date} AND `date` <= {$checkout_date}
)
But although this query would work fine for a property with a single room (that is, it only lists properties which have not been booked altogether between the dates you provide), it does not display properties that have been booked but still have vacant rooms. How can we use count and the num_rooms table to show in my results the rooms which are still vacant, even if they already have a booking between the selected dates, and to display in my results the number of rooms that are free.
You need 3 levels of query. The innermost query will list properties and dates where all rooms are fully booked (or overbooked) on any day within your date range. The middle query narrows that down to just a list of property_id's. The outermost query lists all properties that are NOT in that list.
SELECT *
FROM mrp
WHERE property_id NOT IN (
-- List all properties sold-out on any day in range
SELECT DISTINCT Z.property_id
FROM (
-- List sold-out properties by date
SELECT MM.property_id, MM.num_rooms, BB.adate
, COUNT(*) as rooms_booked
FROM mrp MM
INNER JOIN booking BB on MM.property_id = BB.property_id
WHERE BB.adate >= #checkin AND BB.adate <= #checkout
GROUP BY MM.property_id, MM.num_rooms, BB.adate
HAVING MM.num_rooms - COUNT(*) <= 0
) as Z
)
You are close but you need to change the dates condition and add a condition to match the records from the outer and inner queries (all in the inner query's WHERE clause):
SELECT * FROM srp
WHERE NOT EXISTS
(SELECT * FROM bookings_srp
WHERE srp.booking_id = bookings_srp.booking_id
AND `date` >= {$check-in_date} AND `date` <= {$check-out_date})
You have to exclude the properties which are booked between the checkin date and checkout date. This query should do:
SELECT * FROM srp WHERE property_id NOT IN (
SELECT property_id FROM booking WHERE `date` >= {$checkin_date} AND `date` <= {$checkout_date}
)

MySql SUM and JOIN

I am trying to count sales made by a list of sales agents, this count is made every few minutes and updates a screen showing a 'sales leader board' which is updates using a Ajax call in the background.
I have one table which is created and populated every night containing the agent_id and the total sales for the week and month. I create a second, temporary table, on the fly which counts the sales for the day.
I need to combine the two tables to create a current list of sales for all agents in agent_count.
Table agent_count;
agent_id (varchar),
team_id (varchar),
name (varchar),
day(int),
week(int),
month(int)
Table sales;
agent_id (varchar),
day(int)
I can't figure out how to combine these tables. I think I need to use a join as all agents must be returned - even if they don't appear in the agent_count table.
First I make a simple call to get the week and month totals for all agents
SELECT agent_id, team_id, name, week, month FROM agent_count;
the I create a temporary table of todays sales, and then I count the sales for each agent for the day
CREATE TEMPORARY TABLE temp_todays_sales
SELECT s.id, s.agent_id
FROM sales s
WHERE DATEDIFF(s.uploaded, NOW()) = 0
AND s.valid = 1;
SELECT tts.agent_id, COUNT(tts.id) as today
FROM temp_todays_sales tts
GROUP BY tts.agent_id;
What is the best/easiet way to combine these to end up with a resultset such as
agent_id, team_id, name, day, week, month
where week and month also include the daily totals
thanks for any help!
Christy
SELECT s.agent_id, ac.team_id, ac.name,
s.`day` + COALESCE(ac.`day`, 0) AS `day`,
s.`day` + COALESCE(ac.`week`, 0) AS `week`,
s.`day` + COALESCE(ac.`month`, 0) AS `month`
FROM sales s
LEFT JOIN
agent_count ac
ON ac.agent_id = s.agent_id
team_id and name will be NULL if there is no record in agent_count for an agent.
If the agents can be missing from both tables, you normally would need to make a FULL JOIN but since MySQL does not support the latter you may use its poor man's substitution:
SELECT agent_id, MAX(team_id), MAX(name),
SUM(day), SUM(week), SUM(month)
FROM (
SELECT agent_id, NULL AS team_id, NULL AS name, day, day AS week, day AS month
FROM sales
UNION ALL
SELECT *
FROM agent_count
) q
GROUP BY
agent_id

How to get values for every day in a month

Data:
values date
14 1.1.2010
20 1.1.2010
10 2.1.2010
7 4.1.2010
...
sample query about january 2010 should get 31 rows. One for every day. And values vould be added. Right now I could do this with 31 queries but I would like this to work with one. Is it possible?
results:
1. 34
2. 10
3. 0
4. 7
...
This is actually surprisingly difficult to do in SQL. One way to do it is to have a long select statement with UNION ALLs to generate the numbers from 1 to 31. This demonstrates the principle but I stopped at 4 for clarity:
SELECT MonthDate.Date, COALESCE(SUM(`values`), 0) AS Total
FROM (
SELECT 1 AS Date UNION ALL
SELECT 2 UNION ALL
SELECT 3 UNION ALL
SELECT 4 UNION ALL
--
SELECT 28 UNION ALL
SELECT 29 UNION ALL
SELECT 30 UNION ALL
SELECT 31) AS MonthDate
LEFT JOIN Table1 AS T1
ON MonthDate.Date = DAY(T1.Date)
AND MONTH(T1.Date) = 1 AND YEAR(T1.Date) = 2010
WHERE MonthDate.Date <= DAY(LAST_DAY('2010-01-01'))
GROUP BY MonthDate.Date
It might be better to use a table to store these values and join with it instead.
Result:
1, 34
2, 10
3, 0
4, 7
Given that for some dates you have no data, you'll need to fill in the gaps. One approach to this is to have a calendar table prefilled with all dates you need, and join against that.
If you want the results to show day numbers as you have showing in your question, you could prepopulate these in your calendar too as labels.
You would join your data table date field to the date field of the calendar table, group by that field, and sum values. You might want to specify limits for the range of dates covered.
So you might have:
CREATE TABLE Calendar (
label varchar,
cal_date date,
primary key ( cal_date )
)
Query:
SELECT
c.label,
SUM( d.values )
FROM
Calendar c
JOIN
Data_table d
ON d.date_field = c.cal_date
WHERE
c.cal_date BETWEEN '2010-01-01' AND '2010-01-31'
GROUP BY
d.date_field
ORDER BY
d.date_field
Update:
I see you have datetimes rather than dates. You could just use the MySQL DATE() function in the join, but that would probably not be optimal. Another approach would be to have start and end times in the Calendar table defining a 'time bucket' for each day.
This works for me... Its a modification of a query I found on another site. The "INTERVAL 1 MONTH" clause ensures I get the current month data, including zeros for days that have no hits. Change this to "INTERVAL 2 MONTH" to get last months data, etc.
I have a table called "payload" with a column "timestamp" - Im then joining the timestamp column on to the dynamically generated dates, casting it so that the dates match in the ON clause.
SELECT `calendarday`,COUNT(P.`timestamp`) AS `cnt` FROM
(SELECT #tmpdate := DATE_ADD(#tmpdate, INTERVAL 1 DAY) `calendarday`
FROM (SELECT #tmpdate :=
LAST_DAY(DATE_SUB(CURDATE(),INTERVAL 1 MONTH)))
AS `dynamic`, `payload`) AS `calendar`
LEFT JOIN `payload` P ON DATE(P.`timestamp`) = `calendarday`
GROUP BY `calendarday`
To dynamically get the dates within a date range using SQL you can do this (example in mysql):
Create a table to hold the numbers 0 through 9.
CREATE TABLE ints ( i tinyint(4) );
insert into ints (i)
values (0),(1),(2),(3),(4),(5),(6),(7),(8),(9);
Run a query like so:
select ((curdate() - interval 2 year) + interval (t.i * 100 + u.i * 10 + v.i) day) AS Date
from
ints t
join ints u
join ints v
having Date between '2015-01-01' and '2015-05-01'
order by t.i, u.i, v.i
This will generate all dates between Jan 1, 2015 and May 1, 2015.
Output
2015-01-01
2015-01-02
2015-01-03
2015-01-04
2015-01-05
2015-01-06
...
2015-05-01
The query joins the table ints 3 times and gets an incrementing number (0 through 999). It then adds this number as a day interval starting from a certain date, in this case a date 2 years ago. Any date range from 2 years ago and 1,000 days ahead can be obtained with the example above.
To generate a query that generates dates for more than 1,000 days simply join the ints table once more to allow for up to 10,000 days of range, and so forth.
If I'm understanding the rather vague question correctly, you want to know the number of records for each date within a month. If that's true, here's how you can do it:
SELECT COUNT(value_column) FROM table WHERE date_column LIKE '2010-01-%' GROUP BY date_column