issue selecting from many-to-many - mysql

my db is very simple:
CREATE TABLE Account (
accountId int NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
PRIMARY KEY (accountId)
);
CREATE TABLE Manager (
managerId int NOT NULL AUTO_INCREMENT,
name varchar(255) NOT NULL,
PRIMARY KEY (managerId)
);
CREATE TABLE ManagerAccount(
id int not null auto_increment,
managerId int not null,
accountId int not null,
primary key(id),
foreign key(managerid) references Manager (ManagerID),
foreign key(accountId) references Account (AccountID)
);
Now, when i return an account object to the user I need to pull all the account associated with specific account
So I did something like:
select m.name
from manager m
inner join ManagerAccount ma on m.managerId = ma.id
inner join Account a on ma.id = a.accountId
where a.accountId = 1;
but this does not give me the answer I want, I only get one manager name and there are 3 managers associated with accountId 1...
you can see here:

inner join ManagerAccount ma on m.managerId = ma.id
inner join Account a on ma.id = a.accountId
should be
inner join ManagerAccount ma on m.managerId = ma.managerId
inner join Account a on ma.accountId = a.accountId

In your query, you have an foreign key relationship of ManagerAccount.accountId with Account (AccountID).
But you have mapped the primary key of ManagerAccount table with Account (AccountID).
SELECT
m.name
FROM
Manager m
INNER JOIN
ManagerAccount ma ON m.managerId = ma.managerId
INNER JOIN
Account a ON ma.accountId = a.accountId
WHERE
a.accountId = 1;

Related

Mysql find the book with the highest rating for each country. If there is a tie "print" the book with the highest number of ratings

I have the following tables:
CREATE TABLE `country` (
`name` VARCHAR(60) NOT NULL,
`code` VARCHAR(3) UNIQUE NOT NULL,
PRIMARY KEY (`code`)
);
CREATE TABLE `user` (
`userId` INT UNIQUE NOT NULL AUTO_INCREMENT,
`country` VARCHAR(3) NOT NULL,
`age` INT NOT NULL,
PRIMARY KEY (`userId`),
CONSTRAINT `fk_user_country` FOREIGN KEY (`country`) REFERENCES `country`(`code`)
);
CREATE TABLE `bookRating` (
`userId` INT NOT NULL,
`isbn` VARCHAR(13) NOT NULL,
`rate` INT NOT NULL,
`date` DATE NOT NULL,
CONSTRAINT `fk_bookRating_user` FOREIGN KEY (`userId`) REFERENCES `user`(`userId`),
CONSTRAINT `fk_bookRating_book` FOREIGN KEY (`isbn`) REFERENCES `book`(`isbn`)
);
CREATE TABLE `book` (
`isbn` varchar(13) UNIQUE NOT NULL,
`bookTitle` VARCHAR(280),
`bookAuthor` VARCHAR(150),
`yearPublication` int(4),
-- `yearPublication` must be an integer because we have value less that 1901 in dataset
`publisher` VARCHAR(135),
PRIMARY KEY (`isbn`),
CONSTRAINT `publication_yea_chk` check ((`yearPublication` > -1) && (`yearPublication` < 2101))
);
As I am saying on the title I want to find the book with the highest average rating, For each country
I have tried this query:
select T1.name, T1.BookTitle, Rate
from
(
select C.Code, AVG(BR.rate) MAXRating
from `bookRating` BR
inner join `book` B on BR.isbn = B.isbn
INNER JOIN `USER` U ON BR.UserID = U.USERId
INNER JOIN `COUNTRY` C ON U.country = C.Code
group by C.Code
) T
inner join
(
select C.Code, C.name, B.BookTitle, BR.ISBN, BR.rate
from `bookRating` BR
inner join `book` B on BR.isbn = B.isbn
INNER JOIN `USER` U ON BR.UserID = U.USERId
INNER JOIN `COUNTRY` C ON U.country = C.Code
) T1 ON T.Code = T1.Code AND T.MAXRATING = T1.RATE;
I am pretty sure this works. But I want to make it like If 2 or more books have the same average rating I want the one with the highest number of ratings.
I figured that I could use an If() statement, but how could I If(... , a condition)
How could I do it?
UPDATE
I have made the database and inserted some info in db fidle:
https://www.db-fiddle.com/f/s6wKhKhxXMX1W2x9VZn9da/1
You can join the tables, aggregate by country and book to get all average ratings and use window functions MAX() and FIRST_VALUE() on the results of the aggregation to get the book with the highest average for each country:
SELECT DISTINCT c.name,
FIRST_VALUE(b.bookTitle) OVER (
PARTITION BY c.Code
ORDER BY AVG(r.rate) DESC, COUNT(*) DESC
) bookTitle,
MAX(AVG(r.rate)) OVER () AverageRating
FROM country c
INNER JOIN users u ON u.country = c.Code
INNER JOIN bookRating r ON r.UserID = u.UserID
INNER JOIN book b ON b.isbn = r.isbn
GROUP BY c.Code, b.isbn;
See the demo.

