Select three different things from three diffrent tables - mysql

I wanna a query to get first_name of students and first_name of teachers which have the most courses with each other with the number of these courses.
Table Student:
CREATE TABLE Student(
id INT AUTO_INCREMENT,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255),
email VARCHAR(255) UNIQUE,
PRIMARY KEY (id)
);
Table Teacher:
CREATE TABLE Teacher(
id INT AUTO_INCREMENT,
first_name VARCHAR(255) NOT NULL,
last_name VARCHAR(255),
email VARCHAR(255) UNIQUE,
degree VARCHAR(10) NUT NULL,
PRIMARY KEY (id)
);
Table Course:
CREATE TABLE Course(
id INT AUTO_INCREMENT,
code INT NOT NULL UNIQUE,
name VARCHAR(255) NOT NULL,
st_id INT,
teach_id INT,
PRIMARY KEY (id),
FOREIGN KEY st_id REFERENCES Student (id),
FOREIGN KEY teach_id REFERENCES Teacher (id)
);
Is the below query correct? i.e. Can I use 3 SELECT in a query?
query1:
SELECT S.first_name
FROM Student AS S
INNER JOIN Course AS C
ON C.st_id = S.id
SELECT T.first_name
FROM Teacher AS T
INNER JOIN Course AS CC
ON CC.teach_id = T.id
SELECT COUNT(*)
FROM Course
WHERE Course.st_id = S.id
AND Course.teach_id = T.id
GROUP BY COUNT(*)
ORDER BY DESC;
query2:
SELECT S.first_name, T.first_name, COUNT(*)
FROM Student AS S, Teacher AS T, Course
WHERE Course.st_id = S.id
AND Course.teach_id = T.id
GROUP BY COUNT(*)
ORDER BY DESC;
If the above queries are not correct(probably the first one is wrong) guide me to correct answer, please.
NOTE: If the ordering isn't unique, order by the name of teachers first, then order by the name of the students(for clarity but not important so much to me).

Your second query is closer to being right but it has some issues. I would recommend using JOIN statements rather than implied joins. This makes the query easier to read.
Something like this should work:
SELECT t.first_name,
t.id,
s.first_name,
s.id,
COUNT(*) AS course_count
FROM Course c
JOIN Student s ON c.st_id = s.id
JOIN Teacher t ON c.teach_id = t.id
GROUP BY t.id, s.id
ORDER BY course_count DESC, t.first_name, s.first_name;
You need to add a group by in order to get your count on a per student basis. Putting the group by on the id columns rather than the name makes sure you get counts on unique students and teachers in case you have multiple records in your table with the same first name. I am also adding the id columns to the select for the same reason, but these are not necessary and can be removed without affecting the accuracy of the query.

SELECT t.first_name, t.id, s.first_name, s.id, COUNT(c.id) AS course_count
FROM course c
JOIN student s ON c.st_id = s.id
JOIN teacher t ON c.teach_id = t.id
GROUP BY t.id, s.id
ORDER BY t.first_name, s.first_name
The essential data is contained in the Courses table, with the Student and Teacher tables only required for gathering the names. This query joins the 3 tables in question, computing the count of courses shared by teachers and students.

Related

Emulating row_number in MySQL 5.7 with date condition

