SQL get duplicate records for all the users - sql

I have these tables
CREATE TABLE subject
(
id int auto_increment primary key,
name varchar(20)
);
INSERT INTO subject
(name)
VALUES
('subject 1'),('subject 2'), ('subject 3');
CREATE TABLE course
(
id int auto_increment primary key,
name varchar(20),
subject_id int,
FOREIGN KEY (subject_id) REFERENCES subject(id)
);
INSERT INTO course
(subject_id, name)
VALUES
(1, 'course 1'),(1, 'course 2'), (2, 'course 3'), (3, 'course 4');
CREATE TABLE user
(
id int auto_increment primary key,
name varchar(20),
course_id int,
FOREIGN KEY (course_id) REFERENCES course(id)
);
INSERT INTO user
(course_id, name)
VALUES
(1, 'User 1'),(1, 'User 1'), (2, 'User 3'), (3, 'User 4');
I want to get a list of users who belong to the course which belongs to the same subject.
I ran this query to get the list
select u.name as user_name, s.name as subject_name from user u
inner join course c on u.course_id = c.id
inner join subject s on c.subject_id = s.id;
And this is the output
USER_NAME SUBJECT_NAME
User 1 subject 1
User 1 subject 1
User 3 subject 1
User 4 subject 2
Expected output
USER_NAME SUBJECT_NAME
User 1 subject 1
User 1 subject 1
how to I select only user 1 who has a course that belongs to the same subject ??
fiddle link

To get the List of users who have got more than 1 course enrolled, use below query in the fiddle:
select u.name as user_name, s.name as subject_name from user u
inner join course c on u.course_id = c.id
inner join subject s on c.subject_id = s.id
having count(1) > 1;
To Get only distinct users and courses, use below query :
select distinct u.name as user_name, s.name as subject_name from user u
inner join course c on u.course_id = c.id
inner join subject s on c.subject_id = s.id;
EDIT: I assume you need the number of distinct users who have taken the subject for which you can use below query:
select s.name as subject_name, count(distinct(u.name)) as no_of_users from user u
inner join course c on u.course_id = c.id
inner join subject s on c.subject_id = s.id
group by s.name;
By using just count(u.name) as no_of_users You would get the actual count rather than distinct count.
EDIT AGAIN
To get the required output, the below query works!
select tab_a.user_name, tab_a.subject_name from
(
select u.name as user_name, s.name as subject_name from user u
inner join course c on u.course_id = c.id
inner join subject s on c.subject_id = s.id
)tab_a
inner join
(
select u.name as user_name, s.name as subject_name, count(1) reccount from user u
inner join course c on u.course_id = c.id
inner join subject s on c.subject_id = s.id
group by u.name, s.name
) tab_b
on tab_a.user_name = tab_b.user_name
where tab_b.reccount >1

Related

Join the same table based on a same column

I have two tables users and articles. The modified_by column in the Books table holds the corresponding users id who modified it. The user_id column in the Books table is the author. Both user_id and modified_by of the books table look to the id in the users table.
create table USERS (id int, name varchar(55));
insert into USERS values
( 1, 'person1'),
( 2, 'person2');
create table BOOKS (id int, user_id int, modified_by int);
insert into BOOKS values
(1, 2, 2),
(2, 2, 2),
(3, 2, 2),
(4, 1, 2);
My task is to display the books with name of the user in both the modified_by and user_id columns after joining. So far I have joined and got the name in the user_id column. How can I do the same for the modified_by column as it currently just shows the id and not the name
Current query
SELECT
books.id,
books.user_id,
users.name,
FROM
books
INNER JOIN users ON
books.user_id = users.id
Result required:
(1, person2, person2),
(2, person2, person2),
(3, person2, person2),
(4, person1, person2);
You can use join the users table twice as
SELECT b.id, u1.name AS user_id, u2.name AS modified_by
FROM books b
JOIN users u1
ON b.user_id = u1.id
JOIN users u2
ON b.modified_by = u2.id
Demo
Indeed, it's better to use LEFT OUTER JOIN instead of INNER JOIN if existence of non-matching values among tables is the case
You can join the users table twice :
SELECT
b.id,
u1.name as `user`,
u2.name as modified_by
FROM
BOOKS b
INNER JOIN USERS u1 ON b.user_id = u1.id
INNER JOIN USERS u2 ON b.modified_by = u2.id;
Try it here : https://dbfiddle.uk/I1KzRNSs
I think what you're looking for would be this. I haven't done SQL in a while so there could be some syntax errors :), but it should be in the ballpark.
SELECT
b.id,
u1.name,
u2.name
FROM
books as b
INNER JOIN users as u1 ON
b.user_id = u1.id
INNER JOIN users as u2 ON
b.modified_by = u2.id

