MySQL How To Retrieve Table Name As A Field - mysql

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.

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

Correct approach for using related tables and possible workarounds?

I am asking this question which is to teach myself of using correct approach in a certain scenario than any how-to-code problem.
Since I am self taught student and haven't used relational tables before. With search and experiment, I have come to know the basic concept of relations and their usage but I am not sure if I am still using the correct approach while using these tables.
I do not have any official teachers so only place I can ask troubling questions is here with you guys.
For example, I have written a little code where I have 2 tables.
Table-1 is doctors which has an id (AI & Primary) and names table of varChar.
Table-2 is patient_recipts which has a doctor_name table of tinyInt
names table hold the name of the doctor
doctor_name table holds the corresponding id from doctors table
name and doctor_name are related to each other in database
Now when I need to fetch data from patient_recipts and display doctor's name, I will need to INNER JOIN doctor table, compare the doctor_name value with id in doctor table and get the name of the doctor.
The query I will use to fetch patients of a certain doctor, is something like,
$getPatList = $db->prepare("SELECT *
FROM patient_recipts
INNER JOIN doctor ON patient_recipts.doctor_name = doctor.id
WHERE dept = 'OPD' AND date_time = DATE(NOW())
ORDER BY patient_recipts.id DESC");
Now if I were to INSERT an action log entry in some other processor file, it would be something like (action and log entry),
$recipt_no = $_POST['recipt_no'];
$doctor_name = $_POST['doctor_name']; //this hold id(int) not text
$dept = $_POST['dept'];
$patient_name = $_POST['patient_name'];
$patient_tel = $_POST['patient_telephone'];
$patient_addr = $_POST['patient_address'];
$patient_age = $_POST['patient_age'];
$patient_gender = $_POST['patient_gender'];
$patient_fee = $_POST['patient_fee'];
$logged_user = $_SESSION['user_name'];
$insData = $db->prepare("
INSERT INTO patient_recipts (date_time, recipt_no, doctor_name, dept, pat_gender, pat_name, pat_tel, pat_address, pat_age, pat_fee, booked_by)
VALUES (NOW(),?,?,?,?,?,?,?,?,?,?)");
$insData->bindValue(1,$recipt_no);
$insData->bindValue(2,$doctor_name);
$insData->bindValue(3,$dept);
$insData->bindValue(4,$patient_gender);
$insData->bindValue(5,$patient_name);
$insData->bindValue(6,$patient_tel);
$insData->bindValue(7,$patient_addr);
$insData->bindValue(8,$patient_age);
$insData->bindValue(9,$patient_fee);
$insData->bindValue(10,$logged_user);
$insData->execute();
// Add Log
write_log("{$logged_user} booked OPD of patient {$patient_name} for {$doctor_name}");
OUTPUT: Ayesha booked OPD of patient Steve for 15
Now here the problem is apparent, I would need to execute the above mentioned fetch query yet again to get name of the doctor with ID comparison and bind the ID 15 to Doctor's name before calling the write_log() function.
So this is where I think my approach has been wrong altogether.
One way could be using actual doctor name in patient_recipts rather than ID
but this would i, in first place, kill the purpose of learning related tables and keys, learning design scenarios and troubleshooting.
Please help so I can understand and implement a better approach for days to come :)
Your table structure is correct, it's considered best practice to use the ID as the foreign key in other tables. If you want to include the doctor's name in the log message, you do have to do another SELECT query. A query like
SELECT name
FROM doctor
WHERE id = :doctor_id
is not very expensive.
But you can simply live with the log file only containing IDs. Look up the doctor's name later if you need to find out which doctor a particular log message is referring to.
BTW, when you use PDO, I recommend you use named placeholders (as in my example above) rather than ?. It makes the code easier to read, and if you modify the query to add or remove columns you don't have to change all the placeholder numbers.

joining two tables in my sql desn't return a result set

Hi I have run in to a problem when retrieving a particular data set using 3 tables in a MySql database.Tables are as follows
Student
SID | Name | Age | Telephone
Term
TID | Start | End
Payment
PID | TID | SID | Value
SID is primary key of Student table. TID is primary key of Term table. PID is primary key of Payment table. TID and SID in Payment table are foreign key references.
Student table contains data of students. Term table contain data of term start and end dates. Payment table contain data about student payment. Records in Payment table may either contain TID or not. When it is a registration payment there will be no TID. Otherwise it is a term fee and there will be TID. What I want is a list of students that hasn't paid this terms fees until today. Asuume this TID is in a variable. How can I obtain the list of students ? IT SEEMS SUPER EASY. BUT I COULDNT FIND AN ANSWER THIS WHOLE DAY 😣
You want a list of just those students who do not have a TID-populated record whose start and end dates are either side of today, in Payment
SELECT * FROM
student
LEFT OUTER JOIN
(select * from payment where TID is not null and NOW() BETWEEN start and end) this_term_payments
on student.id = this_term_payments.sid
WHERE
this_term_payments.ID is null
There are many ways to skin this cat. Here is one. We filter the payments table down to just a list of this term's payments (that's the inner query). And left join that to students. Left join means we get all students, matched with this_term_payments if the this_term_payments row exists, or NULL in every this_term_payments column if the term payment doesn't exist. The where clause then filters the whole results set down to "just those who don't have a term payment" by looking for those nulls that the left join creates
FWIW, your question attracted close votes because it didn't include example data/demonstrate the level of your effort we like to see on SQL questions. If you'd included sample data for all your tables and an example result set you wanted to see out, it means we can write an exact query that meets your requirements.
This is a bit of a double edged sword for me; we can deliver exactly what you ask for even if you later realise it's not what you want (asking in English is far more vague than giving an example result set) but at the same time we basically become some free outsourced homework contractor or similar, doing your work for you and removing learning opportunities along the way. Hopefully you'll take this query (it's likely it doesn't output everything you want, or outputs stuff you don't want) and craft what you want out of it now that the technique has been explained.. :)
For an SQL question that was relatively well received (by the time i'd finished editing it following up on the comments), and attracted some great answers take a look here:
Fill in gaps in data, using a value proportional to the gap distance to data from the surrounding rows?
That's more how you need to be asking SQL questions - say what you want, give example data, give scripts to help people create your same data so they can have a play with their idea without the boring bits of creating the data first. I picked on that one because I didn't even have any SQL attempts to show at the time; it was just a thought exercise. Having nothing working isn't necessarily a barrier to asking a good question
Try this:
select s.name, p.value from Student s, Term t, Payment p where t.TID = p.TID and s.SID=p.SID and p.TID is null;

Specific MYSQL view with null values

I'm having trouble creating a view for one of my MYSQL assignments. I understand how to create a view technically, as in, the commands to do so. (I have already done a few other different views for this assignment) My problem is with how to design this particular view... I don't know how to with the knowledge I have/The way I designed my tables.
So, I have 2 relevant tables(There are 2 others but I don't think they are needed for this problem): Attendance and Scholar. I need to create a view where all scholars are listed as well as the date where they were an invited speaker. However, if they were never an Invited Speaker, the date should have a null value shown. So I need to select First Name and LastName from Scholar and ADate from Attendance. Attendance has the column AttendanceType that can be either Invited Speaker or Chairman. Attendance also has the foreign key LName, relating to LastName, and ADate obviously. I can't conceptually think of have to do this, I thought that using a join, which I'm not that experienced with would be the right choice but it didn't work...
Here's what I attempted
CREATE VIEW InvitedScholars
AS SELECT FirstName,LastName,ADate
FROM Scholar LEFT JOIN Attendance ON AttendanceType='Invited Speaker'
WHERE Lname=LastName;
This only gave me Invited Speakers, not all Scholars... I don't know how to progress... any advice would be appreciated.
You need to do your left join on the Last Name (assuming this is your key on both tables). See the SQL below:
CREATE VIEW InvitedScholars
AS SELECT FirstName,LastName,ADate
FROM Scholar LEFT JOIN Attendance ON Scholar.LastName = Attendance.LName
AND Attendance.AttendanceType = 'Invited Speaker';
It appears you have your join and where clauses mixed up. You want to join the the two tables on the last name (which invites another issue if you have two speakers with the same last name) and filter by AttendanceType
FROM Scholar LEFT JOIN Attendance ON Lname=LastName
WHERE AttendanceType='Invited Speaker'

What is the best way to count rows in a mySQL complex table

I have a table with the following fields (for example);
id, reference, customerId.
Now, I often want to log an enquiry for a customer.. BUT, in some cases, I need to filter the enquiry based on the customers country... which is in the customer table..
id, Name, Country..for example
At the moment, my application shows 15 enquiries per page and I am SELECTing all enquiries, and for each one, checking the country field in customerTable based on the customerId to filter the country. I would also count the number of enquiries this way to find out the total number of enquiries and be able to display the page (Page 1 of 4).
As the database is growing, I am starting to notice a bit of lag, and I think my methodology is a bit flawed!
My first guess at how this should be done, is I can add the country to the enquiryTable. Problem solved, but does anyone else have a suggestion as to how this might be done? Because I don't like the idea of having to update each enquiry every time the country of a contact is changed.
Thanks in advance!
It looks to me like this data should be spread over 3 tables
customers
enquiries
countries
Then by using joins you can bring out the customer and country data and filter by either. Something like.....
SELECT
enquiries.enquiryid,
enquiries.enquiredetails,
customers.customerid,
customers.reference,
customers.countryid,
countries.name AS countryname
FROM
enquiries
INNER JOIN customers ON enquiries.customerid = customers.customerid
INNER JOIN countries ON customers.countryid = countries.countryid
WHERE countries.name='United Kingdom'
You should definitely be only touching the database once to do this.
Depending on how you are accessing your data you may be able to get a row count without issuing a second COUNT(*) query. You havent mentioned what programming language or data access strategy you have so difficult to be more helpful with the count. If you have no easy way of determining row count from within the data access layer of your code then you could use a stored procedure with an output parameter to give you the row count without making two round trips to the database. It all depends on your architecture, data access strategy and how close you are to your database.