How can I write a query to join two tables and return result if exactly one match in there. I have to discard results if zero match and more than one match.
All I am looking for is to extend the INNER JOIN. Let me just get to the point. I have two tables Dept & Emp. One Dept can have multiple Emp's & not the other way around.
Table Dept
Table Emp
I need to JOIN it on Dept_id
Expected Results
You can join with a not exists condition:
select d.*, e.emp_id, e.emp_name
from dept d
inner join emp e
on d.dept_id = e.dept_id
and not exists (
select 1
from emp e1
where e1.dept_id = d.dept_id and e1.emp_id != e.emp_id
)
One alternative to existing solutions can be one using analytics (window functions),
instead of joining twice:
select dept_id, dept_name, emp_id, emp_name
from
(
SELECT
d.Dept_id, d.Dept_name, e.Emp_id, e.Emp_Name,
count(*) over (partition by d.dept_id) cnt1
FROM d
INNER JOIN e
ON d.Dept_id = e.Dept_id
) where cnt = 1;
You could use a subquery for group by dept_id haing count = 1
select t.dept_id, dept.dept_name, emp.Emp_name
from (
select dept_id
from emp
group by dept_id
having count(*) = 1
) t
INNER JOIN dept on t.dept_id = dept.dept_id
INNER JOIN emp ON t.dept_id = emp.dept_id
You can phrase this as an aggregation query in Oracle:
select d.dept_id, d.dept_name,
max(e.emp_id) as emp_id,
max(e.emp_name) as emp_name
from dept d inner join
emp e
using (dept_id)
group by d.dept_id, d.dept_name
having count(*) = 1;
This works because if there is only one match, then max() returns the value from the one row.
Also, try below query;
SELECT a.depid dept_id,dept_name,emp_id,emp_name
FROM
(SELECT case WHEN count(*)=1 THEN dept_id END depid FROM emp GROUP BY dept_id) a INNER JOIN emp ON depid=dept_id
INNER JOIN dept b ON a.depid = b.dept_id
WHERE depid IS NOT NULL
Another way would be
select d.dept_id, d.dept_name, e.emp_name
from emp e
join dept d on d.dept_id = e.dept_id
where e.dept_id in
( select dept_id from emp group by dept_id having count(*) = 1 )
Related
I have vh_emp table whose table structure is(This is empty table):
I am taking data from employee table as source table where I wrote some query to match with vh_emp table:
SELECT e.emp_id EMP_ID,
e.emp_name EMP_NAME,
e.dob DOB,
e.join_date JOIN_DATE,
CASE
WHEN e.leave_date IS NULL THEN Trunc(sysdate)
ELSE e.leave_date
END AS LEAVE_DATE,
e.salary / 100 SALARY,
d.dept_name DEPT_NAME,
one.addr_name ADDRESS_ONE,
two.addr_name ADDRESS_TWO,
b.emp_name AS MANAGER_NAME
FROM offc.employee e
LEFT JOIN offc.department d
ON e.dept_id = d.dept_id
LEFT JOIN offc.add_line_one one
ON e.line1 = one.addr_id_one
LEFT JOIN offc.add_line_two two
ON e.line2 = two.addr_id_two
LEFT OUTER JOIN (SELECT *
FROM offc.employee) b
ON e.manager_id = b.emp_id;
The query is running fine and the output is coming fine from this query:
So,I wanted to insert these data in vh_emp tables using :
INSERT INTO offc.vh_emp
(emp_id,
emp_name,
dob,
join_date,
leave_date,
address_one,
address_two,
salary,
manager_name,
dept_name)
SELECT e.emp_id EMP_ID,
e.emp_name EMP_NAME,
e.dob DOB,
e.join_date JOIN_DATE,
CASE
WHEN e.leave_date IS NULL THEN Trunc(sysdate)
ELSE e.leave_date
END AS LEAVE_DATE,
e.salary / 100 SALARY,
d.dept_name DEPT_NAME,
one.addr_name ADDRESS_ONE,
two.addr_name ADDRESS_TWO,
b.emp_name AS MANAGER_NAME
FROM offc.employee e
LEFT JOIN offc.department d
ON e.dept_id = d.dept_id
LEFT JOIN offc.add_line_one one
ON e.line1 = one.addr_id_one
LEFT JOIN offc.add_line_two two
ON e.line2 = two.addr_id_two
LEFT OUTER JOIN (SELECT *
FROM offc.employee) b
ON e.manager_id = b.emp_id;
But,I executed this query I got error as:
ORA-01722: invalid number
The structure of table employee is:
why is this error coming? I reviewed and saw that datatype is same for the column and value of select statement.This error should not appear as column datatype is matching.
Order of your insert clause column name and select clause column name should match.
Please match the positions of them as following:
INSERT INTO offc.vh_emp
(emp_id,
emp_name,
dob,
join_date,
leave_date,
Salary, -- this
dept_name, -- this
address_one,
address_two,
manager_name
)
SELECT e.emp_id EMP_ID,
e.emp_name EMP_NAME,
e.dob DOB,
e.join_date JOIN_DATE,
CASE
WHEN e.leave_date IS NULL THEN Trunc(sysdate)
ELSE e.leave_date
END AS LEAVE_DATE,
e.salary / 100 SALARY,
d.dept_name DEPT_NAME,
one.addr_name ADDRESS_ONE,
two.addr_name ADDRESS_TWO,
b.emp_name AS MANAGER_NAME
FROM offc.employee e
LEFT JOIN offc.department d
ON e.dept_id = d.dept_id
LEFT JOIN offc.add_line_one one
ON e.line1 = one.addr_id_one
LEFT JOIN offc.add_line_two two
ON e.line2 = two.addr_id_two
LEFT OUTER JOIN (SELECT *
FROM offc.employee) b
ON e.manager_id = b.emp_id;
Cheers!!
I have a query with NOT IN clause,need to convert into join statement.
SELECT EMP_NBR
FROM employees not in (select emp_id from departments where dept_id = 10 and division = 'sales')
I think the proper transformation would be a left join:
select EMP_NBR
from employees e join
departments d
on e.dept_id = d.dept_id and
d.dept_id = 10 and
d.division = 'sales'
where d.dept_id is null;
Note: I added what I consider to be correct JOIN conditions.
not in could be mimicked in SQL using just not in the where clause, e.g.
SELECT EMP_NBR FROM employees inner join department on
employees.emp_id =departments.emp_id
where NOT (dept_id = 10 and division = 'sales')
There are three tables: dept, emp, sal . You can find their structure and data in the images.
I need to extract the list of employees who have location as pune and have max salary in their department. Since there are five departments, the final output will contain five rows and columns of emp_id, dept, dept_id, salary.
I've tried...
select e.emp_id, dept,e.dept_id, max(sal) as 'highest salary'
from sal s,emp e,dept d
where e.emp_id = s.emp_id and d.dept_id = e.dept_id and loc ='Pune'
group by e.emp_id,e.dept_id,dept
order by e.dept_id
I would use apply :
select t.emp_id, d.dept, d.dept_id, t.sal
from dept d
cross apply ( select top 1 e.emp_id, s.sal as sal
from emp e
inner join sal s on s.emp_id = e.emp_id
where d.dept_id = e.dept_id and e.loc = 'Pune'
order by s.sal desc
) t;
Unless you have window functions (that depends on whether you're using MySQL, Oracle, SQLite, etc) you will need to do it in two steps.
Find the highest salary per department (for employees in 'Pune'), then another set of joins to find out who those people are.
SELECT
dep.dept,
dep.dept_id,
emp.emp_id,
sal.sal
FROM
emp
INNER JOIN
sal
ON sal.emp_id = emp.emp_id
INNER JOIN
(
SELECT
emp.dept_id,
MAX(sal.sal) AS max_sal
FROM
emp
INNER JOIN
sal
ON sal.emp_id = emp.emp_id
WHERE
emp.loc = 'Pune'
)
dep_sal
ON dep_sal.dept_id = emp.dept_id
AND dep_sal.max_sal = sal.sal
INNER JOIN
dep
ON dep.dept_id = emp.dept_id
WHERE
emp.loc = 'Pune'
ORDER BY
dep.dept,
emp.emp_id
EDIT: With SQL Server 2008 on-wards it's a bit easier...
WITH
emp_sal_ranked AS
(
SELECT
emp.dept_id,
emp.emp_id,
sal.sal,
RANK(sal.sal) OVER (PARTITION BY emp.dept_id
ORDER BY sal.sal
)
AS rank_sal
FROM
emp
INNER JOIN
sal
ON sal.emp_id = emp.emp_id
WHERE
emp.loc = 'Pune'
)
SELECT
dep.dept,
dep.dept_id,
emp_sal_ranked.emp_id,
emp_sal_ranked.sal
FROM
emp_sal_ranked
INNER JOIN
dept
ON dept.dept_id = emp_sal_ranked.dept_id
WHERE
emp_sal_ranked.rank_sal = 1
ORDER BY
dep.dept,
emp_sal_ranked.emp_id
I currently have a query to find if one employee is working on more than one assignment, and returns the names of the employees.
SELECT Eid, Count(Eid),
(SELECT Ename from Employee where Employee.Eid = Assignment.Eid) as EName
FROM Assignment
GROUP BY Eid HAVING Count(Eid) > 1;
Employee table has EID, and EName field. Assignment has EID and AssignmentNum.
Currently it works, but I think it can be written better with Inner Join, but I can't seem to compose a query with join that works. Any help would be appreciated.
You would just do:
SELECT a.Eid, Count(*), e.EName
FROM Assignment a JOIN
Employee e
ON e.Eid = a.Eid
GROUP BY a.Eid, e.name
HAVING Count(*) > 1;
SELECT e.ename, a.Eid, Count(a.Eid),
from Employee
Inner join assignment as a on e.Eid = a.Eid
GROUP BY a.Eid
HAVING Count(a.Eid) > 1;
You had the necessary join logic within the subquery.
As I was learning SQL statements I encountered one example (regarding the demo SCOTT database), I have no idea how to solve.
In which department(s) are all salgrades present?
My most promising approach is to group all salgrades and departments in the joined tables emp, dept and salgrade:
SELECT s.grade AS "Salgrade",
d.dname AS "Department ID"
FROM emp e INNER JOIN dept d ON(e.deptno = d.deptno)
INNER JOIN salgrade s ON(e.sal BETWEEN s.losal AND s.hisal)
GROUP BY d.dname, s.grade
Executing this gives me the following results:
If I could group this another time by department, COUNT(*) could give me the number of different salgrades per department. Then I could compare this number (with HAVING) to the following subselect:
(SELECT COUNT(*)
FROM salgrade)
Is there any possibility to group a table which already contains
GROUP BY?
Is there another (better) approach I could use?
I am using an apex-oracle-server with "Application Express 4.2.4.00.07"
Minor change from your version, by removing the grouping inside, and this version, first generates, salgrade and department of all employees, and then doing a grouping outside, counting distinct salary grades.
SELECT Department_ID
FROM
(
SELECT s.grade AS Salgrade,
d.dname AS Department_ID
FROM emp e
INNER JOIN dept d ON(e.deptno = d.deptno)
INNER JOIN salgrade s ON(e.sal BETWEEN s.losal AND s.hisal)
)
GROUP BY Department_ID
HAVING COUNT(distinct Salgrade) = ( SELECT count(1) FROM salgrade);
I found an even easier solution now:
SELECT d.dname
FROM emp e INNER JOIN dept d ON(e.deptno = d.deptno)
INNER JOIN salgrade s ON(e.sal BETWEEN s.losal AND s.hisal)
GROUP BY d.dname
HAVING COUNT(DISTINCT s.grade) = (SELECT COUNT(*) FROM salgrade);
Simple way would be - if performance is not a problem.
SELECT
COUNT(DISTINCT [Salgrade]) AS [COUNT]
,[Department ID]
FROM (SELECT s.grade AS "Salgrade",
d.dname AS "Department ID"
FROM emp e INNER JOIN dept d ON(e.deptno = d.deptno)
INNER JOIN salgrade s ON(e.sal BETWEEN s.losal AND s.hisal)
GROUP BY d.dname, s.grade) DEPT_SALE
GROUP BY [Department ID]
There could be better solutions though if we know more of your base tables - emp & salegrade