What's wrong with this mySQL logic? - mysql

In table "Booking" I hold details of a customer's booking (date/time/RowNumber).
In table "Seat" I hold details of all seats (RowNumber and Zone) in the theater.
The purpose of this query is to join Seat to Booking to see which seats are taken by displaying those rows in Seat which have a null entry in the corresponding Booking RowNumber. Here is the code:
SELECT s.Zone, s.RowNumber
FROM Booking b JOIN Seat s
ON s.RowNumber = b.RowNumber
WHERE b.PerfDate = '2016-12-12'
AND b.PerfTime = '20:30:00'
AND b.RowNumber is null;
The code is accepted but the result comes back as an empty set with the last AND statement or just shows what is in bookings without it. There is clearly something wrong with the logic I am using but I cannot pinpoint it as what I am trying to do makes sense when I read it. Probably an obvious mistake but some help would be appreciated.
Thank you in advance.
[Edit - I have spotted the logic error... it is the fact that I am asking for specific date and time when the records I want don't have those by definition but no idea how to get round it, seems like catch 22.]

You need to use a LEFT JOIN, and put all the criteria for the non-matching row into the ON clause.
SELECT s.zone, s.RowNumber
FROM Seat AS s
LEFT JOIN Booking AS b
ON s.RowNumber = b.RowNumber AND b.PerfDate = '2016-12-12' AND b.PerfTime = '20:30:00'
WHERE b.RowNumber IS NULL

Related

Correct mySQL query for Student attendance entry

I'm trying to develop a student attendance form using Codeigniter & MySQL. My table structure for the two tables are as follows:
student_mst table
-----------------
s_id(pk)
s_name
s_acssn_id
s_prog_id
s_admission_no
s_photo_name
stu_attendance table
--------------------
sa_id(pk)
sa_s_id(fk)
sa_s_acssn_id
sa_s_prog_id
sa_atten_dt
sa_atten_code(A=Absent,P=Present)
sa_teacher_id
The design of my interface is as shown in the image below:
My plan is, on any particular date, a teacher can mark a student as either Present or Absent from the Action buttons for which ajax code is there which inserts rows in the above-mentioned stu_attendance table. And also every new day when the attendance entry form is opened, the Datatable plugin should open just like as shown in the image.
In order to accomplish my plans, I'm populating a Datatable plugin using the following query:
SELECT a.s_id AS sid,a.s_prog_id AS progid,a.s_acssn_id AS ssnid,a.s_name AS sname,a.s_admission_no AS sadmsnno,a.s_photo_name AS sphotonm,
b.sa_atten_code AS acode
FROM student_mst a
LEFT JOIN stu_attendance b
ON a.s_id = b.sa_s_id
The above SQL query works well for the first time (i.e. when stu_attendance table is empty)
and populates the Datatable plugin as shown in the above image. I'm able to mark students as Present/Absent.
However, my plan fails the next day, because as per the query written above, it also picks and shows records of the students from the previous day which is not desired.
I hope I have made my problems clear. Anyone, please guide me. Should I change my table structures or should I change my design interface or should change my SQL query. Please help maybe with some code/examples.
Add a date check to the join so it only shows the current day's attendance.
SELECT a.s_id AS sid,a.s_prog_id AS progid,a.s_acssn_id AS ssnid,a.s_name AS sname,a.s_admission_no AS sadmsnno,a.s_photo_name AS sphotonm,
b.sa_atten_code AS acode
FROM student_mst a
LEFT JOIN stu_attendance b
ON a.s_id = b.sa_s_id AND b.sa_atten_dt = CURRENT_DATE()
use a where condition at the end of your query.
for example:
where date like '%19%August%';
Use following query, it should work
SELECT a.s_id AS sid,a.s_prog_id AS progid,a.s_acssn_id AS ssnid,a.s_name AS
sname,a.s_admission_no AS sadmsnno,a.s_photo_name AS sphotonm,
b.sa_atten_code AS acode
FROM student_mst a
LEFT JOIN stu_attendance b
ON a.s_id = b.sa_s_id AND b.sa_atten_dt >= date_format(curdate(),'%Y-%m-%d %H:00:00')

Join two SQL Queries based on first query answer

I have been looking everywhere and this would be the best website to ask someone for the help. I have to make SQL query that checks the total amount of bookings for the specific flight and then based on the number of bookings the system should provide the choice of an aircraft. First query works and it finds total number of bookings and i think i have the case statement right to choose an aircraft but i cant find the way of physically joining both queries , i tried to use unison , inner join and nested queries but it appears that Total number of seats booked (the answer from first query ) cannot be found please help me guys.
First SQL Query(find total number of bookings )
SELECT count(bookingdetails.FlightID)AS TotalNumberOfSeatsBooked,flightdetails.FlightID
FROM bookingdetails, bookingdetails AS TEMP,flightdetails
WHERE bookingdetails.BookingID = TEMP.BookingID
AND bookingdetails.FlightID= flightdetails.FlightID
Group BY FlightID;
SECOND SQL Query(Choose an aircraft type depending on how many bookings are made)
SELECT CASE chooseaircraft
WHEN TotalNumberOfSeatsBooked <= 110 THEN 'BA 146-200'
ELSE'Embraer 170'
END AS ChoiceOfAircraft
FROM aircrafttype;
Big Thanks to everyone
After one answer i think im heading in the right direction with merging the both queries together , the code now displays the total number of seats and flight number in the sub query but the choice of aircraft column still doesnt show but it does if you run the query by it self i know i am close to getting this and i would appreciate any help to become better in SQL the code i have now is :
SELECT count(bookingdetails.FlightID)AS TotalNumberOfSeatsBooked,flightdetails.FlightID
FROM bookingdetails, bookingdetails AS TEMP,flightdetails
WHERE bookingdetails.BookingID = TEMP.BookingID
AND bookingdetails.FlightID= flightdetails.FlightID
AND bookingdetails.FlightID= flightdetails.FlightID IN(
SELECT CASE WHEN count(bookingdetails.FlightID) <= 110 THEN 'BA 146-200'
ELSE'Embraer 170'
END AS ChoiceOfAircraft
FROM bookingdetails,flightdetails)
Group BY FlightID;
You can use the same expression count(bookingdetails.FlightID) in your CASE statement (or) wrap your first query in a subquery and access the column in your outer query. That is
CASE WHEN count(bookingdetails.FlightID) <= 110 THEN 'BA 146-200'
ELSE'Embraer 170'
END AS ChoiceOfAircraft

Join error and order by

I'm trying to write a query which does the below:
For every guest who has the word “Edinburgh” in their address show the total number of nights booked. Be sure to include 0 for those guests who have never had a booking. Show last name, first name, address and number of nights. Order by last name then first name.
I am having problems with making the join work properly,
ER Diagram Snippet:
Here is my current (broken) solution:
SELECT last_name, first_name, address, nights
FROM booking
RIGHT JOIN guest ON (booking.booking_id = guest.id)
WHERE address LIKE '%Edinburgh%';
Here is the results from that query:
The query is partially complete, hoping someone can help me out and create a working version. I'm currently in the process of learning SQL so apologies if its a rather basic or dumb question!
Your query seems almost correct. You were joining the booking id with guets id which gave you some results because of overlapping (matching) ids, but this most likely doesn't correspond to the foreign keys. You should join on guest_id from booking to id from guest.
I'd add grouping to sum all booked nights for a particular guest (assuming that nights is an integer):
SELECT g.last_name, g.first_name, g.address, SUM(b.nights) AS nights
FROM guest AS g
LEFT JOIN booking AS b ON b.guest_id = g.id
WHERE g.address LIKE '%Edinburgh%'
GROUP BY g.last_name, g.first_name, g.address;
Are you sure that nights spent should be calculated using nights field? Why can it be null? If you'd like to show zero for null values just wrap it up with a coalesce function like that:
COALESCE(SUM(b.nights), 0)
Notes:
Rewriten RIGHT JOIN into LEFT JOIN, but that doesn't affect results - it's just cleaner for me
Using aliases eg. AS g makes the code shorter when specifying joining columns
Reference every column with their table alias to avoid ambiguity
SELECT g.first_name,
g.last_name,
g.address,
COALESCE(Sum(b.nights), 0)
FROM booking b
RIGHT JOIN guest g
ON ( b.guest_id = g.id )
WHERE address LIKE 'edinburgh%'
GROUP BY g.last_name,
g.first_name,
g.address;
This post answers your questions about how to make the query.
MySQL SUM with same ID
You can simply use COALESCE as referenced here to avoid the NULL Values
How do I get SUM function in MySQL to return '0' if no values are found?

mysql query finding results using joined status table that needs EXIST and NOT EXIST results

I have been looking around for ages for a solution to my problem.
I have something that works but i am not sure it is the most efficient way of doing things and can't find anyone trying to do this when googling around.
I have a table with customers and a table with statuses that that customer has had.
If I want to find results where a customer has had a status happen I have managed to get the required results using a join, but sometimes I want to be able to find clients where not only has a status been reached but also where a few other statuses haven't been.
Currently I am doing this with a NOT EXISTS Sub query but it seem a bit slow and thinking about it if I have to check after finding a result that matches the first status through all the results again to see if it doesn't match another it could explain the slowness.
for instance a client could have a status of invoiced and a status of paid.
If I wanted to see which clients have been invoiced thats fine, If I want to see which clients have been invoiced and paid thats fine, but if I wanted to see which clients have been invoiced but NOT paid thats where I start having to use a NOT EXIST subquery
Is there another more efficient way around this? or is this the best way to proceed but I need to sort out how mysql uses indxes with these tables to be more efficient?
I can provide more detail of the actual sql if that helps?
Thanks
Matt
If this is over multiple clients then the usual solution would be to have a subselect for the status per client and then use LEFT OUTER JOIN to connect this.
Something like
SELECT *
FROM Clients a
LEFT OUTER JOIN (SELECT ClientId, COUNT(*) FROM ClientsStatus WHERE Status IN (1,2) GROUP BY ClientId) b
ON a.ClientId = b.ClientId
WHERE b.ClientId IS NULL
This (very rough) example is to give you a list of clients who do not have a status of 1 or 2.
You should be able to expand this basic idea to cover the scenarios / data you are dealing with
Edited for below
I have had a play with your SQL. I think you can use a JOIN onto the subselect fairly easily, but this doesn't seem to be checking anything other than whether a claim has had a status of 3 or 95.
SELECT claims.ID, claims.vat_rate, claims.com_rate,
claims.offer_val, claims.claim_value, claims.claim_ppi_amount, claims.claim_amount, claims.approx_loan_val, claims.salutationsa, claims.first_namesa, claims.last_namesa,
clients.salutation, clients.first_name,clients.last_name, clients.phone, clients.phone2, clients.mobile, clients.dob,clients.postcode, clients.address1, clients.address2, clients.town, client_claim_status.person,clients.ID
AS client_id,claims.ID AS claim_id, claims.date_added AS status_date_added,client_claim_status.date_added AS last_client_claim_status_date_added,work_suppliers.name AS refname, financial_institution.name AS lendname, clients.date_added AS client_date_added,ppi_claim_type_2.claim_type AS ppi_claim_type_name
FROM claims
RIGHT JOIN clients ON claims.client_id = clients.ID
RIGHT JOIN client_claim_status
ON claims.ID = client_claim_status.claim_id
AND client_claim_status.deleted != 'yes'
AND ((client_claim_status.status_id IN (1, 170))
AND client_claim_status.date_added < '2012-12-02 00:00:00' )
LEFT OUTER JOIN (SELECT claim_id FROM client_claim_status WHERE status_id IN (3, 95 )) Sub1
ON claims.ID = Sub1.claim_id
LEFT JOIN financial_institution ON claims.claim_against = financial_institution.ID
LEFT JOIN work_suppliers ON clients.work_supplier_id = work_suppliers.ID
LEFT JOIN ppi_claim_type_2 ON claims.ppi_claim_type_id = ppi_claim_type_2.ID
WHERE claims.deleted != 'yes'
AND Sub1.claim_id IS NULL
ORDER BY last_client_claim_status_date_added DESC
I would suggest that you rearrange the code to remove the RIGHT OUTER JOINs though to be honest. Mixing left and right joins up tend to be very confusing.

Mysql - Summing counts of multiple joined tables

I will do my best to explain this clearly. I am trying to accomplish two things, but am having trouble even getting the first to work correctly.
I have a schema that has a member table which has foreign keys to multiple tables. In the end I am going to be drawing from about 10 tables that may or may not have records for a particular member. I am trying to get the sum of all the counts. My query looks like this:
SELECT (COUNT(tb1.member_id) + COUNT(tb2.member_id)) as total
FROM members m
LEFT JOIN table_1 tb1 ON tb1.member_id = m.member_id
LEFT JOIN table_2 tb2 ON tb2.member_id = m.member_id
WHERE m.member_id = 27
Where 27 is the member_id of the test account I am working with. This doesn't produce accurate results and I believe it is because of the left join, it seems to be throwing things off and I am getting a total of 8 even though there are only two of each kind of record. If I eliminate one of the left joins then I get the expected result.
Could anyone tell me how I should go about doing this?
That is part one of my problem. The second issue is that in some of these cases I will want each result to count as either 1 or 0, that is even if there are 2 or 3 corresponding records. I was looking for something like casting a result as a bool but have not found anything. Could anyone suggest a way to do this?
Thanks much for reading, any advice would be very much appreciated. It could be that I am approaching this problem in the wrong way, again any advice is appreciated.
Eventhough i am not familiar with state of the art of mysql i am pretty sure something like this will work:
SELECT
(select COUNT(*) from table_1 = where member_id = m.member_id)
+
(select COUNT(*) from table_2 = where member_id = m.member_id)
as total
FROM members m
WHERE m.member_id = 27