SQL select the available rooms by date cheking - mysql

I have in my database a table called rooms that contain the rooms information and property ,and another table called reservation table that contain the Room Reserved, FromDate and ToDate .
What i want to do is to make the user pick room size that he want to reserve and pick the date for reserving the room ,then i provide for him the available rooms depend on the Room Reservation table.
here what i did:
SELECT * FROM Rooms,Reservations WHERE
Rooms.R_Size = 'roomSize' AND ('4/19/2013' NOT
BETWEEN Reservation.FromDate AND Reservation.ToDate AND '4/19/2013'
NOT BETWEEN Reservation.FromDate AND Reservation.ToDate)
The problem its return to me duplicate's rooms and even if its between the reserved date in specific reservation but its not between reserved date in another reservation still it will return it to me.
What i want is to check if the room is reserved at the same or between a specif date and if it is i don't want it to be selected and returned at all.
Thanks.. and sorry for my poor english

There are two problems with your query. One is that there is no condition on the join between rooms and reservations, such that rooms of the correct size will be returned once for each reservation satisfying the date tests. Another problem is that your date test is wrong as it will not detect existing reservations that is completely within the date interval of the new reservation.
A query like this one should give you the result you want:
SELECT * FROM Rooms
LEFT JOIN Reservations
ON Reservations.R_Number = Rooms.Number
AND Reservations.ToDate > '4/19/2013'
AND Reservations.FromDate < '4/20/2013'
WHERE Rooms.R_Size = 'roomSize'
AND Reservations.R_Number IS NULL
It works by joining the rooms to the reservations for that room, and then selecting the rooms for which there are no reservations that conflicts with the new reservation being made.(Old reservation that ends before the new one starts, or that starts after the new one ends are no problem).

What you are doing here is a cross join. Every row from table a (Rooms) is joined with every row in table b (Reservations).
In order to make your query work, you need to specify that Rooms.Rooms_Key = Reservations.Rooms_ForignKey in your where clause (or an explicit join [inner,left,right] and specify the ON fields as they are easier to read in my opinion - explicit-vs-implicit for more info).
Once you have converted the join type, the where clause will start to give you better results, and you should be able to modify it if you still need to at that point.

Related

Where clause with multi AND & OR conditions

