How to retrieve data from multiple tables using Subquery? - sql

Suppose we have two tables
student(studentID, name, department_ID)
department(departmentID, name).
Our aim is to retrieve the data from both tables using the subquery. I'm trying this
select * from department, student
where department.departmentID
IN (select student.departmentID from student, department
where student.departmentID = department.departmentID)
but it returns the cross product of the number of rows of two tables.
This is possible to get the correct result using JOIN like this
select * from department
Inner join student
on student.departmentID = department.departmentID
and using WHERE clause like this
select * from department, student
where department.departmentID = student.departmentID
I'm wondering if someone can tell me how it can be possible using subquery in SQL.

Hope this helps:
select *, (select name from department d where s.departmentID = d.departmentID) as dname
from student s
where (select name from department d where s.departmentID = d.departmentID) is not null
This problem is however meant to be solved using joins. To learn subQ, use proper examples.
SQL Fiddle: Test

Related

Single SQL query on many to many relationship using join

I have a simple database with few tables (and some sample columns)
Employee (ID, Title, Content)
Project (ID, Title)
ProjectEmployee(ID ,EMPLOYEE_ID,PROJECT_ID)
Need to find employee's whose don't have any project using join query
I am able to do it using subquery
select * from employee
where id not in (select id from project_employee);
The LEFT JOIN with NOT NULL give you the expected results.
select e.*
from employee e
LEFT JOIN project_employee pe on pe.id = e.id
where pe.id is not null;
This would be best implemented using not exists, joining is not the correct solution if you do not need to return any columns from the joined table.
select *
from employee e
where not exists (select * from project_employee pe where pe.id = e.id)

How can we rewrite a query with a subquery in SELECT clause?

How would you rewrite the following query into one without subquery as much as possible?
Select dept name,
(Select Count(*)
From instructor
Where department.dept name = instructor.dept name
) As num_instructors
From department;
I came up with the following. Is it a good equivalence to the above?
Select dept name, count(*)
From department, instructor
Where department.dept name = instructor.dept name
Group By department.dept_name;
Thanks.
The proper way to write the query uses explicit JOIN syntax:
select d.dept_name, count(i.dept_name)
from department d left join
instructor i
on d.dept_name = i.dept_name
group by d.dept_name;
If you only care about departments that have at least one instructor, then no join is necessary at all:
select i.dept_name, count(*)
from instructor i
group by i.dept_name;
Your attempt is really close, just a couple things..
You should use explicit joins (ie. JOIN, LEFT JOIN etc.) instead of implicit joins (commas in the FROM clause). Implicit joins are 25+ years depreciated.
Also, in this case you will want a LEFT JOIN or no departments will be displayed that don't have instructors. LEFT JOIN will retain departments without instructors and give you a 0 count (like the first query), where a JOIN would not display those at all.
SELECT d.dept_name, COUNT(i.dept_name) as num_instructors
FROM department d
LEFT JOIN instructors i on d.dept_name = i.dept_name
GROUP BY d.dept_name

Converting SQL query from using join to correlated nested query?

I am new to SQL and working on an assignment using Oracle SQL Developer. The assignment asks to solve the problems using correlated nested queries, but I am having a lot of trouble doing so. I have been able to solve the problems using join statements. I understand a correlated nested query to be where the subquery references the same relation used in the outer query. Is this correct line of thinking and how does this differ from using join operations? Also, in my use of SQL, I've noticed that to SELECT an attribute, it must be a column of the outer query (opposed to inner query). Does this mean all aggregate functions must be in the outer query if using correlated nested queries? Any advice would be appreciated.
For example, one problem asks to create a view with employee names, salary, department name, department manager name, department manager salary, and average department salary. My solution using joins:
CREATE VIEW DEPT_EMP_MGR_SALARIES
AS
SELECT
E.Fname AS EMP_FNAME,
E.Minit AS EMP_MINIT,
E.Lname AS EMP_LNAME,
E.Salary AS EMP_SALARY,
Dname,
DMgr_Fname,
DMgr_Minit,
DMgr_Lname,
DMgr_Salary,
DEPT_AVG_SALARY
FROM
EMPLOYEE E
JOIN (
SELECT
D.Dname,
D.Dnumber,
M.Fname AS DMgr_Fname,
M.Minit AS DMgr_Minit,
M.Lname AS DMgr_Lname,
M.Salary AS DMgr_Salary
FROM
EMPLOYEE M,
DEPARTMENT D
WHERE
D.Mgr_SSN = M.SSN
) ON E.Dno = Dnumber
NATURAL JOIN (
SELECT
D1.Dname,
AVG(E1.SALARY) AS DEPT_AVG_SALARY
FROM
EMPLOYEE E1,
DEPARTMENT D1
WHERE
E1.Dno = D1.Dnumber
GROUP BY
D1.Dname
);

Create filter for most recent date using combined columns

