I have two MySQL tables memberships and member_cards. Each membership & member card can have three states.
Active = start_date <= today <= end_date
Future = today < start_date
Expired = end_date < today
Memberships table
id--------membership_number--------start_date-------------end_date
1--------**123**--------------------------------09-20-2014-----------09-20-2015
2--------**123**--------------------------------09-20-2015-----------09-20-2016
3--------**123**--------------------------------09-20-2016-----------09-20-2017
4--------**123**--------------------------------09-20-2017-----------09-20-2018
5--------**456**--------------------------------09-20-2013-----------09-20-2014
6--------**456**--------------------------------09-20-2014-----------09-20-2015
Membership cards
id--------membership_id-------------start_date-------------end_date
1--------**1**--------------------------------09-20-2014-----------05-15-2015
2--------**1**--------------------------------09-20-2014-----------09-20-2015
3--------**2**--------------------------------09-20-2015-----------05-13-2016
4--------**2**--------------------------------09-20-2015-----------09-20-2016
5--------**3**--------------------------------09-20-2016-----------09-21-2016 (past)
6--------**3**--------------------------------09-20-2016-----------05-15-2017
7--------**3**--------------------------------09-20-2016-----------09-20-2017
8--------**4**--------------------------------09-20-2017-----------05-13-2017
9--------**4**--------------------------------09-20-2017-----------09-20-2018
10-------**5**--------------------------------09-20-2013-----------05-13-2014
11-------**5**--------------------------------09-20-2013-----------09-20-2014
12------**6**--------------------------------09-20-2014-----------05-13-2015
13-----**6**--------------------------------09-20-2014-----------09-20-2015
I want to retrieve
All the active + future memberships + (if there are no active or future memberships for a particular membership number, the last expired record)
The results:
id--------membership_number--------start_date-------------end_date
3--------**123**--------------------------------09-20-2016-----------09-20-2017
4--------**123**--------------------------------09-20-2017-----------09-20-2018
6--------**456**--------------------------------09-20-2014-----------09-20-2015
Active cards + (if the membership has expired, all the cards tied to that membership )
The results:
id--------membership_id-------------start_date-------------end_date
6--------**3**--------------------------------09-20-2016-----------05-15-2017
7--------**3**--------------------------------09-20-2016-----------09-20-2017
8--------**4**--------------------------------09-20-2017-----------05-13-2017
9--------**4**--------------------------------09-20-2017-----------09-20-2018
12------**6**--------------------------------09-20-2014-----------05-13-2015
13-----**6**--------------------------------09-20-2014-----------09-20-2015
Each table contains about 200k records. I am trying to do the second query (for the member_cards) using a single MySQL query using UNION. Are there any better approaches?
As has been said your question is a bit unclear, but I've written a query that returns your 2'nd target table based on the 1'st one. Inner query returns your 1'st target.
select
b.id#,
b.membership_id,
b.start_date,
b.end_date
from membership_cards b
full outer join (
select * from
(select a.*,
max(end_date) over (partition by membership_number) max_end_date,
case when
end_date>=sysdate --replace with today or whatever
then 1 --active
else 0 --inactive
end index_active
from memberships a)
where (end_date<=max_end_date and end_date>=sysdate) or
end_date = max_end_date) c
on c.id# = membership_id
where (c.index_active = 1 and b.end_date >= sysdate) or
c.index_active = 0
Related
I have a list of workers and would like to understand how many and what % of workers each day were returning workers. I'm defining a returner as someone that worked anytime from the beginning of the year to the day before date in the specific row. The output I'm looking for:
Date, # of workers total today, # of returners, % returners
7/29 , 5, 3, 60%
7/28 , 10, 5, 50%
and so on...
For 7/29, 3 workers worked on or before 7/28 (day before).
For 7/28, 5 workers worked on or before 7/27 (day before).
I currently have a list of workers that came in for today and a list of workers that came in for all days before today, but I'm not sure how to put them together in one query.Currently the dates are hardcoded; but how do I run this query for each day in the last 2 months? I'm also not sure that the way I'm using dates is correct; is there a way better way other than using BETWEEN?
What I currently have:
List of workers for 1 day (the date should be dynamic depending on the date of the row):
select
/* date_format(CONVERT_TZ(backend_shiftgroup.ends_at,'+00:00','{UTC_OFFSET.RAW}:00'), '%X-W%v') as Week, */
backend_userprofile.name,
business.name as 'Business',
worker_id,
ends_at
from backend_shift
join backend_shiftgroup on backend_shift.shift_group_id = backend_shiftgroup.id
join backend_gigtemplate on backend_shift.gig_id = backend_gigtemplate.id
join business on backend_gigtemplate.business_id = business.id
join backend_company on backend_company.id = backend_gigtemplate.company_id
join backend_userprofile on backend_shift.worker_id = backend_userprofile.id
where is_cancelled = 0
and worker_id is not null
and ends_at BETWEEN '2020-07-28' and '2020-07-29'
group by worker_id
order by count(backend_shift.id) desc;
List of all workers before today:
select
/* date_format(CONVERT_TZ(backend_shiftgroup.ends_at,'+00:00','{UTC_OFFSET.RAW}:00'), '%X-W%v') as Week, */
count(backend_shift.id) as '# of shifts',
backend_userprofile.name,
business.name as 'Business',
worker_id,
ends_at
from backend_shift
join backend_shiftgroup on backend_shift.shift_group_id = backend_shiftgroup.id
join backend_gigtemplate on backend_shift.gig_id = backend_gigtemplate.id
join business on backend_gigtemplate.business_id = business.id
join backend_company on backend_company.id = backend_gigtemplate.company_id
join backend_userprofile on backend_shift.worker_id = backend_userprofile.id
where is_cancelled = 0
and worker_id is not null
and ends_at <= '2020-07-28'
group by worker_id
order by count(backend_shift.id) desc;
I want to SELECT data from my MySQL using PHP but the WHERE clause can be kind of confusing.
Basically i want to select the appointments that are coming up, then checking if the business is wanting to send out reminders, then checking if the user wants to receive reminders. But I want to do all that in one query.
Here are my tables:
appointments
- appointment_date
- appointment_time
- business_id
- user_id
businesses
- reminders_enabled
users
- reminders_enabled
Here are the steps of what I want to do before I select the right data:
SELECT * FROM appointments WHERE DATE(appointment_date) = CURDATE() AND appointment_time is within the next 1 hour
From the data selected above from step #1, I want to filter it. I want to SELECT * FROM businesses WHERE business_id = business_id_from_step_1 AND reminders_enabled = 1
Then I want to filter it even more. If results are found after step 2, do another select: SELECT * FROM users WHERE user_id = user_id_from_step_1 AND reminders_enabled = 1
That's it after that. How can I do that? Thanks!
Something like this should work. The JOINs do the filtering as you describe in your steps 1-3:
SELECT *
FROM appointments a
JOIN businesses b ON b.business_id = a.business_id AND b.reminders_enabled = 1
JOIN users u ON u.user_id = a.user_id AND u.reminders_enabled = 1
WHERE DATE(a.appointment_date) = CURDATE() AND
a.appointment_time BETWEEN NOW() AND NOW() + INTERVAL 1 HOUR
SELECT * FROM appointments
WHERE DATE(appointment_date) = CURDATE()
AND appointment_time BETWEEN NOW() AND NOW() + INTERVAL 1 HOUR
AND business_id in (SELECT business_id FROM businesses WHERE reminders_enabled=1)
AND user_id in (SELECT user_id FROM users WHERE reminders_enabled=1)
I'm developing a booking system and in my booking form I have a dropdown element which is returning (still) available start time slots for a booking system.
By creating a new booking the query I have created is working fine and all available start time slots are returned correctly.
QUERY :
WHERE {thistable}.id
IN (
SELECT id +3
FROM (
SELECT p1.book_date, t.*, count(p1.book_date) AS nbre
FROM fab_booking_taken AS p1
CROSS JOIN fab_booking_slots AS t
WHERE NOT ((t.heuredepart_resa < p1.book_end AND
t.heurearrivee_resa > p1.book_start))
AND DATE(p1.book_date)=DATE('{fab_booking___book_bookingdate}')
GROUP BY t.id) AS x
WHERE nbre =
(
SELECT count(p2.book_date)
FROM fab_booking_taken AS p2
WHERE p2.book_date = x.book_date
)
) ORDER BY id ASC
Please see video : booking creationg
The problem I have by using the same query by editing an existing booking the available start time slots are returned which is fine :
18:00
18:30
19:00
19:30
but not the already by the customer chosen (and in the database saved) time slot which is in my example 14:00.
Please see video : Editing booking with same query
Dropdown should be populated with the following options :
14:00
18:00
18:30
19:00
19:30
I tried to create an union query to get the already by the customer chosen start time slot and the (still) available start time slots.
QUERY :
{thistable}.id
IN (
SELECT id + 3
FROM (
SELECT p1.book_date, t.*, count(p1.book_date) AS nbre
FROM fab_booking_taken AS p1
CROSS JOIN fab_booking_slots AS t
WHERE NOT ((t.heuredepart_resa < p1.book_end
AND t.heurearrivee_resa > p1.book_start))
AND p1.book_date = DATE_FORMAT('{fab_booking___book_bookingdate}', '%Y-%m-%d')
GROUP BY t.id
) as foobar2
UNION (
SELECT id + 3
FROM (
SELECT p1.book_date, t.*, count(p1.book_date) AS nbre
FROM fab_booking_taken AS p1
CROSS JOIN fab_booking_slots AS t
WHERE ( ( t.heuredepart_resa < p1.book_end
AND t.heurearrivee_resa > p1.book_start ) )
AND t.id = '{fab_booking___book_starttime}'
AND p1.book_date = DATE_FORMAT('{fab_booking___book_bookingdate}', '%Y-%m-%d')
GROUP BY t.id
) AS x
WHERE nbre = (
SELECT count(p2.book_date)
FROM fab_booking_taken AS p2
WHERE p2.book_date = x.book_date
)
)
)
The already by the customer chosen start time slot is returned (14:00) but the other available returned start time slots are not correct.
Please see video : Editing booking with union query
I'm stuck and I have no clue how I could solve this issue, so I would appreciate some help here.
Thanks
Relevant database tables
fab_booking with the booking concerned into the video
please download the sql table
fab_booking_taken with the already existing bookings on 25 11 2016 id = 347
Please download the sql table
id 347 is the concerned booking
fab_booking_slots table which contains all possible time slots
Please download the sql table
fab_heuredepart_resa table which populate the dropdown element
Please download the sql table
I have to admit that it is daunting to try to untangle that query to understand the logic behind it but I think that the following query should return the results that you need.
{thistable}.id IN (
/*Finds booking slots where the booking slot does not overlap
with any of the existing bookings on that day,
or where the booking slot id is the same as the current slot.*/
SELECT t.id + 3
FROM fab_booking_slots AS t
WHERE t.id = '{fab_booking___book_starttime}'
OR NOT EXISTS (
Select 1
From fab_booking_taken AS p1
Where Date(p1.book_date) = Date('{fab_booking___book_bookingdate}')
And p1.book_end > t.heuredepart_resa
And p1.book_start < t.heurearrivee_resa
)
)
Order By id Asc;
I'm pretty sure that this is logically equivalent, and once expressed in a simplified form like this it's easier to see how you can get it to also return the additional time slot.
You should have a separate query to use when populating the time slots for a new booking that doesn't have an existing time slot, in which case you can just remove the single line t.id = '{fab_booking___book_starttime}' OR.
I have two tables, one is a list of firms, the other is a list of jobs the firms have advertised with deadlines for application and start dates.
Some of the firms will have advertised no jobs, some will only have jobs that are past their deadline dates, some will only have live jobs and others will have past and live applications.
What I want to be able to show as a result of a query is a list of all the firms, with the nearest deadline they have, sorted by that deadline. So the result might look something like this (if today was 2015-01-01).
Sorry, I misstated that. What I want to be able to do is find the next future deadline, and if there is no future deadline then show the last past deadline. So in the first table below the BillyCo deadline has passed, but the next BuffyCo deadline is shown. In the BillyCo case there are earlier deadlines, but in the BuffyCo case there are both earlier and later deadlines.
id name title date
== ==== ===== ====
1 BobCo null null
2 BillCo Designer 2014-12-01
3 BuffyCo Admin 2015-01-31
So, BobCo has no jobs listed at all, BillCo has a deadline that has passed and BuffyCo has a deadline in the future.
The problematic part is that BillCo may have a set of jobs like this:
id title date desired hit
== ===== ==== ===========
1 Coder 2013-12-01
2 Manager 2014-06-30
3 Designer 2012-12-01 <--
And BuffyCo might have:
id title date desired hit
== ===== ==== ===========
1 Magician 2013-10-01
2 Teaboy 2014-05-19
3 Admin 2015-01-31 <--
4 Writer 2015-02-28
So, I can do something like:
select * from (
select * from firms
left join jobs on firms.id = jobs.firmid
order by date desc)
as t1 group by firmid;
Or, limit the jobs joined or returned by a date criterion, but I don't seem to be able to get the records I want returned. ie the above query would return:
id name title date
== ==== ===== ====
1 BobCo null null
2 BillCo Designer 2014-12-01
3 BuffyCo Writer 2015-02-28
For BuffyCo it's returning the Writer job rather than the Admin job.
Is it impossible with an SQL query? Any advice appreciated, thanks in advance.
I think this may be what you need, you need:
1) calculate the delta for all of your jobs between the date and the current date finding the min delta for each firm.
2) join firms to jobs only on where firm id's match and where the calculated min delta for the firm matches the delta for the row in jobs.
SELECT f.id, f.name, j.title,j.date
FROM firms f LEFT JOIN
(SELECT firmid,MIN(abs(datediff(date, curdate())))) AS delta
FROM jobs
GROUP BY firmid) d
ON f.id = d.firmid
LEFT JOIN jobs j ON f.id = j.id AND d.delta = abs(datediff(j.date, curdate())))) ;
You want to make an outer join with something akin to the group-wise maximum of (next upcoming, last expired):
SELECT * FROM firms LEFT JOIN (
-- fetch the "groupwise" record
jobs NATURAL JOIN (
-- using the relevant date for each firm
SELECT firmid, MAX(closest_date) date
FROM (
-- next upcoming deadline
SELECT firmid, MIN(date) closest_date
FROM jobs
WHERE date >= CURRENT_DATE
GROUP BY firmid
UNION ALL
-- most recent expired deadline
SELECT firmid, MAX(date)
FROM jobs
WHERE date < CURRENT_DATE
GROUP BY firmid
) closest_dates
GROUP BY firmid
) selected_dates
) ON jobs.firmid = firms.id
This will actually give you all jobs that have the best deadline date for each firm. If you want to restrict the results to an indeterminate record from each such group, you can add GROUP BY firms.id to the very end.
The revision to your question makes it rather trickier, but it can still be done. Try this:
select
closest_job.*, firm.name
from
firms
left join (
select future_job.*
from
(
select firmid, min(date) as mindate
from jobs
where date >= curdate()
group by firmid
) future
inner join jobs future_job
on future_job.firmid = future.firmid and future_job.date = future.mindate
union all
select past_job.*
from
(
select firmid, max(date) as maxdate
from jobs
group by firmid
having max(date) < curdate()
) past
inner join jobs past_job
on past_job.firmid = past.firmid and past_job.date = past.maxdate
) closest_job
on firms.id = closest_job.firmid
I think this does what I need:
select * from (
select firms.name, t2.closest_date from firms
left join
(
select * from (
--get first date in the future
SELECT firmid, MIN(date) closest_date
FROM jobs
WHERE date >= CURRENT_DATE
GROUP BY firmid
UNION ALL
-- most recent expired deadline
SELECT firmid, MAX(date)
FROM jobs
WHERE date < CURRENT_DATE
GROUP BY firmid) as t1
-- order so latest date is first
order by closest_date desc) as t2
on firms.id = t2.firmid
-- group by eliminates all but latest date
group by firms.id) as t3
order by closest_date asc;
Thanks for all the help on this
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}
)