Connecting two tables using third table with foreign key in mySQL

I have three tables.
CREATE TABLE IF NOT EXISTS users (
userID int AUTO_INCREMENT,
username text NOT NULL,
password text NOT NULL,
PRIMARY KEY (userID)
);
CREATE TABLE IF NOT EXISTS schedule (
actID int AUTO_INCREMENT,
actName text NOT NULL,
actDay text NOT NULL,
actStart text NOT NULL,
actStop text NOT NULL,
PRIMARY KEY (actID)
);
CREATE TABLE IF NOT EXISTS connection (
userID int,
actID int,
FOREIGN KEY (userID) REFERENCES users(userID),
FOREIGN KEY (actID) REFERENCES schedule(actID)
);
I am using a third table (connection) to connect the first two tables (users and schedule) with FOREIGN KEY. How can I get all activities from schedule for a specific user?
If you want to search by user's name you must join all 3 tables:
select u.*, s.*
from users u
inner join connection c on c.userID = u.userID
inner join schedule s on s.actID = c.actID
where u.username = ?
If you want to search by user's id you must join only 2 tables:
select c.userID, s.*
from connection c inner join schedule s
on s.actID = c.actID
where c.userID = ?
If a user does not have any activities but you want in the results 1 row, with no activity then use left joins:
select u.*, s.*
from users u
left join connection c on c.userID = u.userID
left join schedule s on s.actID = c.actID
where u.username = ?

Find average amount paid to each doctor

Create Table Doctor(
doctorID varchar(50) Primary key,
doctorFName varchar(50),
);
Create Table Appointment(
appID varchar(50) Primary Key,
doctorID varchar(50) Foreign Key References Doctor(doctorID),
);
Create Table Payment(
paymentID varchar(50) Primary Key,
paymentAmount int,
appID varchar(50) Foreign Key References Appointment(appID),
);
paymentAmount is also known as payment for each appointment
How can I get the average payment amount of each doctor?
I tried:
SELECT d.doctorID, d.doctorFName, SUM(p.paymentAmount) as AverageDailySalary
FROM Payment p JOIN Appointment a ON p.appID = a.appID JOIN Doctor d ON a.doctorID = d.doctorID
ORDER BY d.doctorID
That's a simple JOIN..GROUP BY query :
SELECT d.doctorID,d.doctorFName,AVG(p.paymentAmount) as avg_pay
FROM Doctor d
JOIN Appoitment a
ON(d.doctorID = a.doctorID)
JOIN payment p
ON(a.appID = p.appID)
GROUP BY d.doctorID,d.doctorFName

How do I limit the result to just different results?

