Get sums of vacancies on two different fields - mysql

I have a table "vacancies", which consists of an id, vacancy_status_id, published_date, created and updated.
The created indicates the time the row was created in the database, the updated indicates last time any value in the row changed and published_date indicates the time the vacancy went to status 2 (active).
Apart from that I have created a calendar table that contains all dates from 2010-01-01 to 2050-12-31 (as described here: https://ubiq.co/database-blog/fill-missing-dates-in-mysql/ )
I want to get the amount of active vacancies and the amount of inactive vacancies per day starting from the date the first vacancy was created in my database.
The tricky part is, that the active vacancies are calculated based on the "published_date" field, while the inactive vacancies are based on the "updated" field.
I am able to write this query as 2 separate queries, but I'm not able to combine these into one query:
SELECT c.datefield AS created,
SUM(case when v.vacancy_status_id=2 then 1 else 0 end) as amount_active_vacancies
FROM calendar AS c
LEFT OUTER JOIN vacancy AS v ON c.datefield = DATE(v.published_date)
WHERE c.datefield BETWEEN (SELECT MIN(DATE(created)) FROM vacancy) AND DATE(NOW() - INTERVAL 1 DAY)
GROUP BY c.datefield
SELECT c.datefield AS created,
SUM(case when v.vacancy_status_id=3 OR v.vacancy_status_id=4 then 1 else 0 end) as amount_inactive_vacancies
FROM calendar AS c
LEFT OUTER JOIN vacancy AS v ON c.datefield = DATE(v.updated)
WHERE c.datefield BETWEEN (SELECT MIN(DATE(created)) FROM vacancy) AND DATE(NOW() - INTERVAL 1 DAY)
GROUP BY c.datefield
How could I combine these two in one query ?
My result should show :
created | amount_active_vacancies | amount_inactive_vacancies

If your two queries are correct, you can join them together
SELECT a.created, a.amount_active_vacancies, b.amount_inactive_vacancies
FROM (
SELECT c.datefield AS created,
SUM(case when v.vacancy_status_id=2 then 1 else 0 end) as amount_active_vacancies
FROM calendar AS c
LEFT OUTER JOIN vacancy AS v ON c.datefield = DATE(v.published_date)
WHERE c.datefield BETWEEN (SELECT MIN(DATE(created)) FROM vacancy) AND DATE(NOW() - INTERVAL 1 DAY)
GROUP BY c.datefield
) a
JOIN (
SELECT c.datefield AS created,
SUM(case when v.vacancy_status_id=3 OR v.vacancy_status_id=4 then 1 else 0 end) as amount_inactive_vacancies
FROM calendar AS c
LEFT OUTER JOIN vacancy AS v ON c.datefield = DATE(v.updated)
WHERE c.datefield BETWEEN (SELECT MIN(DATE(created)) FROM vacancy) AND DATE(NOW() - INTERVAL 1 DAY)
GROUP BY c.datefield
) b
WHERE a.created = b.created
or combine the conditions
SELECT c.datefield AS created,
SUM(case when v.vacancy_status_id=2 then 1 else 0 end) as amount_active_vacancies,
SUM(case when v.vacancy_status_id=3 OR v.vacancy_status_id=4 then 1 else 0 end) as amount_inactive_vacancies
FROM calendar AS c
LEFT OUTER JOIN vacancy AS v ON c.datefield = DATE(v.published_date) OR c.datefield = DATE(v.updated)
WHERE c.datefield BETWEEN (SELECT MIN(DATE(created)) FROM vacancy) AND DATE(NOW() - INTERVAL 1 DAY)
GROUP BY c.datefield

Related

How to query a hotel database to return the query for a single room available for three consecutive nights?

I'm trying to find an answer to the following query:
A customer wants a single room for three consecutive nights. Find the first available date in December 2016.
As per the question, this should be the right answer. But I don't know how to solve it.
+-----+------------+
| id | MIN(i) |
+-----+------------+
| 201 | 2016-12-11 |
+-----+------------+
The link is from question number 14 here.
This is the ER diagram of the database:
I apologize that I'm a bit rusty with this kind of query and I can't guarantee that I got all of the syntax correct, but I think that something like the following might work:
SELECT id, DATE_ADD(b.booking_date, INTERVAL (end_date + 1 DAY) as date
FROM (
SELECT r.id, STR_TO_DATE('2016-01-01', '%Y-%m-%d') as start_of_month, b.booking_date as start_date, DATE_ADD(b.booking_date, INTERVAL (nights - 1) DAY) as end_date
FROM room r
LEFT JOIN booking b ON r.id = b.room_no
ORDER BY r.id, b.booking_date
) as room_bookings
WHERE DATE_DIFF(room_bookings.start_of_month, room_bookings.start_date) >= 3
OR DATE_DIFF(room_bookings.end_date, (
SELECT b2.booking_date FROM booking b2
WHERE b2.room_no = room_bookings.id AND b2.booking_date > room_bookings.start_date
ORDER BY b2.booking_date LIMIT 1)
) >= 3
In fact, now that I type that all out, you might be able to tweak the WHERE of the main query so that you don't even need the room_bookings subselect. Hopefully this helps and isn't too far off the mark.
This seems very hard to do without a calendar table -- because an appropriate room might have no booking at all during the month. Without any booking, there is no record in the month to start with.
select r.id, dte
from rooms r cross join
(select date('2018-12-01') as dte union all
select date('2018-12-02') as dte union all
. . .
select date('2018-12-32') as dte
) d
where not exists (select 1 from bookings b where b.room_no = r.id and b.booking_date = d.dte) and
not exists (select 1 from bookings b where b.room_no = r.id and b.booking_date = d.dte + interval 1 day) and
not exists (select 1 from bookings b where b.room_no = r.id and b.booking_date = d.dte + interval 2 day)
order by d.dte
limit 1;
This assumes that booking_date is the start of the stay. You need to provide the logic for a "single room".
select distinct top 1 alll.i,alll.room_no,
case
when (select count(*) from booking where room_no = alll.room_no and booking_date between dateadd(day,1,alll.i) and dateadd(day,3,alll.i)) > 0 then 'Y'
else 'N'
end as av3
from
(select c.i,b.room_no,b.booking_date
from calendar c cross join booking b
where month(c.i) = 12 and year(c.i) = 2016 and b.room_type_requested = 'single'
) as alll
join
(
select distinct c.i, b.room_no
from calendar c join booking b
on c.i between b.booking_date and DATEADD(day,b.nights-1,b.booking_date)
where month(c.i) = 12 and year(c.i) = 2016 and b.room_type_requested = 'single'
) as booked
on alll.i = booked.i
and alll.room_no <> booked.room_no
order by 1
This works. It is a little complicated but basically first checks all the rooms that are booked and then does a comparison between rooms not booked on each day of the month till the next 3 days.
My solution is separate problem into 2 parts (in the end was 2 queries joined together). May not be the most efficient but the solution is correct.
1) Of the single rooms, look at the last check-out date, and see which one is vacant first (i.e. no more bookings for the rest of the month)
2) check in between current reservations - and see if there's a 3 day gap between them
3) join those together - grab the min
WITH subquery AS( -- existing single-bed bookings in Dec
SELECT room_no, booking_date,
DATE_ADD(booking_date, INTERVAL (nights-1) DAY) AS last_night
FROM booking
WHERE room_type_requested='single' AND
DATE_ADD(booking_date, INTERVAL (nights-1) DAY)>='2016-12-1' AND
booking_date <='2016-12-31'
ORDER BY room_no, last_night)
SELECT room_no, MIN(first_avail) AS first_avail --3) join the 2 together
FROM(
-- 1) check the last date the room is booked in December (available after)
SELECT room_no, MIN(first_avail) AS first_avail
FROM(
SELECT room_no, DATE_ADD(MAX(last_night), INTERVAL 1 DAY) AS first_avail
FROM subquery q3
GROUP BY 1
ORDER BY 2) AS t2
UNION
-- 2) check if any 3-day exist in between reservations
SELECT room_no, DATE_ADD(MIN(end2), INTERVAL 1 DAY) AS first_avail
FROM(
SELECT q1.booking_date AS beg1, q1.room_no, q1.last_night AS end1,
q2.booking_date AS beg2, q2.last_night AS end2
FROM subquery q1
JOIN subquery q2
ON q1.room_no = q2.room_no AND q2.booking_date > q1.last_night
GROUP BY 2,1
ORDER BY 2,1) AS t
WHERE beg2-end1 > 3) AS inner_t
This works conceptually as the first avaiable date should always be the end of the previous booking.
SELECT MIN(DATE_ADD(a.booking_date, INTERVAL nights DAY)) AS i
FROM booking AS a
WHERE DATE_ADD(a.booking_date, INTERVAL nights DAY)
>= '2016-12-01'
AND room_type_requested = 'single'
AND NOT EXISTS
(SELECT 1 FROM booking AS b
WHERE b.booking_date BETWEEN
DATE_ADD(a.booking_date, INTERVAL nights DAY)
AND DATE_ADD(a.booking_date, INTERVAL nights+2 DAY)
AND a.room_no = b.room_no)