I am working in MySQL 5.7.34 and I have the following:
create table employees (
employee_id int not null auto_increment,
first_name varchar(100) not null,
last_name varchar(100) not null,
primary key (employee_id)
);
create table documents (
document_id int not null auto_increment,
title varchar(100) not null,
last_modified datetime not null,
employee_id int not null,
primary key (document_id),
foreign key (employee_id) references employees(employee_id)
);
I have created a db-fiddle here. The columns shown in the fiddle are only a few, but in the real data set, the documents table will have 20 or more columns and all need to show.
SQL Query:
-- simply query
select e.employee_id,
e.first_name,
e.last_name,
d.title
from employees e
inner join documents d on e.employee_id = d.employee_id;
Current Output:
employee_id
first_name
last_name
title
1
John
Doe
JD_Doc_Updated
1
John
Doe
JD_Doc
2
Mike
Anderson
MA_Doc
Desired Output:
For each employee, I want to grab the most recent document only. In MS SQL Server (and recent versions of MySQL), I could use something like ROW_NUMBER() OVER(PARTITION BY d.employee_id ORDER BY d.last_modified DESC) AS num
employee_id
first_name
last_name
title
1
John
Doe
JD_Doc_Updated
2
Mike
Anderson
MA_Doc
I am not sure how to achieve the same here.
I had a read of this question, and I don't think the problem here is the same, that other question deals with data on the same table and isn't dealing with datetime as the data type. Unless I'm missing something?
A pretty simple method uses a correlated subquery:
select e.employee_id, e.first_name, e.last_name,
d.title
from employees e join
documents d
on e.employee_id = d.employee_id
where d.last_modified = (select max(d2.last_modified)
from documents d2
where d2.employee_id = d.employee_id
);
For performance, you want an index on documents(employee_id, last_modified).
Here is a db-fiddle.
You are using an old MySQL version which doesn't feature window functions. So, to get the latest document per employee you need two steps:
get the maximum document ID or date
get the row belonging to that ID or date
Let's say "latest document" refers to the one that was last modified:
select *
from employees e
join documents d
on d.employee_id = e.employee_id
and (d.employee_id, d.last_modified) in
(
select employee_id, max(last_modified)
from documents
group by employee_id
)
order by e.employee_id;
The IN clause ensures that the joined document is in the set of latest documents. There are several other ways to write this. You can replace the IN clause with a correlated clause to refer to the employee's ID. Or you could join the tables and have a criteria that NOT EXISTS a newer document. Or you could upgrade to MySQL 8 :-)
You can use the method. The first method is the same as row-number in MSSQL
select employee_id,first_name,last_name,title
from
(select *,
#row_number:=CASE
WHEN #empl_no = employee_id
THEN
#row_number + 1
ELSE
1
END AS num,
#empl_no := employee_id EmplNumber
from
(select e.employee_id,
e.first_name,
e.last_name,
d.title
from employees e
inner join documents d on e.employee_id = d.employee_id
order by d.last_modified desc) t1,(SELECT #empl_no :=0,#row_number:=0) as t2) T
where num = 1
order by employee_id
or you can use group by then select max last_modified. finally join the documents and fetch title column.
select t1.employee_id,t1.first_name,t1.last_name,title,t2.title
from
(select e.employee_id,
max(e.first_name) as first_name,
max(e.last_name) as last_name,
max(d.last_modified ) as last_modified
from employees e
inner join documents d on e.employee_id = d.employee_id
group by e.employee_id) t1
join documents t2 on t1.last_modified = t2.last_modified

SQL query to select students who have taken all subjects from subjects table

I have two tables, student_records and subjects. I have to find out those students who have taken all the subjects (1,2,3) from the subjects table.
create table subject (ID int primary key, subject varchar(50));
insert into subject values (1,'English'),(2,'Bengali'),(3,'Math');
create table student_records(ID int primary key,student_id int , sub_id int,foreign key (sub_id)
references subject(ID));
insert into student_records values (1,1,1),(2,3,2),(3,1,3),(4,1,2),(5,2,1),(6,3,3),(7,3,1),(8,4,1);
/*select *from student_records;
select *from subject;*/
I have done with this
select student_id from student_records
group by student_id
having count(*)=(select count(*) from subject);
Is there any better query?
Not a "better" way, but I would make sure to use count distinct in this case:
SELECT
student_id
,COUNT( distinct sub_id) AS subject_cnt
FROM student_records
GROUP BY student_id
HAVING subject_cnt = SELECT COUNT(*) FROM subject

Get all Items attached to sellerId - SQL

When execute my query i just get 1 item back that i attached to the sellerId instead of 2. Does anyone know how i can say?
select the name of item and re seller for each item that belongs to the re seller. With a rating higher than 4?
Current Query:
SELECT items.name, sellers.name
FROM items
inner JOIN sellers
on items.id=sellers.id
WHERE rating > 4
ORDER BY sellerId
The query for tables inc. data:
CREATE TABLE sellers (
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR(30) NOT NULL,
rating INTEGER NOT NULL
);
CREATE TABLE items (
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR(30) NOT NULL,
sellerId INTEGER REFERENCES sellers(id)
);
INSERT INTO sellers(id, name, rating) values(1, 'Roger', 3);
INSERT INTO sellers(id, name, rating) values(2, 'Penny', 5);
INSERT INTO items(id, name, sellerId) values(1, 'Notebook', 2);
INSERT INTO items(id, name, sellerId) values(2, 'Stapler', 1);
INSERT INTO items(id, name, sellerId) values(3, 'Pencil', 2);
You've got the wrong join, here's a corrected query;
SELECT items.name, sellers.name
FROM items
inner JOIN sellers
on items.sellerId=sellers.id
WHERE rating > 4
ORDER BY sellerId
You're joining on id = id, you want sellerid = id
Notice in your table definition that item.sellerId is the field that joins to seller.id
CREATE TABLE items (
id INTEGER NOT NULL PRIMARY KEY,
name VARCHAR(30) NOT NULL,
sellerId INTEGER REFERENCES sellers(id)
);
You need to join on the correct column:
SELECT i.name, s.name
FROM items i INNER JOIN
sellers s
ON i.sellerid = s.id
----------^
WHERE rating > 4
ORDER BY i.sellerId
Note that I also introduced table aliases and qualified column names. These make a query easier to write and to read.
SELECT items.name, sellers.name
FROM items, sellers
WHERE items.sellerId = sellers.id and sellers.rating>4;
Here is the right query:
SELECT items.name as items, sellers.name as sellers
FROM sellers
INNER JOIN items
ON (sellers.id = items.sellerid)
WHERE sellers.rating > 4

Using LIKE %% in query clause with multiple values from different tables

I have a basic query that fetches values from three tables. The three tables : company, classes_by_company, and person. I have a foreign key in all tables with the name company_id. In the where clause I am using LIKE to compare with a random value the values fetched for any results that may have a match. But I am having no success in doing so. I get an error in the sql line WHERE a.id LIKE %1% OR a.company_id LIKE %3% OR c.count LIKE %1% OR p.count LIKE %1%. How can I compare all the values fetched from these three tables against a random value to see if there is a match? SQLFIDDLE
SELECT a.id, a.company_id, a.status,
c.count AS classes_per_company,
p.count AS employees_per_company
FROM company a
LEFT JOIN (SELECT company_id, COUNT(*) as count
FROM classes_by_company
GROUP BY company_id) c
ON a.company_id = c.company_id
LEFT JOIN (SELECT company_id, COUNT(*) as count
FROM person
GROUP BY company_id) p
ON a.company_id = p.company_id
WHERE a.id LIKE %1% OR a.company_id LIKE %3% OR c.count LIKE %1% OR p.count LIKE %1%
Table Schema:
CREATE TABLE company
(
id int auto_increment primary key,
company_id int,
status varchar(20)
);
CREATE TABLE classes_by_company
(
id int auto_increment primary key,
company_id int,
class_name varchar(20)
);
CREATE TABLE person
(
id int auto_increment primary key,
employee_id int,
company_id int,
person_name varchar(20)
);
LIKE has to be followed by a string, so it should be:
WHERE a.id LIKE '%1%'
OR a.company_id LIKE '%3%'
OR c.count LIKE '%1%'
OR p.count LIKE '%1%'
id is an integer column; the LIKE operator relies on a text data type e.g. nvarchar.
You could convert the id to a string e.g. WHERE CAST(a.id AS VARCHAR(10)) LIKE '%1%' ...
Please also note the single quotes around the %1%.

Sub select or group by

Hello have users table, related many-to-many with another table.
fiddle
CREATE TABLE users (
id INT NOT NULL PRIMARY KEY,
name varchar(50)
);
;
CREATE TABLE items (
id INT NOT NULL PRIMARY KEY,
name varchar(50)
);
CREATE TABLE user_items (
id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
user_id int NOT NULL,
item_id int NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (item_id) REFERENCES items(id)
)
What is the best way to display the user information + count(of the related items) ?
I tried 2 queries .. but they both have some disadvantages:
SELECT u.*,
(SELECT count(id) FROM user_items WHERE user_id = u.id) as items_count
FROM users u;
SELECT u.*,
COUNT(ui.id) as items_count
FROM users u
JOIN user_items ui ON ui.user_id = u.id
GROUP BY u.id, u.name;
The sub select query .. will become heavy when this tables become big ..(and they will).
The grouping query .. force me to put all user fields in the GROUP BY ... to have a normal result witch becomes inconvenient for real data .. and for structure updates.
What is the best practice in this scenario ?
Also what is the optimal scenario for speed optimization if the table become large ?
Actually, in MySQL you don't have to either aggregate or group by an item to include it in a grouped result set:
SELECT u.*,
COUNT(*) as items_count
FROM users u
JOIN user_items ui ON ui.user_id = u.id
GROUP BY u.id
Alternatively, you could group inside an inline view in the from clause:
SELECT u.*,
ui.items_count
FROM users u
JOIN (select user_id, COUNT(*) as items_count
from user_items
group by user_id) ui
ON ui.user_id = u.id
GROUP BY u.id
SQLFiddle here.