The database itself is about storing cocktails with their own recipes (Recipe) and ingredients (RecipeIngredient). Each user (User) has their own "pantry" (UserIngredients) in which they can store the ingredients they have at home. This query should now show them the cocktails they can mix
I've got the following query:
SELECT u.User_Name, r.Recipe_Name
FROM User u
INNER JOIN UserIngredient ui ON u.User_ID = ui.User_ID
INNER JOIN RecipeIngredient ri ON ui.Ingredient_ID = ri.Ingredient_ID
INNER JOIN Ingredient i ON ri.Ingredient_ID = i.Ingredient_ID
INNER JOIN Recipe r ON ri.Recipe_ID = r.Recipe_ID
WHERE u.User_Session = 'DgRkQztkvUhotfSf53l7ciiI8rOhKtuvoPqCTvdlBXWTn9cYxz'
and would like to know if it is possible to just get one "r.Recipe_Name" per recipe and not one for each ingredient.
My tablelayout is the following:
CREATE TABLE User
(
User_ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
User_Pass TEXT NOT NULL,
User_Name TEXT NOT NULL,
User_Surname TEXT NOT NULL,
User_Nickname TEXT,
User_EMail TEXT,
User_Session VARCHAR(50) UNIQUE,
User_Admin BOOLEAN
);
CREATE TABLE Recipe
(
Recipe_ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
Recipe_Name TEXT NOT NULL,
Recipe_Clicks INT,
Recipe_Description TEXT
);
CREATE TABLE Ingredient
(
Ingredient_ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
Ingredient_Name VARCHAR(255) UNIQUE,
Ingredient_Description TEXT
);
CREATE TABLE RecipeIngredient
(
RecipeIngredient_ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
RecipeIngredient_Amount DECIMAL(8,2) NOT NULL,
MeasuringUnit_ID INT NOT NULL,
Recipe_ID INT NOT NULL,
Ingredient_ID INT NOT NULL,
FOREIGN KEY (MeasuringUnit_ID) REFERENCES MeasuringUnit(MeasuringUnit_ID),
FOREIGN KEY (Recipe_ID) REFERENCES Recipe(Recipe_ID),
FOREIGN KEY (Ingredient_ID) REFERENCES Ingredient(Ingredient_ID)
);
CREATE TABLE UserIngredient
(
UserIngredient_ID INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
Ingredient_ID INT NOT NULL,
User_ID INT NOT NULL,
FOREIGN KEY(Ingredient_ID) REFERENCES Ingredient(Ingredient_ID),
FOREIGN KEY(User_ID) REFERENCES User(User_ID)
);
Try
SELECT u.User_Name, MAX(r.Recipe_Name)
FROM User u
INNER JOIN UserIngredient ui ON u.User_ID = ui.User_ID
INNER JOIN RecipeIngredient ri ON ui.Ingredient_ID = ri.Ingredient_ID
INNER JOIN Ingredient i ON ri.Ingredient_ID = i.Ingredient_ID
INNER JOIN Recipe r ON ri.Recipe_ID = r.Recipe_ID
WHERE u.User_Session = 'DgRkQztkvUhotfSf53l7ciiI8rOhKtuvoPqCTvdlBXWTn9cYxz'
GROUP BY u.User_Name, r.Recipe_Name
Not sure about this but it sounds like multiple ingredients will have the same recipe so just select max, which will return the only recipe name and if you group by user name + recipe name it might give you what you need.
To get the desired result using this database, try
SELECT DISTINCT u.User_Name, r.Recipe_Name
FROM User u
INNER JOIN UserIngredient ui ON u.User_ID = ui.User_ID
INNER JOIN RecipeIngredient ri ON ui.Ingredient_ID = ri.Ingredient_ID
INNER JOIN Ingredient i ON ri.Ingredient_ID = i.Ingredient_ID
INNER JOIN Recipe r ON ri.Recipe_ID = r.Recipe_ID
WHERE u.User_Session = 'DgRkQztkvUhotfSf53l7ciiI8rOhKtuvoPqCTvdlBXWTn9cYxz'
My guess is that users create Recipies, why don't you instead add User_ID to Receipe?
you can just use distinct
SELECT DISTINCT u.User_Name, r.Recipe_Name
FROM User u
INNER JOIN UserIngredient ui ON u.User_ID = ui.User_ID
INNER JOIN RecipeIngredient ri ON ui.Ingredient_ID = ri.Ingredient_ID
INNER JOIN Ingredient i ON ri.Ingredient_ID = i.Ingredient_ID
INNER JOIN Recipe r ON ri.Recipe_ID = r.Recipe_ID
WHERE u.User_Session = 'DgRkQztkvUhotfSf53l7ciiI8rOhKtuvoPqCTvdlBXWTn9cYxz'

Why is this LEFT JOIN eliminating records with nothing in the other table?