How to select passengers that never flew to a city

I will send the Database Description in an Image.
I tried this Select but I'm afraid that this isn't right
SELECT t.type , a.ICAOId , a.name , ci.id , c.ISOAlpha2ID , p.docReference , ti.docReference , ti.number , p.name , p.surname
FROM dbo.AirportType t
INNER JOIN dbo.Airport a ON t.type = a.type
INNER JOIN dbo.City ci ON a.city = ci.id
INNER JOIN dbo.Country c ON ci.ISOalpha2Id = c.ISOalpha2Id
INNER JOIN dbo.Passenger p ON c.ISOalpha2Id = p.nationality
INNER JOIN dbo.Ticket ti ON p.docReference = ti.docReference
WHERE NOT ci.id = 'Tokyo'
Can you please help to get this right?
enter image description here
You could make a list of the passengers that HAVE flown to the city then use that as a subquery to select the ones not in the list
I am just going to make an example of how it should be done
Subquery:
SELECT p.id FROM passengers
JOIN tickets t ON p.id = t.passengerID
JOIN city c ON c.id = t.cityID
Now you just put that into another query that selects the elements not in it
SELECT * FROM passenger
WHERE id not in (
SELECT p.id FROM passengers
JOIN tickets t ON p.id = t.passengerID
JOIN city c ON c.id = t.cityID
WHERE c.name= 'tokyo'
)
Notice I didn't use your attribute names, you will have to change those.
This was a bit simplified version of what you will have to do because the city is not directly in your tickets table. So you will also have to join tickets, with coupons, and flights to get the people that have flown to a city. But from there it is the same.
Overall I believe this should help you get what you have to do.
A minimal reproducible example is not provided.
Here is a conceptual example, that could be easily extended to a real scenario.
SQL
-- DDL and sample data population, start
DECLARE #passenger TABLE (passengerID INT PRIMARY KEY, passenger_name VARCHAR(20));
INSERT #passenger (passengerID, passenger_name) VALUES
(1, 'Anna'),
(2, 'Paul');
DECLARE #city TABLE (cityID INT PRIMARY KEY, city_name VARCHAR(20));
INSERT #city (cityID, city_name) VALUES
(1, 'Miami'),
(2, 'Orldando'),
(3, 'Tokyo');
-- Already visited cities
DECLARE #passenger_city TABLE (passengerID INT, cityID INT);
INSERT #passenger_city (passengerID, cityID) VALUES
(1, 1),
(2, 3);
-- DDL and sample data population, end
SELECT * FROM #passenger;
SELECT * FROM #city;
SELECT * FROM #passenger_city;
;WITH rs AS
(
SELECT c.passengerID, b.cityID
FROM #passenger AS c
CROSS JOIN #city AS b -- get all possible combinations of passengers and cities
EXCEPT -- filter out already visited cities
SELECT passengerID, cityID FROM #passenger_city
)
SELECT c.*, b.city_name
FROM rs
INNER JOIN #passenger AS c ON c.passengerID = rs.passengerID
INNER JOIN #city AS b ON b.cityID = rs.cityID
ORDER BY c.passenger_name, b.city_name;
Output
passengerID
passenger_name
city_name
1
Anna
Orldando
1
Anna
Tokyo
2
Paul
Miami
2
Paul
Orldando

Figure out the total number of people in an overlapping er database

