Using UNION with JOIN in Inheritance Case - MySQL - mysql

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

Related

show the names of all suppliers that appear more than once in the supplies table

I have 2 tables supplies and supplier, i need to print names of all suppliers that appear more than once in the supplies table I tried use the follow code:
SELECT name FROM SUPPLIER
WHERE supplier.supplierNum = supplies.supplerNum
having count(supplierNum) > 1;
but the code i=didn't work.
here its the tables:
CREATE TABLE supplies (
supplierNum CHAR(2) NOT NULL,
partNum CHAR(2) NOT NULL,
quantity SMALLINT(6) NOT NULL,
PRIMARY KEY (supplierNum, partNum),
FOREIGN KEY (supplierNum) REFERENCES supplier (supplierNum),
FOREIGN KEY (partNum) REFERENCES parts (partNum)
);
CREATE TABLE supplier (
supplierNum CHAR(2) NOT NULL,
name CHAR(10) NOT NULL,
status TINYINT(4) NOT NULL,
city VARCHAR(10) NOT NULL,
PRIMARY KEY (supplierNum)
);
Count the occurrence in supplies table first then join it with supplier:
SELECT sp.name
FROM supplier AS sp
INNER JOIN
(
SELECT distinct supplierNum
FROM supplies
GROUP BY supplierNum
HAVING COUNT(supplierNum) > 1
) as cnt
ON sp.supplierNum = cnt.supplierNum;
Demo in db<>fiddle here

Didnt display results in database

I have a little problem with one database. I have already entered data in the individual tables in the database. The problem is that with this code, it displays the column names, but didnt return rows. I can't find the error. I think the problem is in JOIN itself. Any ideas for solving the problem?
SELECT cars.brand,
cars.model,
cars.yearofproduction,
cars.engine_type,
parts.part_name,
parts.price AS MONEY,
parts.quantity
FROM CATALOG
JOIN parts
ON parts.part_name = parts.id
JOIN cars
ON CATALOG.car_id = cars.id
WHERE quantity >= '0'
HAVING MONEY < (
SELECT AVG(price)
FROM cars
);
And here the tables. I've already insert values in the tables.
CREATE TABLE CATALOG.parts
(
id INT AUTO_INCREMENT PRIMARY KEY,
part_name VARCHAR(255) NOT NULL,
price DECIMAL NOT NULL,
DESCRIPTION VARCHAR(255) DEFAULT NULL,
quantity TINYINT DEFAULT 0
);
CREATE TABLE CATALOG.cars
(
id INT AUTO_INCREMENT PRIMARY KEY,
brand VARCHAR(255) NOT NULL,
model VARCHAR(255) NOT NULL,
yearofproduction YEAR NOT NULL,
engine_type SET('Diesel', 'A95', 'Gas', 'Metan')
);
CREATE TABLE CATALOG.catalog
(
part_id INT NOT NULL,
CONSTRAINT FOREIGN KEY(part_id) REFERENCES parts(id)
ON DELETE RESTRICT ON UPDATE CASCADE,
car_id INT NOT NULL,
CONSTRAINT FOREIGN KEY(car_id) REFERENCES cars(id)
ON DELETE RESTRICT ON UPDATE CASCADE,
PRIMARY KEY(part_id, car_id)
);

Display unmatched data along with aggregate functions and multiple joins

