I have the following MySQL tables. They represent CS courses at a school and applicants to be a TA (teaching assistant) for specific courses.
I want to create a query that will print the "best" applicant for each course. The constraints for the best applicant are:
Applicants with Applicants.level = 7 are matched first.
Applicants with ApplicantsToCourses.returning = true are chosen second.
All other applicants are matched without further discrimination.
The table definitions are:
CREATE TABLE Courses (
course_number SMALLINT(3) UNSIGNED NOT NULL,
course_section SMALLINT(1) UNSIGNED NOT NULL,
name CHAR(30) NOT NULL,
instructor CHAR(30),
lab_time CHAR(30),
PRIMARY KEY(course_number, section),
FOREIGN KEY(course_number, section) REFERENCES ApplicantsToCourses(course_number, course_section)
)
CREATE TABLE Applicants (
student_id CHAR(10) NOT NULL,
name CHAR(30),
email CHAR(30),
gpa DECIMAL(4,3) UNSIGNED,
level CHAR(2),
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY(student_id),
FOREIGN KEY(student_id) REFERENCES ApplicantsToCourses(student_id),
CHECK(gpa <= 4.000)
)
CREATE TABLE ApplicantsToCourses (
student_id CHAR(10) NOT NULL,
returning BOOLEAN DEFAULT FALSE NOT NULL,
course_number SMALLINT(3) UNSIGNED NOT NULL,
course_section SMALLINT(1) UNSIGNED NOT NULL,
PRIMARY KEY(student_id, course_number, course_section),
FOREIGN KEY(student_id) REFERENCES Applicants(student_id),
FOREIGN KEY(course_number, course_section) REFERENCES Courses(course_number, course_section)
)
My attempt at a query was . . .
select a.student_id, ac.course_number, ac.course_section
from Applicants a, ApplicantsToCourses ac, Courses c
where a.student_id = ac.student_id and ac.course_number = c.course_number and ac.course_section = c.course_section
order by a.level, ac.returning desc
. . . but that certainly doesn't have the correct logic.
You can use the following pseudo code to create some temporary tables that should help you reach your final solution.
SELECT *
FROM Applicants APP
JOIN ApplicantsToCourses ATC ON ATC.student_id = APP.student_id
JOIN Courses COU ON COU.number = ATC.course_number AND COU.section = ATC.course_section
WHERE APP.level = 7
SELECT *
FROM Applicants APP
JOIN ApplicantsToCourses ATC ON ATC.student_id = APP.student_id
JOIN Courses COU ON COU.number = ATC.course_number AND COU.section = ATC.course_section
WHERE ATC.returning = true
Related
CREATE TABLE Person (#Superclass
PPS varchar(9) NOT NULL PRIMARY KEY,
fName varchar(20) NOT NULL,
lName varchar(20) NOT NULL,
DOB date NOT NULL,
gender enum('M','F') NOT NULL DEFAULT 'M',
email varchar(25) NOT NULL,
contactNo varchar(10) NOT NULL,
city varchar(10) NOT NULL,
street varchar(20) NOT NULL
);
CREATE TABLE Donor (#Subclass
PPS varchar(9) NOT NULL PRIMARY KEY REFERENCES Person(PPS),
timesOfDonations int(1) NOT NULL DEFAULT '0'
);
CREATE TABLE Doctor(#Subclass
PPS varchar(9) NOT NULL PRIMARY KEY REFERENCES Person(PPS),
speciality varchar(20) NOT NULL,
workHours int(3)
);
CREATE TABLE Health_Check (
hId int NOT NULL AUTO_INCREMENT PRIMARY KEY,
bloodPressure varchar(7) NOT NULL,
weight float(4,1) NOT NULL,
height float(4,1) NOT NULL,
heartRate int(3) NOT NULL,
temprature float(3,1) NOT NULL,
alcoholicTest enum('P','F') NOT NULL DEFAULT 'P',
dateOfCheck date,
doctorId varchar(9),
donorId varchar(9)
);
ALTER TABLE Health_Check
ADD CONSTRAINT donorH_fk FOREIGN KEY (donorId) REFERENCES Donor (PPS) ON DELETE RESTRICT ON UPDATE CASCADE,
ADD CONSTRAINT doctor_fk FOREIGN KEY (doctorId) REFERENCES Doctor (PPS) ON DELETE RESTRICT ON UPDATE CASCADE;
#List the doctors and the names of the donors they examined during the last week.
CREATE VIEW DoctorsAndDonors AS
SELECT * FROM (SELECT concat(fname, ' ', lName) AS'Doctor Name' FROM Person JOIN Doctor on Person.PPS=Doctor.PPS
JOIN Health_Check on Doctor.PPS = Health_Check.doctorId WHERE DATEDIFF(CURDATE(), dateOfCheck) BETWEEN 1 AND 7
UNION ALL
SELECT concat(fname, ' ', lName) AS'Donor Name' FROM Person JOIN Donor on Person.PPS=Donor.PPS
JOIN Health_Check on Donor.PPS = Health_Check.donorId WHERE DATEDIFF(CURDATE(), dateOfCheck) BETWEEN 1 AND 7) AS a;
The above VIEW is returning correct result but the names of doctors and donors are in one column under the label "Doctor Name".
My question is:
Is there a way that I can create two separate columns one shows the name of the doctors and the second shows the name of donors.
Thanks.
You'll need to join all of your tables together to get from the PERSON record for the Doctor to the Person record for the Donor using your Health_Check table to determine the relationship. Probably something like:
SELECT
CONCAT (DoctorPerson.fname,' ',DoctorPerson.lName) AS 'Doctor Name',
CONCAT (DonorPerson.fname,' ',DonorPerson.lName) AS 'Donor Name'
FROM Person as DoctorPerson
INNER JOIN Doctor ON DoctorPerson.PPS = Doctor.PPS
INNER JOIN Health_Check ON Doctor.PPS = Health_Check.doctorId
INNER JOIN Donor ON health_check.donorid = donor.PPS
INNER JOIN Person as DonorPerson ON Donor.PPS = DonorPerson.PPS
WHERE DATEDIFF(CURDATE(), dateOfCheck) BETWEEN 1 AND 7
I have two tables, publishers and titles.
I want to find out how many types of each book the publisher has, such as history, childrens, etc.
Here are the two tables:
CREATE TABLE publishers
(
pub_id CHAR(3) NOT NULL,
pub_name VARCHAR(20) NOT NULL,
city VARCHAR(15) NOT NULL,
state CHAR(2) ,
country VARCHAR(15) NOT NULL,
CONSTRAINT pk_publishers PRIMARY KEY (pub_id)
)ENGINE = InnoDB;
CREATE TABLE titles
(
title_id CHAR(3) NOT NULL,
title_name VARCHAR(40) NOT NULL,
type VARCHAR(10) ,
pub_id CHAR(3) NOT NULL,
pages INTEGER ,
price DECIMAL(5,2) ,
sales INTEGER ,
pubdate DATE ,
contract SMALLINT NOT NULL,
CONSTRAINT pk_titles PRIMARY KEY (title_id)
)ENGINE = InnoDB;
All I have been able to do so far is find out the total count of types(genres) of books.
SELECT COUNT(DISTINCT type)
FROM publishers AS a
INNER JOIN titles AS p
ON a.pub_id = p.pub_id;
How can I go about doing this?
You need to have a query with Group By as shown below
SELECT a.pub_id, a.pub_name, p.type, COUNT(DISTINCT p.type)
FROM publishers AS a
INNER JOIN titles AS p ON a.pub_id = p.pub_id
GROUP BY a.pub_id, a.pub_name, p.type
add group by for pub_id and pub_name to avoid two publishers having same name to be considered as one publisher
Try this out: http://sqlfiddle.com/#!9/9331e/24
SELECT p.pub_name, t.type, count(t.type)
FROM publishers AS p
INNER JOIN titles AS t on t.pub_id = p.pub_id
GROUP BY p.pub_id, t.type
I'm making a database for a unit and I need a query that selects the vet with less appointments assigned so I can assign the next appointment to him or her. I don't know how to start, but I'm pretty sure I'll have to use variables here. Those are my tables:
CREATE TABLE IF NOT EXISTS staff (
stafId MEDIUMINT UNSIGNED AUTO_INCREMENT,
stafAdd VARCHAR(150) NOT NULL,
stafConNum VARCHAR(15) NOT NULL,
stafEma VARCHAR(40) NOT NULL,
stafFirNam VARCHAR(20) NOT NULL,
stafLasNam VARCHAR(30) NOT NULL,
stafPos ENUM('nurse', 'vet') NOT NULL,
PRIMARY KEY (stafId)
) engine = InnoDB;
CREATE TABLE IF NOT EXISTS vet (
vetId MEDIUMINT UNSIGNED AUTO_INCREMENT,
FOREIGN KEY (vetId) REFERENCES staff(stafId),
PRIMARY KEY (vetId)
) engine = InnoDB;
CREATE TABLE IF NOT EXISTS appointment (
appoId MEDIUMINT UNSIGNED AUTO_INCREMENT,
appoDat DATETIME NOT NULL,
appoPetId MEDIUMINT UNSIGNED,
FOREIGN KEY (appoPetId) REFERENCES pet(petId),
appoVetId MEDIUMINT UNSIGNED,
FOREIGN KEY (appoVetId) REFERENCES vet(vetId),
PRIMARY KEY (appoId)
) engine = InnoDB;
You should start by looking up the mysql MIN() function. Follow that up with learning about JOINs and you'll be breezing through this.
You could get the vet's with the number of appointment like this:
SELECT
*
FROM
(
SELECT
vet.vetId,
COUNT(*) AS nbrOfAppointment
FROM
vet
JOIN appointment
ON vet.vetId = appointment.appoVetId
) AS tbl
ORDER BY tbl.nbrOfAppointment ASC
This Request gives you the number of appointement per vet ordered by number of appointement.
Select vet.id, count(*) as nb_appointement
from vet
inner join appointement app on vet.vetId = app.appoVetId
group by vet.id
order by nb_appointement asc
I have two tables: attendees & history. History table is where I post payments for all the attendees. It also stores an historyid and the attendeeid. The query I'm using to try to get to the last record entered in the history table for a particular attendee is:
$stmt = $db->prepare('SELECT a.fname, a.lname, h.amount, h.subsidy, h.last_payment, h.balance
FROM history AS h
INNER JOIN
attendees AS a
ON a.attendeeid = h.attendeeid
WHERE a.attendeeid = :id
ORDER BY historyid DESC LIMIT 1)');
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
$result = $stmt->fetchAll();
But the code is failing to return anything at all. Since there will likely be historyid's greater than the one I'm trying to retrieve, how do I associate the greatest historyid for that attendee so I get the last record entered for them in the History table? What I'm using is obviously not the answer. Thanks in advance for your help.
Sorry. The schemas are:
TABLE attendees (
attendeeid int(10) unsigned NOT NULL AUTO_INCREMENT,
fname varchar(20) NOT NULL,
lname varchar(20) NOT NULL,
dojid varchar(10) NOT NULL,
address1 varchar(25) NOT NULL,
address2 varchar(25) NOT NULL,
city varchar(20) NOT NULL,
state char(2) NOT NULL,
zipcode varchar(5) NOT NULL,
phonenumber varchar(15) NOT NULL,
memberid int(10) unsigned NOT NULL,
PRIMARY KEY (attendeeid),
KEY memberid (memberid),
CONSTRAINT attendees_ibfk_2 FOREIGN KEY (memberid) REFERENCES members (memberid) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
TABLE history (
historyid int(10) unsigned NOT NULL AUTO_INCREMENT,
amount float NOT NULL,
subsidy char(1) NOT NULL,
last_payment date NOT NULL,
amount_paid float NOT NULL,
balance float NOT NULL,
attendeeid int(10) unsigned NOT NULL,
memberid int(10) unsigned NOT NULL,
PRIMARY KEY (historyid),
KEY attendeeid (attendeeid),
CONSTRAINT history_ibfk_2 FOREIGN KEY (attendeeid) REFERENCES attendees (attendeeid) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Please can you provide the structure of both tables? I'm guessing your tables contains a date columns, or something like that.
Anyway, I would try something like:
select *
from (select a.attendeeid,max(h.id)
FROM history AS h INNER JOIN attendees AS a ON a.attendeeid = h.attendeeid
group by a.attendeeid) as maxHistoryPerAttendee
In that way, you will have the maximun history id per each attendeeid. I'm guessing the max id, is the last inserted row.
This is your query with a couple fields added:
select *
from (select a.attendeeid,max(h.historyid),fname,lname,last_payment,amount
FROM history AS h INNER JOIN attendees AS a ON a.attendeeid = h.attendeeid
group by a.attendeeid) as maxHistoryPerAttendee
where attendeeid = 29
In doing that, I got the first-row entry for that attendee's last_payment instead of the last_payment associated with the MAX(historyid) row.
To add to this and call it done, I got this to work. It's very near my original query.
$stmt = $db->prepare('SELECT fname, lname, amount, subsidy, last_payment, balance, a.attendeeid, h.historyid
FROM history AS h
INNER JOIN attendees AS a ON a.attendeeid = h.attendeeid
where h.attendeeid = :id
ORDER BY h.historyid DESC LIMIT 1');
$stmt->bindValue(':id', $id, PDO::PARAM_INT);
$stmt->execute();
Works like a champ!
I have redesigned my database structure to use PRIMARY and FOREIGN KEYs to link the entries in my 3 tables together, and I am having problems trying to write queries to select data in one table given data in a another table. Here is an example of my 3 CREATE TABLE statements:
CREATE TABLE IF NOT EXISTS players (
id INT(10) NOT NULL AUTO_INCREMENT,
username VARCHAR(16) NOT NULL,
uuid VARCHAR(200) NOT NULL DEFAULT 0,
joined TIMESTAMP DEFAULT 0,
last_seen TIMESTAMP DEFAULT 0,
PRIMARY KEY (id)
);
/* ^
One |
To
| One
v
*/
CREATE TABLE IF NOT EXISTS accounts (
id INT(10) NOT NULL AUTO_INCREMENT,
account_id INT(10) NOT NULL,
pass_hash VARCHAR(200) NOT NULL,
pass_salt VARCHAR(200) NOT NULL,
created BIGINT DEFAULT 0,
last_log_on BIGINT DEFAULT 0,
PRIMARY KEY (id),
FOREIGN KEY (account_id) REFERENCES players(id) ON DELETE CASCADE
) ENGINE=InnoDB;
/* ^
One |
To
| Many
v
*/
CREATE TABLE IF NOT EXISTS purchases (
id INT(10) NOT NULL AUTO_INCREMENT,
account_id INT(10) NOT NULL,
status VARCHAR(20) NOT NULL,
item INT NOT NULL,
price DOUBLE DEFAULT 0,
description VARCHAR(200) NOT NULL,
buyer_name VARCHAR(200) NOT NULL,
buyer_email VARCHAR(200) NOT NULL,
transaction_id VARCHAR(200) NOT NULL,
payment_type VARCHAR(20) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (account_id) REFERENCES accounts(account_id) ON DELETE CASCADE
) ENGINE=InnoDB;
Say for example, I want to select all the usernames of users who purchased anything greater than $30. All the usernames are stored in the players table, which is linked to the accounts table and that is linked to the purchases table. Is this this the best way to design this relational database? If so, how would I run queries similar to the above example?
I was able to get get all of a users purchase history given their username, but I did it with 2 sub-queries... Getting that data should be easier than that!
Here is the SELECT query I ran to get all of a players purchase data:
SELECT *
FROM purchases
WHERE account_id = (SELECT id FROM accounts WHERE account_id = (SELECT id FROM players WHERE username = 'username'));
Also, when I try to make references to the other tables using something like 'players.username', I get an error saying that the column doesn't exist...
I appreciate any help! Thanks!
Your design is ok in my opinion. The relation between players and account is one-to-many and not one-to-one since this way, you can have two tuples referencing a single player.
I would write the query you need as:
SELECT DISTINCT p.id, p.username
FROM players p INNER JOIN accounts a ON (p.id = a.account_id)
INNER JOIN purchases pc ON (a.id = pc.account_id)
WHERE (pc.price > 30);
As Sam suggested, I added DISTINCT to avoid repeating id and username in case a user have multiple purchases.
Note the id is here to avoid confusion among repeated usernames.