Query from many-to-many relationship - mysql

Student stores a list of student name and Friend stores relationship between students.
Create table Student (
id int NOT NULL AUTO_INCREMENT,
name varchar(35),
PRIMARY KEY (id)
);
insert into Student (name) values ('John');
insert into Student (name) values ('Kelly');
insert into Student (name) values ('Mary');
Create table Friend (
id_from int NOT NULL REFERENCES Student(id),
id_to int NOT NULL REFERENCES Student(id),
PRIMARY KEY (id_from, id_to)
);
insert into Friend (id_from,id_to) values (1, 3);
insert into Friend (id_from,id_to) values (1, 2);
insert into Friend (id_from,id_to) values (3, 2);
How can I query all friends of "John", for example, in MySql? The schema is here.
http://sqlfiddle.com/#!9/aeacd/1

I have created a query. In general you can join tables with itself using the relation table. What the query does is join Student with Friend with Student and then selects the entries with "John" as name in the joined table between Student.id and Friend.id_from.
The query looks like this:
SELECT *
FROM Student as s1
INNER JOIN Friend as f1 on s1.id = f1.id_from
INNER JOIN Student as s2 on s2.id = f1.id_to
WHERE s1.name = "John";
you can try it out here:
http://sqlfiddle.com/#!9/aeacd/15

Related

MySQL join dataset on at least X items