So, what i have is a system using MySQL for storage that should be storing donations made by people (donators). Donation is entered into system by authorized user.
Here are create tables for all 4 tables:
CREATE TABLE `donator` (
`DONATOR_ID` int(11) NOT NULL AUTO_INCREMENT,
`DONATOR_NAME` varchar(50) NOT NULL,
`STATUS` char(1) COLLATE NOT NULL DEFAULT 'A',
PRIMARY KEY (`DONATOR_ID`)
)
CREATE TABLE `user` (
`USER_ID` int(11) NOT NULL AUTO_INCREMENT,
`USERNAME` varchar(100) NOT NULL,
`PASSWORD` varchar(200) NOT NULL,
`TYPE` char(1) COLLATE NOT NULL,
PRIMARY KEY (`USER_ID`)
)
CREATE TABLE `sif_res` (
`RES_ID` int(11) NOT NULL AUTO_INCREMENT,
`RES_NAME` varchar(50) NOT NULL,
`MON_VAL` double NOT NULL,
PRIMARY KEY (`RES_ID`)
)
CREATE TABLE `donations` (
`DONATION_ID` int(11) NOT NULL AUTO_INCREMENT,
`RESOURCE` int(11) NOT NULL,
`AMOUNT` int(11) NOT NULL,
`DONATOR` int(11) NOT NULL,
`ENTRY_DATE` datetime NOT NULL,
`ENTERED_BY_USER` int(11) NOT NULL,
PRIMARY KEY (`DONATION_ID``),
KEY `fk_resurs` (`RESOURCE``),
KEY `fk_donator` (`DONATOR``),
KEY `fk_user` (`ENTERED_BY_USER``),
CONSTRAINT `fk_1` FOREIGN KEY (`DONATOR`) REFERENCES `donator` (`DONATOR_ID`) ON UPDATE CASCADE,
CONSTRAINT `fk_2` FOREIGN KEY (`RESOURCE`) REFERENCES `sif_res` (`RES_ID`) ON UPDATE CASCADE,
CONSTRAINT `fk_3` FOREIGN KEY (`ENTERED_BY_USER`) REFERENCES `user` (`USER_ID`) ON UPDATE CASCADE
)
As you can see, I have a list of donators, users and resources that can be donated.
Now, I want to display all donators' name and their id's, however in third column I would like to display their balance (sum of all of items they donated) - this is calculated with
donation.AMOUNT * sif_res.MON_VAL
for each donation
The SQL SELECT I have written works, however donators that haven't donated anything are left out (they are not matched by JOIN). I would need that it displays everyone (with STATUS!=D) even if they don't have any entries (in that case their balance may be 0 or NULL)
This is my SQL i have written:
SELECT DONATOR_ID
, DONATOR_NAME
, round(SUM(d.AMOUNT * sr.MON_VAL)) as BALANCE
from donator c
join donations d on c.DONATOR_ID=d.DONATOR
join sif_res sr on sr.RES_ID=d.RESOURCE
where c.STATUS!='D'
group by DONATOR_ID, DONATOR_NAME
So, if i execute next sentences:
INSERT INTO donator(DONATOR_NAME, STATUS) VALUES("John", 'A'); //asigns id=1
INSERT INTO donator(DONATOR_NAME, STATUS) VALUES("Willie", 'A'); //asigns id=2
INSERT INTO user (USERNAME, PASSWORD, TYPE) VALUES("user", "pass", 'A'); //asigns id=1
INSERT INTO sif_res(RES_NAME, MON_VAL) VALUES("Flour", "0.5"); //asigns id=1
INSERT INTO donations(RESOURCE, AMOUNT, DONATOR, ENTRY_DATE, ENTERED_BY_USER) VALUES(1, 100, 1, '2.2.2017', 1);
I will get output (with my SELECT sentence above):
DONATOR_ID | DONATOR_NAME | BALANCE
--------------------------------------------
1 | John | 50
What i want to get is:
DONATOR_ID | DONATOR_NAME | BALANCE
--------------------------------------------
1 | John | 50
2 | Willie | 0
I have tried all version of joins (left, right, outer, full,..) however none of them worked for me (probably because i was using them wrong)
If it was just the problem of unmatched data i would be able to solve it, however the aggregate function SUM and another JOIN make it all more complicated
Using a left outer join on the second two tables should do the trick:
SELECT c.DONATOR_ID
, c.DONATOR_NAME
, ifnull(round(SUM(d.AMOUNT * sr.MON_VAL)),0) as BALANCE
from donator c
left outer join donations d on c.DONATOR_ID=d.DONATOR
left outer join sif_res sr on sr.RES_ID=d.RESOURCE
where c.STATUS!='D'
group by DONATOR_ID, DONATOR_NAME
I also wrapped the BALANCE expression in ifnull to display 0 instead of null.

How Can I Find Last Entered Record

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!

Choosing applicants for positions via a MySQL query

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