I am trying to find:
the total number of doctors which aren't patients
the total number of patients which aren't doctors
the total number of people who are both patients and doctors
I can't seem to get the correct answer.
SQL:
CREATE TABLE persons (
id integer primary key,
name text
);
CREATE TABLE doctors (
id integer primary key,
type text,
FOREIGN KEY (id) REFERENCES persons(id)
);
CREATE TABLE patients (
id integer primary key,
suffering_from text,
FOREIGN KEY (id) REFERENCES persons(id)
);
INSERT INTO persons (id, name) VALUES
(1, 'bob'), (2, 'james'), (3, 'bill'), (4, 'mark'), (5, 'chloe');
INSERT INTO doctors (id, type) VALUES
(2, 'family doctor'), (3, 'eye doctor'), (5, 'family doctor');
INSERT INTO patients (id, suffering_from) VALUES
(1, 'flu'), (2, 'diabetes');
Select statement:
select count(d.id) as total_doctors, count(pa.id) as total_patients, count(d.id) + count(pa.id) as both_doctor_and_patient
from persons p
JOIN doctors d
ON p.id = d.id
JOIN patients pa
ON p.id = pa.id;
http://www.sqlfiddle.com/#!17/98ae9/2
One option uses left joins from persons and conditional aggrgation:
select
count(dr.id) filter(where pa.id is null) cnt_doctor_not_patient,
count(pa.id) filter(where dr.id is null) cnt_patient_not_doctor,
count(pa.id) filter(where do.id is not null) cnt_patient_and_doctor,
count(*) filter(where dr.id is null and pa.id is null) cnt_persons_not_dotor_nor_patient
from persons pe
left join doctors dr on dr.id = pe.id
left join patients pa on pa.id = pe.id
As a bonus, this gives you an opportunity to count the persons that are neither patient nor doctor. If you don't need that information, then a full join is simpler, and does not require bringing the persons table:
select
count(dr.id) filter(where pa.id is null) cnt_doctor_not_patient,
count(pa.id) filter(where dr.id is null) cnt_patient_not_doctor,
count(pa.id) filter(where dr.id is not null) cnt_patient_and_doctor
from doctors dr
full join patients pa using (id)
You can simply solve this using LEFT JOIN like:
--Aren't doctors:
SELECT count(*) from persons as A left join doctors as B on A.id=B.id where B.id is null
--Aren't patients:
SELECT count(*) from persons as A left join patients as B on A.id=B.id where B.id is null
--Both:
SELECT
(SELECT count(*) from persons as A left join patients as B on A.id=B.id where B.id is not null) +
(SELECT count(*) from persons as A left join doctors as B on A.id=B.id where B.id is not null)
AS summ
Here a CTE alternative:
with doc_not_pat
as(
select count(*) as Doc_Not_Pat
from doctors d
where not exists (select 1 from patients p where p.id = d.id)
),
pat_not_doc as(
select count(*) as Pat_Not_Doc
from patients p
where not exists ( select 1 from doctors d where d.id = p.id)
),
pat_and_doc as(
select count(*) as Pat_And_Doc
from patients p
where exists (select 1 from doctors d where d.id = p.id)
)
select (select Doc_Not_Pat
from doc_not_pat dcp) as Doc_Not_Pat,
(select Pat_Not_Doc
from pat_not_doc) as Pat_Not_Doc,
(select Pat_And_Doc
from pat_and_doc) as Pat_And_Doc

SQL tricky left join of three tables - all different keys