I have created a filtering application in Access that references four simple tables:
Employee: Emp_ID, FirstName, LastName
Skill: Skill_ID, SkillName, SkillDescription, SkillGroup
Employee_Skill: Entry_ID, Emp_ID, Skill_ID, LevelofExperience, Dateupdated
SkillGroupName:SkillGroup_ID SkillGroupName`
Basically the idea of this database is to track employee skills and how the level of experience improves (or not!) over time. The problem I am facing is that I want the application to filter by the most recently updated combination of Skill and Employee. I have found the query that will allow for me to use the two columns as a distinct entity:
SELECT DISTINCT Emp_ID, Skill_ID FROM Employee_Skill
WHERE (SELECT MAX(DateUpdated)From Employee_Skill);
And it works perfectly on its own, but I don't know how to incorporate it either into my main query, which simply joins together the necessary columns for an easier end user experience. It does not visibly show Emp_ID or Skill_ID. It also doesn't in the VBA for the application. (-1 = Include all History; 0 = Only include most updated.)
Update:
I have been able to select the distinct combination of Employee and Skill through my main query by doing this:
SELECT
Employee.FirstName,
Employee.LastName,
Max(Employee_Skill.LevelOfExperience) AS LevelOfExperience,
Skill.SkillName,
Max(Employee_Skill.DateUpdated) AS DateUpdated,
Max(SkillGroup.SkillGroupName) AS SkillGroupName
FROM
SkillGroup INNER JOIN
(Skill INNER JOIN
(Employee INNER JOIN
Employee_Skill ON
Employee.Emp_ID = Employee_Skill.Emp_ID) ON
Skill.Skill_ID = Employee_Skill.Skill_ID) ON
SkillGroup.SkillGroup_ID = Skill.SkillGroup
WHERE
Employee.Active=True
GROUP BY
Employee.FirstName,
Employee.LastName,
Skill.SkillName
ORDER BY
Max(Employee_Skill.LevelOfExperience) DESC;
However, my forms and reports built on this query are stuck with only the option of seeing the most updated version. I am really hoping to have a dynamic form that removes the constraints as desired.
Not sure what you're doing with Max(Employee_Skill.LevelOfExperience) or Max(SkillGroup.SkillGroupName) but I think you need to stick with querying for the detail rows and then include another column marking the Max(Employee_Skill.DateUpdated) filter, like:
SELECT
Employee.FirstName,
Employee.LastName,
Employee_Skill.LevelOfExperience,
Skill.SkillName,
Employee_Skill.DateUpdated,
SkillGroup.SkillGroupName,
iif(max_dateUpdated=dateupdated,1,0) as is_max_DateUpdated
FROM
SkillGroup INNER JOIN
(Skill INNER JOIN
(Employee INNER JOIN
Employee_Skill ON
Employee.Emp_ID = Employee_Skill.Emp_ID) ON
Skill.Skill_ID = Employee_Skill.Skill_ID) ON
SkillGroup.SkillGroup_ID = Skill.SkillGroup inner join
(select
empID,
max(dateupdated) as max_dateUpdated
from
Employee_Skill
group by
empID) mx on
Employee.empID = mx.empID
WHERE
Employee.Active=True

How to use "IN" for more than one column

This question might be trivial or even silly but I was wondering if there is a way to use "IN" on more than one column on one to one matching.
For example I use
select emp_id from employee where emp_id IN (select emp_id from employee_other)
How could I achieve something like
select emp_id from employee where emp_id,emp_org IN (select emp_id,emp_org from employee_other)
I know I cant be using the following because it will simply do the union whereas I want a selection based on one to one record matching.
select emp_id from employee where emp_id IN (select emp_id from employee_other) and emp_org in (select emp_org from employee)
Please note that I am reluctant to use EXCEPT.
Thanks guys
You may want to use the EXISTS operator
select e.emp_id
from employee e
where EXISTS
(
SELECT *
FROM employee_other eo
WHERE e.emp_id = eo.emp_id
AND e.emp_org = eo.emp_org
)
IN in Microsoft SQL Server only works with a single column, ie. you can only write X IN (...), never anything remotely like X,Y IN (...).
There are two ways to handle this, depending on your data:
Joining with a sub-query
Using EXISTS
To JOIN, do this:
select emp_id
from employee
inner join (select emp_id,emp_org from employee) as x
on employee.emp_id = x.emp_id and employee.emp_org = x.emp_org
Your example is a bit lousy, however, since you're using the same table.
To use EXISTS, do this:
select emp_id
from employee
where exists (
select emp_id,emp_org from employee e2
where e2.emp_id = employee.emp_id and e2.emp_org = employee.emp_org)
This, in the same way as the join, links the main table to the "sub-query" table, but whereas the join will produce duplicate rows if the "sub-query" produces multiple hits, the EXISTS clause will not.
I don't understand what you are trying to accomplish with emp_org in (select emp_org from employee) isn't that always true?
does this work?
select emp_id from employee e
where exists (select 1 from employee_other eo
WHERE e.emp_id =eo.emp_id and
AND e.emp_org = eo.emp_org )
You had it almost completely right in your second example. You just need to add parens around your column names.
select emp_id
from employee
where (emp_id,emp_org) IN (select emp_id,emp_org from employee)
Use Inner Join
select e1.emp_id from employee e1
inner join employee_other e2 on e1.emp_id = e1.emp_id and e1.emp_org = e2.emp_org
You may have to use Distinct in case the employee_other table causes dups.
select Distinct e1.emp_id from employee e1
inner join employee_other e2 on e1.emp_id = e1.emp_id and e1.emp_org = e2.emp_org