I got a table agenda in which the admin can make a reservation for him self or for someone else (another user). If the admin make the reservation for him self in agenda.user_id will be stored the id of admin.
In case that admin make a reservation for another person (another user) in agenda.user_id will be stored the id of the user for which the reservation will be made. The id of the admin will be stored in another column agenda.booked_user.
All the reservations are stored on agenda_users table also. agenda_users has this columns: id,agenda_id, user_id. The agenda_users.user_id it refers to agenda.user_id.
I want to retrieve all the reservations made by the admin which has made reservations for himself and for other users also.
I did a query with some AND & OR:
SELECT agenda.*
FROM agenda,agenda_users
WHERE agenda_users.agenda_id=agenda.id
AND (agenda_users.user_id=$user_id
AND agenda_users.user_id=agenda.user_id)
OR (agenda_users.user_id=agenda.user_id
AND agenda.booked_user=agenda.$user_id)
AND checkout IS NULL
AND NOW() < DATE_ADD(date_end, INTERVAL 6 HOUR) ORDER BY type ASC,date_start ASC
Cannot figure out the right solution to 'grab' all the reservations the admin has made for him self and other users.
solving the old-style-joins will leave you with this SQL:
SELECT agenda.*
FROM agenda
INNER JOIN agenda_users ON agenda_users.user_id=agenda.user_id AND agenda_users.agenda_id=agenda.id
WHERE
(agenda_users.user_id=$user_id) OR (agenda.booked_user=agenda.$user_id)
AND checkout IS NULL
AND NOW() < DATE_ADD(date_end, INTERVAL 6 HOUR) ORDER BY type ASC,date_start ASC;
This SQL is almost human-readable (and understandable). 😉
EDIT: Added extra () because AND has higher precedence than OR.
SELECT agenda.*
FROM agenda
INNER JOIN agenda_users ON agenda_users.user_id=agenda.user_id AND agenda_users.agenda_id=agenda.id
WHERE
((agenda_users.user_id=$user_id) OR (agenda.booked_user=agenda.$user_id))
AND checkout IS NULL
AND NOW() < DATE_ADD(date_end, INTERVAL 6 HOUR) ORDER BY type ASC,date_start ASC;
This is too long for a comment. So I am posting this as an answer and may adjust it, once you clarify doubts about the data model.
There is a parent table agenda and it has a child table agenda_users. So one agenda has several users. But the agenda table itself has two users, too. One is the person who made the reservation, but rather than using one column for that user, you are using sometimes one column and sometimes the other. You say that when an admin makes a reservation for another user, the admin gets stored in the column booked_user, although it's obviously not the booked user, but the booking user. I wonder whether you have understood the data model yourself, because the explanation sounds just wrong.
Then, an agenda should typically be identified by its id (hence the name), so the agenda_users should be linked via its agenda_id only. Are you sure that the user_id of the two tables must match, too? That would mean an agenda.id is unique only in combination with a user_id? It is possible, but doesn't seem likely.
Your query has some issues, too.
agenda.$user_id is probably supposed to mean $user_id only?
The parentheses are probably wrong, too, as AND has precedence over OR, so the checkout and date_end criteria will only work for the part after OR.
Then you are missing qualifiers. This doesn't make the query wrong, but makes it more difficult to read. What table do checkout and date_end belong to? I assume it's the agenda table and will write my query accordingly, because you mentioned the columns of the agenda_users table and these two columns were not among them.
You want to select data from agenda. So, do so; don't join another table. If you have criteria based on the other table, then use IN or EXISTS for the lookup. In your case, though, - but I can only guess here - it seems you don't need the agenda_users table at all.
SELECT *
FROM agenda
WHERE (user_id = $user_id OR booked_user = $user_id)
AND checkout IS NULL
AND NOW() < DATE_ADD(date_end, INTERVAL 6 HOUR)
ORDER BY type, date_start;
It is cleaner in my opinion to use UNION instead of a very complex where conditions.
Note that you know the user_id you are filtering for, therefore you don't need to join.
/* The ones for admin created by the user */
SELECT
agenda.*
FROM
agenda A
WHERE
A.user_id = $user_id
UNION ALL
/* the ones where the admin created it, but not for itself */
SELECT
agenda.*
FROM
agenda A
WHERE
A.booked_user_id = $user_id
AND A.user_id <> $user_id
Don't forget to add the rest of the where conditions to both subqueries of the union

NOT IN use in same table

im kinda new to the site, as a user atleast, been finding my answers in here for a long time :)
My problem is this, i work at a rather big hospital, where we run a mysql db containing all our employees, and now i need to make a query that can show my all employees who's employment ended during 2017. The thing is, since its a big hospital where ALOT of departments alot of the employees change jobs internally between the different departments, and everytime they do a new row in the table is created with their new employment details.
in every row for every employment, it contains their employer ID, the start date and end date, and some other info. the end date, can be either a date ofc, it can be NULL or 0000-00-00, the last 2 indicates that their imployment has no end date.
So its easy enough to do a simple search containing all users with and end date containing 2017
(SELECT * FROM table WHERE enddate LIKE '%2017%')
and it gives me all the users who has an end date in 2017, problem is ofc, that it does not account for users who might just have switched department, and has ended its employment in one department to start in another.
So what im trying to do is get all users with a 2017 enddate and then all people that has no enddate or and enddate after 2017 and then remove them from the list of people with an 2017 enddate.
i tried the following:
SELECT *
FROM users
WHERE enddate LIKE '%2017%' AND employid NOT IN
(
SELECT * FROM users WHERE enddate LIKE '%0000%'
);
i know it doesnt really over all i wanted, but at first i was really just trying to get the NOT IN to work in some way, and then after that work my way on to include the rest, but i cant even get that to work.
I tried searching for some uses of NOT IN, but it seemed to always be some very simple uses of them and always on 2 different tables.
I hope i explained it correct, as stated im still kinda new to mysql and sql in general :)
To solve the NOT IN Problem you can use this query:
SELECT * FROM users
WHERE enddate LIKE '%2017%' AND employid NOT IN
( SELECT employid FROM users WHERE ansslut LIKE '%0000%' );
Note that inside the NOT IN Select you must specify the "employid" field.
See if this work for you
You are matching for a column and the you shoul return a comparable value from the subselect related to NOT IN
eg asssuming you emp_id match with user id from you r subselect you should
SELECT *
FROM users
WHERE enddate LIKE '%2017%'
AND employid NOT IN (
SELECT user_id FROM users WHERE ansslut LIKE '%0000%'
);