Originally I've been trying to merge users_role_change_log table with users_schedule_log. In the first I've got only user id, and in second there is only full name. So no clear hit.
I've tried to introduce third table which could tie those two together - users table. There is users id, but full name is splitted in two columns first_name and second_name. So that further complicates things. Not my design.
Was hoping that I could improvise in concating ad hoc first_name and last_name from main users table and passing it as a relevant parameter for left join.
SELECT r.*,CONCAT(first_name, ' ', last_name) AS u.name
FROM `users_role_change_log` r
LEFT JOIN users u ON r.id = u.id
LEFT JOIN users_schedule_log e ON u.name = e.name
But this doesn't work.
Any recommendation?
Edit:
#1 added table examples:
a) users_role_change_log
id user_id role time_changed
34 60 19 '2020-07-05 05:30:00'
b) users_schedule_log
id name shift_start shift_end
9 'John Doe' '2020-07-05 07:00:00' '2020-07-05 15:00:00'
c) users
id first_name last_name
60 'John' 'Doe'
d)what I want to end up with
a)user_id b)name a)role a)time_changed b)shift_start b)shift_end
60 'John Doe' 19 ...
You must start the joins from users and the left join users_role_change_log and users_schedule_log on the correct columns:
SELECT u.id, CONCAT(u.first_name, ' ', u.last_name) name,
r.role, r.time_changed, s.shift_start, s.shift_end
FROM users u
LEFT JOIN users_role_change_log r ON r.user_id = u.id
LEFT JOIN users_schedule_log s ON s.name = CONCAT(u.first_name, ' ', u.last_name)
See the demo.
You could try it this way:
SELECT *
FROM `users_role_change_log` r
LEFT JOIN users u ON r.id = u.id
LEFT JOIN users_schedule_log e ON CONCAT(u.first_name, ' ', u.last_name) = e.name
or this way:
WITH user_connector as (select CONCAT(first_name, ' ', last_name) as name, id from users)
SELECT *
FROM `users_role_change_log` r
LEFT JOIN user_connector u ON r.id = u.id
LEFT JOIN users_schedule_log e ON u.name = e.name
Of course you shoud change the asterisk to the fields you want to select.
You can use the subquery for this. Hope to help, my friend :))
create table users_role(id int, user_id int, role int, time_changed datetime)
create table users_schedule(id int, name varchar(20), shift_start datetime, shift_end datetime)
create table users(id int, first_name varchar(20), last_name varchar(20))
insert into users_role
values(34, 60, 19, '2020-07-05 05:30:00')
insert into users_schedule
values(9, 'John Doe', '2020-07-05 07:00:00', '2020-07-05 15:00:00')
insert into users
values(60, 'John', 'Doe')
-----
Select *
FROM (SELECT r.*, CONCAT(u.first_name, ' ', u.last_name) AS Name
FROM users_role r
LEFT JOIN users u ON r.user_id = u.id )as t
LEFT JOIN users_schedule e ON t.Name = e.name
Find the answer in this fiddle. You need to construct a subquery for your users table where you CONCAT the columns and then proceed with the JOIN.
You might need to consider the database schema, since the table users_schedule_log can use the ID of the user rather than his name (same name problem).
SELECT r.user_id, u.name, r.role, r.time_Changed, u.shift_start, u.shift_end
FROM users_role_change_log AS r
LEFT JOIN (SELECT id, CONCAT(first_name,' ',last_name) AS name FROM users)A
ON r.user_id = A.id
LEFT JOIN users_schedule_log AS u
ON u.name = A.name

Conditional JOIN different tables

