Conditional JOIN different tables - sql

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

Related

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

ORACLE SQL check if the row number of the table is n, then perform a join

TABLE student: ID, ID2, NAME, AGE
TABLE class: ID, CLASS_NAME, some other columns
TABLE school: ID2, some other columns.
I am trying to perform below in Oracle SQL:
If the count of the records in TABLE "student" with age>5, is 1,
join the student table with "CLASS" table by "ID", else, join the student table with "school" table by "ID2".
I found I cannot put count in where clause. Can someone help?
I would use window functions:
select s.*, . . .
from (select s.*, sum(case when age > 5 then 1 else 0 end) over () as cnt5
from students s
) s left join
class c
on c.id = s.id and cnt5 = 1 left join
school sch
on sch.id2 = s.id2 and cnt5 <> 1
where c.id is not null or sch.id is not null
you can use left join with case
select s.*, case when age > 5 then COALESCE (c.ID,scl.ID2) as id
from student s
left join class c on s.ID=c.ID
left join school scl on s.ID2=scl.ID2
As Gordon said window functions are an option, but this is also a rare occasion where you can deliberately perform a cross join:
select s.*
,...
from students s
left join
(
select count(*) as RECORD_COUNT
from students
where age > 5
) sub
on 1 = 1
left join class c
on s.id = c.id
and sub.record_count = 1
left join school h
on s.id2 = h.id2
and c.id is null

SQL Server: select query from multiple tables

I got 3 tables: projects, employee and project_employee
employee
ID (int, PK)
Name
projects
project_id (int, PK)
project_name
project_employee
project_id (int, PK)
employee_id (int, PK)
What I trying to do is write a query that get ID and Name of all employees that are not in a project, for example project number 9.
So I tried:
SELECT ID, Name
FROM [employee], [project_employee]
WHERE [employee].ID != [project_employee].emp_id AND [project_employee].project_id = 9;
but I always get empty result, something must be wrong with my logic?
You can do it using NOT EXISTS :
SELECT u.ID, u.Name
FROM [User] u
WHERE NOT EXISTS ( SELECT *
FROM [project_employee] pe
WHERE pe.project_id = 9
AND pe.employee_id = u.ID);
Instead try something like this, it should get you all users that are not part of project_id = 9
SELECT u.ID, u.Name
FROM [User] u
WHERE u.ID NOT IN (SELECT pe.employee_id
FROM [project_employee] pe
WHERE pe.project_id = 9);
SELECT ID, NAME
FROM employee e
LEFT JOIN project_employee pe
ON pe.employee_id = e.id
AND pe.project_id = 9
WHERE pe.employee_id IS NULL
You don't even need projects table since you are not looking for employees on a project.
You can use a LEFT JOIN to get the required data
select e.id,e.name
from employee e
left join project_employee pe
on e.id = pe.employee_id
left join projects p
on pe.project_id = p.project_id
where p.project_id is null
(OR) Just left join with project_employee table. No condition has been checked assuming that all employees with no project at hand is the desired output.
select e.id,e.name
from employee e
left join project_employee pe
on e.id = pe.employee_id
where pe.project_id is null
He asked for the ability to check that the employees are not in a specific project. The not in solution is fine, but I generally prefer using left joins.
SELECT ID, NAME
FROM employee e
LEFT JOIN project_employee pe
ON pe.emp_id = e.id
and pe.project_id = 9
WHERE pe.employee_id IS NULL

SQL get duplicate records for all the users

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

SQL: Have 4 Tables, Looking to find unmatched data

I've always done this back asswards in PHP or ASAP, so I figure it's time to actually learn the proper way to do it in SQL. I have the following 4 tables in a database:
Category (Fields: CategoryNumber, Desc) (small table with 15 rows)
Media (Fields: MediaID, Desc, CategoryNumber, etc) (huge table with 15,000 rows)
Sales (Fields: Date, MediaID, EmployeeID etc) (huge table with 100,000 rows)
Employees (Fields: EmployeeID, Name, etc) (small table with only 20 rows)
Category only links to Media
Media has links to both Category and Sales.
Sales links to both the Media and Employee
Employee only links to Sales
What I would like to do is to write a query which tells me what categories a given employee has never sold any media in.
I can write a simple query that looks for unmatched data between 2 tables, but I have no clue how to do it when I'm dealing with 4 tables.
Thanks for your time and help!
Here's my suggestion:
select *
from Category c
where not exists (
select *
from Employee e
inner join Sales s on s.EmployeeId = e.EmployeeId
inner join Media m on m.MediaID = s.MediaID
where e.Name = 'Ryan' and m.CategoryNumber = c.CategoryNumber
)
To query all employes with the categories in which they didn't sell anything:
select e.EmployeeName, c.CategoryNumber
from Category c
cross join Employee e
where not exists (
select *
from Sales s
inner join Media m on m.MediaID = s.MediaID
where c.categoryNumber = m.CategoryNumber
and s.EmployeeId = e.EmployeeId
)
SELECT c.CategoryNumber, c.Desc
FROM Category c
WHERE NOT EXISTS
(
SELECT *
FROM Employees e
INNER JOIN Sales s on s.EmployeeID = e.EmployeeID
INNER JOIN Media m on m.MediaID = s.MediaID
WHERE e.Name = "Ryan"
AND m.CategoryNumber = c.CategoryNumber
)
MS Access evidently needs a lot of parentheses (thanks, Ryan!):
select *
from Category c
where not exists
( select *
from ( Employee e
inner join Sales s on (s.EmployeeId = e.EmployeeId))
inner join Media m on (m.MediaID = s.MediaID)
where (e.Name = 'Ryan' and m.CategoryNumber = c.CategoryNumber) )
select c.desc
from category
left outer join (select s.employeeid,m.categorynumber
from sales s
inner join media m on s.mediaid=m.mediaid
inner join employee e on e.employeeid=s.employeeid
where e.name = 'JOE'
group by employeeid,categorynumber) t on t.categorynumber=c.categorynumber
where s.employeeid is null
Modified Answer based on the solution provided by Carl in Access SQL Syntax:
select *
from Category c
where not exists (
select *
from (Employee e
inner join Sales s on (s.EmployeeId = e.EmployeeId))
inner join Media m on (m.MediaID = s.MediaID)
where (e.Name = 'Ryan' and m.CategoryNumber = c.CategoryNumber)
)