SQL Set Operators - Selecting rows from tables with different columns - sql

I'm using Oracle 10g, and I'm trying to select rows from one table that do not appear in the other table in the query using a set operator.
I'm trying to select id, last_name and first_name columns from an employee table in which these rows do not appear in a job_history table.
The only common column in these 2 tables is the id column. But I want to display the names as well.
I have tried:
SELECT
id, last_name, first_name
FROM
employees
MINUS
SELECT
id, TO_CHAR(null), TO_CHAR(null)
FROM
job_history;
Which doesn't produce desired result.
However, if I didn't want to display the names from the employee table, I use:
SELECT id FROM employees
MINUS
SELECT id FROM job_history;
Which gives me half of the result, except for that I want the names from the employee table.
Any advice?

Why can't you just use NOT IN like
SELECT id, last_name, first_name FROM employees
WHERE ID NOT IN (SELECT id FROM job_history);
You can as well try LEFT JOIN like
SELECT e.id, e.last_name, e.first_name
FROM employees e LEFT JOIN job_history jh
ON e.ID = jh.ID
WHERE jh.some_other_column IS NULL;

You can use a inner join on the select result
select a.id, a.last_name, a.first_name
from employees a
inner join (
SELECT id FROM employees
MINUS
SELECT id FROM job_history ) x on x.id = a.id

Related

Missing expression problem in SQL using Oracle

I want to get the number of employees by department and I wrote this script using Oracle but it always says that there is a missing expression
The columns used in my tables :
department :name (the name of the department) -
depnum (the id of the department"primary key"),
employee : empnum (the id of the employee) -
depnum (the id of the department in which the employee in question is working "foreign key")
Query:
select
s.name
from
department s
inner join
employee p on s.depnum = p.depnum
group by
s.name
having
count(p.empnum) = max(select count(p.empnum)
from employee p, department s
where s.depnum = p.depnum
group by s.name) ;
If you want the number of employees by department, I would expect something like this:
select s.name, count(*) as num_employees
from department s inner join
employe p
on s.depnum = p.depnum
group by s.name ;
If you want the department names with the maximum number of names, you can use a having clause:
select s.name, count(*) as num_employees
from department s inner join
employe p
on s.depnum = p.depnum
group by s.name
having count(*) = (select max(cnt)
from (select count(*) as cnt
from employee e2
group by e2.depnum
) e2
);
The problem with your query is that you are attempting to take the max() of a subquery. That syntax is not allowed -- and not necessary.
you sql statement is not correct that's why it thrown that error. I think you tried something like below
select s.name
from department s
inner join employe p on s.depnum=p.depnum
group by s.name
having count(p.empnum)=
select max(cnt) from
(
select count(p.empnum) as cnt
from employe p join department s
on s.depnum=p.depnum
group by s.name
) t;

Does this SQL statement look right?

I wondering is what I am doing right.
select distinct
Departments.Department_No, Departments.Department_Name
from
Departments
join
Employees on Departments.Department_No = Employees.Department_No
join
Jobs on Jobs.Job_ID = Employees.Job_ID
where
Departments.Department_No not in (select distinct Department_No
from Employees
where Employees.Job_ID like '%SA_REP%');
You want to display distinct values of Department Number and Department Name
You join Employees table with Department on Department Number
You join Jobs table with Employees on Job ID
You filter the result by excluding those Department Numbers of the entire Employee table that have a Job ID matching the pattern %SA_REP%
In my opinion you don't need
the join with the Jobs table
the join with the Employees table
you could maybe see if one of the other users' suggestions can bring performance improvement
SELECT DISTINCT departments.department_no,
departments.department_name
FROM departments
WHERE departments.department_no NOT IN (SELECT DISTINCT department_no
FROM employees
WHERE employees.job_id LIKE '%SA_REP%'
);
You could simply do this using NOT EXISTS instead of using a NOT IN subquery:
SELECT DISTINCT
d.Department_No
,d.Department_Name
FROM Departments d
JOIN Employees e ON d.Department_No = e.Department_No
WHERE NOT EXISTS
(select 1
from Employees e1
where e1.Job_ID like '%SA_REP%'
AND e1.Department_No = e.Department_No);
You can translate where condition without "in".
And you don't need to fetch date from "Jobs" - you don't use it
Select distinct Departments.Department_No, Departments.Department_Name
from Departments
Join Employees on Departments.Department_No = Employees.Department_No
where Employees.Job_ID not like '%SA_REP%';