I want to know if a user has an entry in any of 2 related tables.
Tables
USER (user_id)
EMPLOYEE (id, user_id)
STUDENT (id, user_id)
A User may have an employee and/or student entry. How can I get that info in one query?
I tried:
select * from [user] u
inner join employee e
on e.user_id = case when e.user_id is not NULL
then u.user_id
else null
end
inner join student s
on s.user_id = case when s.user_id is not NULL
then u.user_id
else null
end
But it will return only users with entries in both tables.
SQL Fiddle example
You could use an outer join:
select *
from USER u
left outer join EMPLOYEE e ON u.user_id = e.user_id
left outer join STUDENT s ON u.user_id = s.user_id
where s.user_id is not null or e.user_id is not null
alternatively (if you're not interested in the data from the EMPLOYEE or STUDENT table)
select *
from USER u
where exists (select 1 from EMPLOYEE e where e.user_id = u.user_id)
or exists (select 1 from STUDENT s where s.user_id = u.user_id)
If you want to get all user data together You might have:
SELECT
user_id
,'Employee' AS Source
FROM
employee
UNION
SELECT
user_id
,'Student' AS Source
FROM
student
http://sqlfiddle.com/#!3/90216/22
Which can also be done with a full join and a CASE statement:
SELECT
ISNULL(e.user_id,s.user_id) AS user_id
,CASE WHEN e.user_id IS NULL THEN 'Student'
ELSE 'Employee'
END AS SOURCE
FROM
employee AS e
FULL JOIN student AS s
ON s.user_id = e.user_id
http://sqlfiddle.com/#!3/90216/29
the latter will combine people who are both students adn employees into one row and call them and employee. compare:
http://sqlfiddle.com/#!3/2aa3e/1
and
http://sqlfiddle.com/#!3/2aa3e/2
where I have made user 1 a student and a employee
Such solution also can help you.
SELECT S.*, P.*
,CASE
WHEN S.ShipmentType = 'import' THEN SP.SupplierName
WHEN S.ShipmentType = 'export' THEN C.CustomerName
END AS ShipmentDesination
FROM tblShippments S
INNER JOIN tblProducts P ON S.productId = P.productID
LEFT OUTER JOIN tblCustomers C ON S.companyId = C.customerId AND S.ShipmentType = 'export'
LEFT OUTER JOIN tblSuppliers SP ON S.companyId = SP.supplierId AND S.ShipmentType = 'import'
If you look at employee and student tables as one, you can use left join:
select *
from user u
left join
(
select 'Employee' as UserType,
id,
user_id
from employee e
union all
select 'Student',
id,
user_id
from student s
) r
ON u.user_id = r.user_id
How about UNION which you write as 2 separate SELECT statements
For example:
SELECT * FROM User U
JOIN Employee E ON E.User_Id = U.User_Id
UNION
SELECT * FROM User U
JOIN student S ON S.User_Id = U.User_Id
I couldn't see why you needed the CASE statement it looked superfluous. If you wanted all Users and to show nulls then use LEFT OUTER JOIN.
work for mysql v5.6+
schema:
CREATE TABLE USER (user_id INT);
CREATE TABLE employee (id INT, user_id INT);
CREATE TABLE student (id INT, user_id INT);
INSERT INTO USER SELECT 1;
INSERT INTO USER SELECT 2;
INSERT INTO USER SELECT 3;
INSERT INTO USER SELECT 4;
INSERT INTO employee SELECT 1, 1;
INSERT INTO employee SELECT 2, 4;
INSERT INTO student SELECT 1, 2;
first solution
,full outer join does not work for mysql v5.6+:
SELECT
IF(e.user_id IS NULL,s.user_id,e.user_id) AS user_id
,CASE WHEN e.user_id IS NULL THEN 'Student'
ELSE 'Employee'
END AS SOURCE
FROM
employee AS e
LEFT JOIN student AS s
ON (s.user_id = e.user_id)
UNION
SELECT
IF(e.user_id IS NULL,s.user_id,e.user_id) AS user_id
,CASE WHEN e.user_id IS NULL THEN 'Student'
ELSE 'Employee'
END AS SOURCE
FROM
employee AS e
RIGHT JOIN student AS s
ON (s.user_id = e.user_id)
alternative solution:
SELECT IF(e.user_id IS NULL,s.user_id,e.user_id) AS user_id,
CASE WHEN e.user_id IS NULL THEN 'Student'
ELSE 'Employee'
END AS SOURCE
FROM USER AS u
LEFT OUTER JOIN employee e ON (u.user_id = e.user_id)
LEFT OUTER JOIN student s ON (u.user_id = s.user_id)
WHERE s.user_id IS NOT NULL OR e.user_id IS NOT NULL
other, alternative solution:
SELECT u.user_id,SOURCE
FROM USER u
INNER JOIN
(
SELECT 'Employee' AS SOURCE,id,user_id FROM employee e
UNION ALL
SELECT 'Student',id,user_id FROM student s
) r ON u.user_id = r.user_id
SELECT OrderID, Quantity, O.ProductID , ProductName,
CASE
WHEN Quantity > 3 THEN 'The quantity is More than 3'
WHEN Quantity = 3 THEN 'The quantity is Equal to 3'
ELSE 'The quantity is Less than 3'
END AS QuantityText
FROM tb_OrderDetail O
INNER JOIN tb_Product P ON O.ProductID = P.ProductID