My question in a SQL Fiddle.
How can I join all elements in table A with all elements in table B via a many-to-many mapping table, even if no relationship exists? My LEFT JOIN solution isn't giving the results I expect.
Details:
Given these tables:
CREATE TABLE `user` (
id INT PRIMARY KEY NOT NULL,
email VARCHAR(100) NOT NULL
);
CREATE TABLE `event` (
id INT NOT NULL,
start_time DATETIME NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE `event_response` (
id INT PRIMARY KEY NOT NULL,
user_id INT NOT NULL,
event_id INT NOT NULL,
response VARCHAR(5) NOT NULL,
FOREIGN KEY (user_id)
REFERENCES `user`(id)
ON DELETE CASCADE,
FOREIGN KEY (event_id)
REFERENCES `event`(id)
ON DELETE CASCADE
);
And this seed data:
-- create some users
INSERT INTO `user`(`id`, email)
VALUES
(1, 'abc1#gmail.com'),
(2, 'abc2#gmail.com'),
(3, 'abc3#gmail.com');
-- create two events
INSERT INTO `event`(`id`, start_time)
VALUES
(1, '2020-09-01'),
(2, '2020-10-01');
-- Only 3 users have responded to the events
INSERT INTO `event_response`(`id`, user_id, event_id, response)
VALUES
(1, 1, 1, 'yes'),
(2, 2, 1, 'no'),
(3, 3, 2, 'yes');
I need a report like this:
start_time, email, response
"2020-09-01", abc1#gmail.com, yes
"2020-09-01", abc2#gmail.com, no
"2020-09-01", abc3#gmail.com, <NO RESPONSE>
"2020-10-01", abc1#gmail.com, <NO RESPONSE>
"2020-10-01", abc2#gmail.com, <NO RESPONSE>
"2020-10-01", abc3#gmail.com, yes
The query I have tried (but doesn't give satisfactory results):
SELECT
e.start_time,
u.email,
COALESCE(er.response, '<NO RESPONSE>') AS response
FROM `user` AS u
LEFT JOIN event_response AS er ON u.id = er.user_id
LEFT JOIN `event` AS e ON er.event_id = e.id
ORDER BY e.start_time ASC;
Use a cross join to generate the rows and left join to bring in the values:
select e.*, u.*, er.response
from event e cross join
user u left join
event_response er
on er.user_id = u.id and er.event_id = e.id;

Select all Courses, and for those in which a certain Student is enrolled, also select the Enrollment data

Classic Student-Enrollment-Course structure, but the Enrollment join table has an additional DATE column. Here's a sample fiddle: http://sqlfiddle.com/#!9/81d3e5/3.
CREATE TABLE student (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL
);
CREATE TABLE course (
id INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(255) NOT NULL
);
CREATE TABLE enrollment (
student_id INT NOT NULL,
course_id INT NOT NULL,
enrollment_date DATE NOT NULL,
PRIMARY KEY (student_id, course_id),
FOREIGN KEY (student_id) REFERENCES student(id),
FOREIGN KEY (course_id) REFERENCES course(id)
);
INSERT INTO student (name) VALUES
("Alice"),
("Bob");
INSERT INTO course (name) VALUES
("CS 101"),
("Relational Algebra"),
("Compilers");
INSERT INTO enrollment (student_id, course_id, enrollment_date) VALUES
(1, 1, '2020-01-13'),
(1, 2, '2020-02-05'),
(2, 2, '2020-02-07');
A student needs to see a list of all available courses, and if they're enrolled, the enrollment date. Here's the desired result for student ID 2:
+--------------------+-----------------+
| course_name | enrollment_date |
+--------------------+-----------------+
| CS 101 | null |
| Relational Algebra | 2020-02-07 |
| Compilers | null |
+--------------------+-----------------+
It's been a few years since I did anything more complicated than a simple left join, and I can't figure this one out.
You can create a INNER table and left join the result of the table with the course table.
Note: student table is optional I just used so that you can retrieve student information as well
SELECT
c.name course,
es.enrollment_date
FROM course c
LEFT JOIN
(SELECT course_id , e.`enrollment_date` from enrollment e , student s where e.student_id = s.id and s.id=3)
AS es on c.id = es.course_id;
SELECT course.name, t1.enrollment_date
FROM course
LEFT JOIN (SELECT * FROM enrollment WHERE enrollment.student_id = 2) AS t1
ON course.id = t1.course_id;
Use below query, left outer join will provide you your expected result
select c.name as course_name, e.enrollment_date from course c
left outer join enrollment e
on (c.id = e.student_id);
select c.name as course_name, e.enrollment_date from course c
left outer join enrollment e
on (c.id = e.student_id)
where e.student_id = 2;

Primary / foreign key; joining multiple tables using subqueries

I have a question for an assignment with 5 tables as shown below. I need to write a query with the minimum cost for each sport:
2nd column is equipment_name:
I think I need to do a bunch of joins in subqueries with the primary keys being the id columns and the foreign keys the name_id columns. Is this the right approach?
You don't need a bunch of joins; minimally this question can be solved by one join between the store_equipment_price and the sports_equipment tables - if these two are joined on equipment id then you'll effectively get rows that can give the cost of starting up in each sport per store. You'll need to group by the sport id and the store id; don't forget that it might be cheaper to start soccer by getting all the gear from store A but it might be cheaper to start golf by going to tore B - tho I how I read the question. If however you're prepared to get your gloves from store A and your bat from store B etc then we don't even group by the store when summing, instead we work out which store is cheapest for each component rather than which store is cheapest for each sport overall.
If you're after producing named stores/sports on your result rows then you'll need more joins but try getting the results right based on the fewest number of joins possible to start with
Both these queries will ultimately be made a lot easier by the use of an analytic/windowing function but these are database dependent; never post an sql question up without stating what your db vendor is, as there are few questions that are pure ISO SQL
You question is not completely clear, I assume you need to find stores from which to buy each equipment for all sports so as to incur minimum expense. Following query will achieve this
select s.sports, e.equipment_name, min(sep.price),
(select store_name from stores st where st.id = sep.store_id) store_name
from sports s
join sports_equipment se on s.id = se.sport_id
join equipment e on e.id = se.equipment_id
join sports_equipment_prices sep on sep.equipment_id = se.equipment_id
group by s.sports, e.equipment_name
order by s.sports, e.equipment_name
;
Following 'create table' and 'insert data' script are based on your screen images
create table sports (
id INTEGER PRIMARY KEY AUTOINCREMENT,
sports varchar(50)
);
insert into sports(sports) values('golf');
insert into sports(sports) values('baseball');
insert into sports(sports) values('soccer');
create table stores (
id INTEGER PRIMARY KEY AUTOINCREMENT,
store_name varchar(50)
);
insert into stores(store_name) values('A');
insert into stores(store_name) values('B');
insert into stores(store_name) values('C');
create table equipment (
id INTEGER PRIMARY KEY AUTOINCREMENT,
equipment_name varchar(50)
);
insert into equipment(equipment_name) values('shoes');
insert into equipment(equipment_name) values('ball');
insert into equipment(equipment_name) values('clubs');
insert into equipment(equipment_name) values('glove');
insert into equipment(equipment_name) values('bat');
create table sports_equipment (
sport_id INTEGER not null,
equipment_id INTEGER not null,
FOREIGN KEY(sport_id) REFERENCES sports(id),
FOREIGN KEY(equipment_id) REFERENCES equipment(id)
);
insert into sports_equipment values(1, 1);
insert into sports_equipment values(1, 2);
insert into sports_equipment values(1, 3);
insert into sports_equipment values(2, 2);
insert into sports_equipment values(2, 4);
insert into sports_equipment values(2, 5);
insert into sports_equipment values(3, 1);
insert into sports_equipment values(3, 2);
create table sports_equipment_prices (
id INTEGER PRIMARY KEY AUTOINCREMENT,
store_id INTEGER not null,
equipment_id INTEGER not null,
price INTEGER not null,
FOREIGN KEY(store_id) REFERENCES stores(id),
FOREIGN KEY(equipment_id) REFERENCES equipment(id)
);

MYSQL Not all inserted values being displayed

I should be able to show customer name, pet name, procedures, costs and total costs in one SQL SELECT statement. I am only getting one row of values, of which one of the values is NULL. I can't figure out what is wrong with my SQL statement or tables that it is causing it to not return all of the inputted values.
Here is an image of what I am getting as a result:
CREATE DATABASE IF NOT EXISTS vet;
USE vet;
CREATE TABLE IF NOT EXISTS customer (
customer_id INT PRIMARY KEY AUTO_INCREMENT,
customer_name VARCHAR(70)
);
CREATE TABLE IF NOT EXISTS invoice (
invoice_id INT PRIMARY KEY AUTO_INCREMENT,
invoice_date DATE,
customer_id INT ,
CONSTRAINT FOREIGN KEY (customer_id) REFERENCES customer(customer_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS pet (
pet_id INT PRIMARY KEY AUTO_INCREMENT,
pet_name VARCHAR(50),
customer_id INT,
CONSTRAINT FOREIGN KEY (customer_id) REFERENCES customer(customer_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS procedures (
procedure_id INT PRIMARY KEY AUTO_INCREMENT,
procedure_name VARCHAR(70),
amount DECIMAL
);
CREATE TABLE IF NOT EXISTS invoice_pet (
invoice_id INT,
pet_id INT,
CONSTRAINT FOREIGN KEY (invoice_id) REFERENCES invoice(invoice_id),
CONSTRAINT FOREIGN KEY (pet_id) REFERENCES pet(pet_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS procedures_pet (
procedure_id INT,
pet_id INT,
CONSTRAINT FOREIGN KEY (procedure_id) REFERENCES procedures(procedure_id),
CONSTRAINT FOREIGN KEY (pet_id) REFERENCES pet(pet_id)
ON DELETE CASCADE
ON UPDATE CASCADE
);
INSERT INTO invoice (invoice_date) VALUES ('2004-04-05');
INSERT INTO invoice (invoice_date) VALUES ('2014-12-05');
INSERT INTO invoice (invoice_date) VALUES ('2009-08-29');
INSERT INTO invoice (invoice_date) VALUES ('2016-07-15');
INSERT INTO customer (customer_name) VALUES ('John Garett');
INSERT INTO customer (customer_name) VALUES ('Mary Wist');
INSERT INTO customer (customer_name) VALUES ('Beth Smith');
INSERT INTO customer (customer_name) VALUES ('Rick Sanchez');
INSERT INTO pet (pet_name, customer_id) VALUES ('Rover', 1);
INSERT INTO pet (pet_name, customer_id) VALUES ('Max', 3);
INSERT INTO pet (pet_name, customer_id) VALUES ('Munchie', 4);
INSERT INTO pet (pet_name, customer_id) VALUES ('Dixon', 2);
INSERT INTO pet (pet_name, customer_id) VALUES ('Lucky', 4);
INSERT INTO procedures (procedure_name, amount) VALUES ('Rabies Vaccination', 30.00);
INSERT INTO procedures (procedure_name, amount) VALUES ('Sterilization', 190.00);
INSERT INTO procedures (procedure_name, amount) VALUES ('Dental Surgery', 120.00);
INSERT INTO procedures (procedure_name, amount) VALUES ('Cystotomy', 200.00);
INSERT INTO invoice_pet (invoice_id, pet_id) VALUES (1, 1);
INSERT INTO invoice_pet (invoice_id, pet_id) VALUES (2, 1);
INSERT INTO invoice_pet (invoice_id, pet_id) VALUES (1, 4);
INSERT INTO invoice_pet (invoice_id, pet_id) VALUES (3, 2);
INSERT INTO procedures_pet (procedure_id, pet_id) VALUES (1, 1);
INSERT INTO procedures_pet (procedure_id, pet_id) VALUES (3, 2);
INSERT INTO procedures_pet (procedure_id, pet_id) VALUES (4, 4);
INSERT INTO procedures_pet (procedure_id, pet_id) VALUES (2, 1);
SELECT inv.invoice_id, cust.customer_name, p.pet_name, pro.procedure_name,
pro.amount AS cost, SUM(amount) AS totalcost
FROM vet.procedures pro LEFT JOIN vet.procedures_pet propet
ON pro.procedure_id = propet.procedure_id
LEFT JOIN vet.pet p
ON propet.pet_id = p.pet_id
LEFT JOIN vet.invoice_pet invpet
ON p.pet_id = invpet.pet_id
LEFT JOIN vet.invoice inv
ON invpet.invoice_id = inv.invoice_id
LEFT JOIN vet.customer cust
ON inv.customer_id = cust.customer_id
The following gives each customer, their pets, procedures those pets have had (if any) and the cost of those procedures.
You don't say what you mean by "total cost" (total per customer? per pet? grand total?) so I have gone with cost per customer (see correlated sub query)
SELECT c.customer_name,
p.pet_name,
pr.procedure_name,
pr.amount,
(
SELECT SUM(amount)
FROM procedures pr1
JOIN procedures_pet pp1
ON pr1.procedure_id = pp1.procedure_id
JOIN pet p1
ON p1.pet_id = pp1.pet_id
JOIN customer c1
ON p1.customer_id = c1.customer_id
WHERE c1.customer_id = c.customer_id
) AS totalcost
FROM customer c
JOIN pet p
ON c.customer_id = p.customer_id
LEFT JOIN procedures_pet pp
ON pp.pet_id = p.pet_id
LEFT JOIN procedures pr
ON pr.procedure_id = pp.procedure_id
ORDER BY customer_name,
pet_name,
procedure_name;
The problem is that your invoices do never get any "customer_id" because you do not insert a value here in your insert statement.
So all tuples have "null" for customer_id.
Answering here as I cannot comment yet:
Of course you'll have to add your customers to the database first in this case. Otherwise they cannot be referenced.

How to solve this specific SQL query

I have post 3 Tables structure like this Course,Students,CourseAllot with Data here i need how to multiple row combine single row.How to show the based on the name concatenate and show them
Create Table Course
(
CourseId int Primary key Identity(1,1),
CourseName Varchar(50)
)
Insert into Course values('C#')
Insert into Course values('Asp.net')
Insert into Course values('Sqlserver')
Insert into Course values('MySql')
Create Table Students
(
StudentId int Primary key identity(1,1),
StudentName varchar(30)
)
Insert into Students values('John')
Insert into Students values('David')
Insert into Students values('Hendry')
Insert into Students values('Smith')
Insert into Students values('Watson')
Create Table CourseAllot
(
AllotId int Primary key identity(1,1),
CourseId int,
StudentId int
)
Insert into CourseAllot values (1,1)
Insert into CourseAllot values (1,1)
Insert into CourseAllot values (2,1)
Insert into CourseAllot values (1,2)
Insert into CourseAllot values (3,4)
Insert into CourseAllot values (3,5)
I need Output this
Sno Course Name Student Name
1 C# John,Hendry,David
2 Asp.net John
3 Sqlserver Smith,WatSon
if you are in sqlserver use bellow query
with cte(courseName,c)
as
(select courseName,c= studentname from CourseAllot
inner join Course on CourseAllot.CourseId=Course.CourseId
inner join Students on Students.StudentId=CourseAllot.StudentId
group by courseName,studentname
)
select ROW_NUMBER() over(order by courseName) as rowno, courseName,
stuff((select ','+c from cte t where t.courseName=t2.courseName
for xml path('')),1,1,'') as StudentName from cte t2 group by t2.courseName