SQL: group by table

Suppose we use PostgreSQL and have 2 tables, department and employee, the latter belonging and having a FK into the former.
We now want to do an aggregate select, where we want to put all the information from department and then some aggregate values from employee:
SELECT d.id, d.name, d.budget, count(*), avg(e.salary), max(e.age), sum(e.children)
FROM department d LEFT JOIN employee e ON e.dept = d.id
GROUP BY d.id, d.name, d.budget
I don't like that I need to specify all the columns from department in the GROUP BY - is there a way to "group by the whole table"?
And a bit more philosophical question, suppose I do GROUP BY d.id. Assuming d.id is the primary key of department, why do I need to group by all the other columns as well?
If employee is pre aggregated then there is no need to list the select columns
select *
from
department d
left join (
select
dept as id,
count(*) as count_employee,
avg(salary) as avg_salary,
max(age) as max_age,
sum(children) as sum_children
from employee
group by dept
) e using (id)
The using clause avoids the joined on column duplicity.

SQL inner join and subquery

I am trying to use a sub query on an inner join to get back all department numbers and names from a table that do not have programmer in the department, but I am having a little trouble as it returns no values. Here is my code, thanks for any help.
select Departments.Department_Name, Departments.Department_No
from employees inner join departments
on departments.department_No = employees.Department_No
where Employees.Department_No !=
(select Department_Name
from Employees, Departments
where Job_ID = '%pro%')
From what I can gather, you want something like this:
select d.Department_Name, d.Department_No
from departments d
WHERE NOT EXISTS (SELECT 1 FROM Employees
WHERE d.Department_No = Department_No
AND Job_ID LIKE '%pro%')
This selects all departments, for which there doesn't exist an employee whose job_ib contains 'pro'.
Try this one:
select d1.Department_Name,d1.Department_No
from departments d1
where d1.Department_No in ( select e1.Department_No
from Employees e1
where e1.Department_No=d1.Department_No and
e1.job_id not in ('programmer'));

Can I use more than one column in subquery?

I want to show the names of all employees from the EMPLOYEES table who are working on more than three projects from the PROJECT table.
PROJECTS.PersonID is a a foreign key referencing EMPLOYEES.ID:
SELECT NAME, ID
FROM EMPLOYEES
WHERE ID IN
(
SELECT PersonID, COUNT(*)
FROM PROJECTS
GROUP BY PersonID
HAVING COUNT(*) > 3
)
Can I have both PersonID, COUNT(*) in that subquery, or there must be only one column?
Not in an IN clause (or at least not the way you are trying to use it. Some RDBMSs allow tuples with more than one column in the IN clause but it wouldn't help your case here)
You just need to remove the COUNT(*) from the SELECT list to achieve your desired result.
SELECT NAME, ID
FROM EMPLOYEES
WHERE ID IN
(
SELECT PersonID
FROM PROJECTS
GROUP BY PersonID
HAVING COUNT(*) > 3
)
If you wanted to also return the count you could join onto a derived table or common table expression with more than one column though.
SELECT E.NAME,
E.ID,
P.Cnt
FROM EMPLOYEES E
JOIN (SELECT PersonID,
Count(*) AS Cnt
FROM PROJECTS
GROUP BY PersonID
HAVING Count(*) > 3) P
ON E.ID = P.PersonID
To answer your question, you can only have 1 column for the IN subquery. You could get your results using the query below:
SELECT e.ID
,e.Name
FROM dbo.Projects p
LEFT OUTER JOIN dbo.Employees e
ON p.PersonID = e.ID
GROUP BY e.ID
,e.Name
HAVING COUNT(*) > 3