I have a MySQL Left Join problem.
I have three tables which I'm trying to join.
A person table:
CREATE TABLE person (
id INT NOT NULL AUTO_INCREMENT,
type ENUM('student', 'staff', 'guardian') NOT NULL,
first_name CHAR(30) NOT NULL,
last_name CHAR(30) NOT NULL,
gender ENUM('m', 'f') NOT NULL,
dob VARCHAR(30) NOT NULL,
PRIMARY KEY (id)
);
A student table:
CREATE TABLE student (
id INT NOT NULL AUTO_INCREMENT,
person_id INT NOT NULL,
primary_guardian INT NOT NULL,
secondary_guardian INT,
join_date VARCHAR(30) NOT NULL,
status ENUM('current', 'graduated', 'expelled', 'other') NOT NULL,
tutor_group VARCHAR(30) NOT NULL,
year_group VARCHAR(30) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (person_id) REFERENCES person(id) ON DELETE CASCADE,
FOREIGN KEY (primary_guardian) REFERENCES guardian(id),
FOREIGN KEY (secondary_guardian) REFERENCES guardian(id),
FOREIGN KEY (tutor_group) REFERENCES tutor_group(name),
FOREIGN KEY (year_group) REFERENCES year_group(name)
);
And an incident table:
CREATE TABLE incident (
id INT NOT NULL AUTO_INCREMENT,
student INT NOT NULL,
staff INT NOT NULL,
guardian INT NOT NULL,
sent_home BOOLEAN NOT NULL,
illness_type VARCHAR(255) NOT NULL,
action_taken VARCHAR(255) NOT NULL,
incident_date DATETIME NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (student) REFERENCES student(id),
FOREIGN KEY (staff) REFERENCES staff(id),
FOREIGN KEY (guardian) REFERENCES guardian(id)
);
What I'm trying to select is the first name, last name and the number of incidents for each student in year 9.
Here's my best attempt at the query:
SELECT p.first_name, p.last_name, COUNT(i.student)
FROM person p, student s LEFT JOIN incident i ON s.id = i.student
WHERE p.id = s.person_id AND s.year_group LIKE "%Year 9%";
However, it ignores any students without an incident which is not what I want - they should be displayed but with a count of 0. If I remove the left join and the count then I get all the students as I would expect.
I've probably misunderstood left join but I thought it was supposed to do, essentially what I'm trying to do?
Thanks for your help,
Adam
What you are doing is fine, you just missed off the group by clause
SELECT p.first_name, p.last_name, COUNT(i.student)
FROM person p, student s LEFT JOIN incident i ON s.id = i.student
WHERE p.id = s.person_id AND s.year_group LIKE "%Year 9%"
GROUP BY p.first_name, p.last_name;
Here's some test data
insert into person values(1, 'student', 'Alice', 'Foo', 'f','1970-01-01');
insert into person values(2, 'student', 'Bob', 'Bar', 'm','1970-01-01');
insert into student values(1,1,0,0,'', 'current','','Year 9');
insert into student values(2,2,0,0,'', 'current','','Year 9');
insert into incident values(1,1,0,0,0,'flu','chicken soup', '2008-01-08');
And here's the output of the query with the group by added to it:
+------------+-----------+------------------+
| first_name | last_name | COUNT(i.student) |
+------------+-----------+------------------+
| Alice | Foo | 1 |
| Bob | Bar | 0 |
+------------+-----------+------------------+
You could further clean up the query by making join clauses from your where clause, and grouping on the person id:
SELECT p.first_name, p.last_name, COUNT(i.student)
FROM person p
INNER JOIN student s ON(p.id = s.person_id)
LEFT JOIN incident i ON(s.id = i.student)
WHERE s.year_group LIKE "%Year 9%"
GROUP BY p.id;
Alternately, you could avoid the LEFT JOIN by using a correlated subquery:
SELECT
p.first_name
, p.last_name
, (SELECT COUNT(*) FROM incident i WHERE i.student = s.id)
FROM
person p JOIN student s on s.person_id = p.id
WHERE
s.year_group LIKE "%Year 9%"
You're using count without group by for a start, and you're mixing "where" and "on" syntax for joins.
Try this:
SELECT p.first_name, p.last_name, COUNT(i.student)
FROM person p
JOIN student s on p.id = s.person
LEFT JOIN incident i ON s.id = i.student
WHERE s.year_group LIKE "%Year 9%"
GROUP BY P.id;
Would that not be a left outer join you are looking for? I may have my terminology mixed up? Would not be the first time. But Aron's answer would work.