Sql Server 2008 Exclude Company Holiday from DateDiff

I have three tables, One of which is a master calendar that houses an indicator for company Holidays. I would like to exlude Holiday from my DateDiff calculation. Below is what I have...
alert date 12/23/16
complete date 12/28/16
company Holiday 12/26/16 Need to exlude from DateDIff calculation
--My calculation shows 3 business days it should exclude 12/26/16 = 2 business days
DATEDIFF(DD,A.ALERTS_CREATE_DT,S.CreatedDate)
-(DATEDIFF(WK,A.ALERTS_CREATE_DT,S.CreatedDate) * 2) --HOW MANY WEEKEND DAYS PASSED BY
-(CASE WHEN DATENAME(DW,A.ALERTS_CREATE_DT) = 'Sunday' THEN 1 ELSE 0 END)
-(CASE WHEN DATENAME(DW,S.CreatedDate) = 'Saturday' THEN 1 ELSE 0 END)
-(CASE WHEN CAL.GRH_HOLIDAY_IND = 'Y' THEN 1 ELSE 0 END)
-( SELECT COUNT(CAL.GRH_HOLIDAY_IND)
FROM A
FULL OUTER JOIN S ON A.ID= S.ID
INNER JOIN CAL ON A.ALERTCREATEDT_MMDDYYYY = CAL.CALENDAR_DATE
WHERE 1=1
AND Cal.GRH_HOLIDAY_IND = 'Y'
AND CAL.CALENDAR_DATE between CAST(A.CREATE_DT AS DATE) AND CAST(S.CreatedDate AS DATE) ) AS BusinessDays
Trying to remove the Holidays is where I'm struggling, see below.
-( SELECT COUNT(CAL.GRH_HOLIDAY_IND)
FROM A
FULL OUTER JOIN S ON A.ID= S.ID
INNER JOIN CAL ON A.ALERTCREATEDT_MMDDYYYY = CAL.CALENDAR_DATE
WHERE 1=1
AND Cal.GRH_HOLIDAY_IND = 'Y'
AND CAL.CALENDAR_DATE between CAST(A.CREATE_DT AS DATE) AND CAST(S.CreatedDate AS DATE
After 2 case statements you can add below statement to subtract no of days from holiday Calendar table.
- (select ISNULL(count(1),0) from Cal where Cal.Holiday between ALERTS_CREATE_DT AND CreatedDate )

MySQL: select all parents AND date of most recent child AND date of any future child

I have an existing query which gets me the parent record plus the greatest date from any child records. The child records are calls made. I have now been asked to show the date of the most recent "past" call and the date of any future "scheduled" calls.
Here is the query I am using now:
SELECT doctor.*, contact.date_scheduled
FROM doctor
LEFT JOIN contact ON doctor.doctorID = contact.doctorID
AND date_scheduled = (SELECT MAX(date_scheduled) FROM contact WHERE doctor.doctorID = contact.doctorID)
ORDER BY date_scheduled DESC
Can someone please show me how to modify this query to get the two dates as I have described above?
I think this is what you want:
SELECT d.*,
max(case when c.date_scheduled < now() then c.date_scheduled end) as MostRecentCall,
group_concat(case when c.date_scheduled >= now() then c.date_scheduled end) as FutureCalls
FROM doctor d left join
contact c
on d.doctorID = c.doctorID
GROUP BY d.doctorId;
This will put future call dates in a list, separated by commas.
EDIT:
If you only want doctors that have calls scheduled in the future, then use a having clause:
SELECT d.*,
max(case when c.date_scheduled < now() then c.date_scheduled end) as MostRecentCall,
group_concat(case when c.date_scheduled >= now() then c.date_scheduled end) as FutureCalls
FROM doctor d left join
contact c
on d.doctorID = c.doctorID
GROUP BY d.doctorId
HAVING sum(c.date_scheduled >= now()) > 0;

showing previous and current month data in table using mysql

I am trying to show three different figures of the same column In a mysql query, I would like to keep one month static: April, so it would be a case like this I want to show The current month, the previous month and the static month of the year I'm working with, in this case let us stick with 2012
Example
Tablename:payment
id , pay_date, amount
1 2012-02-12 1000
2 2012-03-11 780
3 2012-04-15 890
4 2012-05-12 1200
5 2012-06-12 1890
6 2012-07-12 1350
7 2012-08-12 1450
So what I want to do is show the column amount for the month of April as I said I want to keep that row static: 890, the current month lets say the current month is August:1450 and the previous month amount which would be July:1350: so the final result would be something like this:
april_amount current_month_amount previous_month_amount
890 1450 1350
However I'm stuck here:
select amount as april_amount
from payment
where monthname(pay_date) LIKE 'April'
and year(pay_date) LIKE 2012
I hope the question is written clear enough, and thanks alot for the help much appreciated.
If the results can be rows instead of columns:
SELECT MONTHNAME(pay_date), amount FROM payment
WHERE pay_date BETWEEN '2012-04-01'
AND '2012-04-30'
OR pay_date BETWEEN CURRENT_DATE
- INTERVAL DAYOFMONTH(CURRENT_DATE) - 1 DAY
AND LAST_DAY(CURRENT_DATE)
OR pay_date BETWEEN CURRENT_DATE
- INTERVAL DAYOFMONTH(CURRENT_DATE) - 1 DAY
- INTERVAL 1 MONTH
AND LAST_DAY(CURRENT_DATE - INTERVAL 1 MONTH)
See it on sqlfiddle.
I might be way off here. But try:
select top 1
p.amount, c.amount, n.amount
from payment c
inner join payment p ON p.pay_date < c.pay_date
inner join payment n ON n.pay_date > c.pay_date
where monthname(c.paydate) LIKE 'April'
and year(c.pay_date) LIKE 2012
order by p.pay_date DESC, n.pay_date ASC
EDIT, I didnt read your question properly. I was going for previous, current, and next month. 1 minute and I'll try again.
select top 1
p.amount AS april_amount, c.amount AS current_month_amount, n.amount AS previous_month_amount
from payment c
inner join payment p ON monthname(p.pay_date) = 'April' AND year(p.pay_date) = 2012
inner join payment n ON n.pay_date > c.pay_date
where monthname(c.paydate) = monthname(curdate())
and year(c.pay_date) = year(curdate())
order by n.pay_date ASC
This assumes there is only 1 entry per month.
Ok, so i haven't written in mysql for a while. here is what worked for your example data:
select
p.amount AS april_amount, c.amount AS current_month_amount, n.amount AS previous_month_amount
from payment AS c
inner join payment AS p ON monthname(p.pay_date) LIKE 'April' AND year(p.pay_date) LIKE 2012
inner join payment AS n ON n.pay_date < c.pay_date
where monthname(c.pay_date) LIKE monthname(curdate())
and year(c.pay_date) LIKE year(curdate())
order by n.pay_date DESC
limit 1
the previous month table joined is counterintuitively named n, but this works. I verified it in a WAMP install.
To handle aggregates per month you can use subselects. Performance may suffer on very large tables (millions of rows or more).
SELECT SUM( a.amount ) AS april_amount,
(
SELECT SUM( c.amount )
FROM payment c
WHERE MONTH( c.pay_date ) = MONTH( CURDATE( ) )
) AS current_month_amount,
(
SELECT SUM( p.amount )
FROM payment p
WHERE MONTH( p.pay_date ) = MONTH( CURDATE( ) - INTERVAL 1
MONTH )
) AS previous_month_amount
FROM payment a
WHERE MONTHNAME( a.pay_date ) = 'April'
AND YEAR( a.pay_date ) =2012

Count another count value but only if it is high enough

I'm trying to pull 2 numbers. One is a total of how many doctors (dr table) have more than 10 answers (answers table) from within 1 month and 75 answers total regardless of the date. The other number is the same thing but for within the last 3 months instead of 1 month.
I used this answer answer below to come up with this query:
SELECT D.name,
count(DISTINCT case when A.created > DATE_SUB(NOW(), INTERVAL 1 MONTH) then A.id end) as '1 month',
count(DISTINCT case when A.created > DATE_SUB(NOW(), INTERVAL 3 MONTH) then A.id end) as '1 quarter',
count(DISTINCT A.id) as total
FROM dr D
JOIN answer A ON A.dr_id=D.id AND A.status=3
GROUP BY D.id
This gives me the raw information I need, but I don't know how to count the counts given by comparing them to the 10 and 75 answers requirements.
Something like this, I think:
SELECT
COUNT(CASE WHEN total >= 75 AND `1month` > 10 THEN name END) AS `10+ per month count`,
COUNT(CASE WHEN total >= 75 AND `1quarter` > 10 THEN name END) AS `10+ per quarter count`
FROM (
SELECT D.name,
count(DISTINCT case when A.created > DATE_SUB(NOW(), INTERVAL 1 MONTH) then A.id end) as `1month`,
count(DISTINCT case when A.created > DATE_SUB(NOW(), INTERVAL 3 MONTH) then A.id end) as `1quarter`,
count(DISTINCT A.id) as total
FROM dr D
JOIN answer A ON A.dr_id=D.id AND A.status=3
GROUP BY D.id
) s
You might need to play w/ this query a bit, but it should give you what you're looking for. Basically take your query and use it as a derived table and summarize it further using group by/having.
;with DrCounts as (
SELECT D.id,
count(DISTINCT case when A.created > DATE_SUB(NOW(), INTERVAL 1 MONTH) then A.id end) as '1month',
count(DISTINCT case when A.created > DATE_SUB(NOW(), INTERVAL 3 MONTH) then A.id end) as '1quarter',
count(DISTINCT A.id) as total
FROM dr D
JOIN answer A ON A.dr_id=D.id AND A.status=3
GROUP BY D.id)
select count(distinct D.id) as Dr1075
from DrCounts D
group by D.Id
having D.total >= 75 and D.1month >= 10
union
select count(distinct D.id) as Dr1075
from DrCounts D
group by D.Id
having D.total >= 75 and D.1quarter >= 10