Adding a query (new join) to an existing join

So I have three databases (all on one server) that I need to join tables on. Essentially I do a join across two tables to determine the identification of particular users who do a certain thing after a certain date. It works fine:
SELECT a.THING, a.ANOTHERTHING, b.IDENTIFICATION, b.RELEVANTDATE
FROM FIRSTDATABASE.TABLE a
JOIN SECONDDATABASE.TABLE b
ON a.THING = b.THING
WHERE ANOTHERTHING = '----' AND IDENTIFICATION <> 'NULL'
AND b.RELEVANTDATE > date('YYYY-MM-DD')
At present I'm also running a second query by its lonesome - this is one table, on a third database - to get all users with a certain amount of an item. It also works:
SELECT ITEM, AMOUNT, IDENTIFICATION
FROM TABLE
WHERE ITEM = '----' AND AMOUNT > '0' AND IDENTIFICATION <> 'NULL'
GROUP BY AMOUNT
I then, using the first table as my guide, use VLOOKUP so I can get the AMOUNT generated in the second query for each and every user IDENTIFICATION meeting the criteria after a certain date AND who did a certain thing, from the first query.
My question is, how would I join these two into one large query?

Check if a set of keys exists in a result set

In a simplified rental system, there are three relations:
Book: id, title, description
Copy: id, book_id
Booking: id, copy_id, rent_from, rent_until
For each book there exist many copies. If a customer rents a book, an available copy is selected and a corresponding booking record is created.
Now my problem: A customer selects a book, he/she wants to rent. The system needs to check, which copies are available for the given rental period.
What is the most elegant and fastest way to query the available copies?
Basically, step-by-step one would do the following:
Select all copies of the corresponding book
Select all bookings in the specified period of those copies
Check which copies of the book are not included in the selected bookings. If all copies are included, the book is not available for rent in the given period
Step 3 gives my a headache. How to do this in SQL? Can I probably even perform all three steps in one elegant query?
Assuming you have the bookid. You are looking for:
select c.*
from copies c
where not exists (select 1
from booking b
where b.copy_id = c.id and
b.rent_until >= #PeriodStart and b.rent_from <= #PeriodEnd
);
This is checking for overlapping time periods. There is an overlap if the booking ends after the period starts and if the booking starts before the period ends. The comparisons might be < and >, depending on whether a book can be rented on the rent_until date, or if it has to wait until the next day.

MySQL How To Retrieve Table Name As A Field

Im not sure how to exactly word my issue but I will try my best. Im trying to model a database for a driving school. I have a "timeslot" table such that activities such as lessons, tests, and registration interviews can all be linked to a given timeslot for a staff member. One of the queries I am providing is to be able to view a "timetable" of events for a staff member. I have constructed the query and it is working, however it joins data from various other tables, and I would like to see the names of those tables to know what activity the timeslot is reserved for.
ER Model
The query I perform to check the staff timetable is the following:
SELECT Timeslot.*
FROM Timeslot
LEFT JOIN Test
ON Timeslot.Timeslot_ID = Test.Timeslot
LEFT JOIN Interview
ON Timeslot.Timeslot_ID = Interview.Timeslot
LEFT JOIN Lesson
ON Timeslot.Timeslot_ID = Lesson.Timeslot
WHERE Timeslot.Date BETWEEN CURDATE() AND (CURDATE() + INTERVAL 7 DAY)
AND Timeslot.Staff = 1;
This works and shows a list of all registered timeslots for a given staff member for the next week. What I would like is a further column which would show what type of activity it is, such as "Lesson", "Interview", or "Test". As you can see, I am currently storing this as a field in the timeslot table, which means that I have to specify this every time I insert a timeslot. I will be normalising the database to 3NF and want to avoid duplication. Is there a way I model this to get the name of the table, I considered using UNIONS and many other things but could use some help.
Many thanks and apologies if this seems a bit vague.
Mike
My stab at it, if you're keeping the model you've described, would be with a Case statement, like so:
Select Timeslot.*,
Case
When Test.Timeslot Is Not Null Then 'Test'
When Interview.Timeslot Is Not Null Then 'Interview'
When Lesson.Timeslot Is Not Null Then 'Lesson'
End As ActivityType
From ...
Your query would then have an "ActivityType" column at the very end that you could use.