INNER JOIN condition not working - sql

Suppose we have table as :
EMPNO ENAME DEPTNO
------ ------ ----------
7369 SMITH 20
7499 ALLEN 30
7654 MARTIN 30
7788 SCOTT 20
7839 KING 10
7900 JAMES 30
How can we find all those employees who work in the same deptno as of SCOTT ?
I tried as follows :
SELECT e.ename FROM employee e INNER JOIN employee m ON e.deptno=m.deptno;
, but the result isn't correct.

Try to add one more condition as below
SELECT e.ename
FROM employee e
INNER JOIN employee m ON e.deptno=m.deptno
AND m.ENAME = 'SCOTT'
WHERE m.ENAME <> 'SCOTT'; -- exclude scott
You can also try with a subquery
SELECT e.ename
FROM employee e
WHERE EXISTS
(
SELECT 1
FROM employee M
WHERE M.ENAME = 'SCOTT'
and E.deptno=M.deptno
)
AND m.ENAME <> 'SCOTT'; -- exclude scott;

Related

ORDER BY with DISTINCT gives ORA-01791: not a SELECTed expression

I trying to use oracle order by with select statement but it causes an Exception:
ORA-01791: not a SELECTed expression.
select distinct usermenu.menuname
from usermenu, userpermission
where userpermission.menuno = usermenu.menuno
and userpermission.userno = 1
order by userpermission.menuno;
When there's DISTINCT or an aggregate function in the SELECT statement's column list, ORDER BY a column which isn't part of the SELECT column list won't work.
Here's an example, based on Scott's schema.
This works OK, although D.LOC isn't selected:
SQL> select d.dname, e.ename
2 from dept d join emp e on e.deptno = d.deptno
3 order by d.loc;
DNAME ENAME
-------------- ----------
SALES BLAKE
SALES TURNER
SALES ALLEN
SALES MARTIN
SALES WARD
SALES JAMES
RESEARCH SCOTT
RESEARCH JONES
RESEARCH SMITH
RESEARCH ADAMS
RESEARCH FORD
ACCOUNTING KING
ACCOUNTING MILLER
ACCOUNTING CLARK
14 rows selected.
Now, add DISTINCT - basically, that's what you have:
SQL> select distinct d.dname, e.ename
2 from dept d join emp e on e.deptno = d.deptno
3 order by d.loc;
order by d.loc
*
ERROR at line 3:
ORA-01791: not a SELECTed expression
The same goes for aggregate functions, such as COUNT:
SQL> select d.dname, e.ename, count(*)
2 from dept d join emp e on e.deptno = d.deptno
3 group by d.dname, e.ename
4 order by d.loc;
order by d.loc
*
ERROR at line 4:
ORA-00979: not a GROUP BY expression
SQL>
So, what to do? Order by something else. Alternatively, use the current query as an inline view, join it with the table that contains the column you'd want to order the result by and it'll work:
SQL> select x.dname, x.ename
2 from (select distinct d.dname, e.ename
3 from dept d join emp e on e.deptno = d.deptno
4 ) x
5 join dept d1 on d1.dname = x.dname
6 order by d1.loc;
DNAME ENAME
-------------- ----------
SALES TURNER
SALES JAMES
SALES BLAKE
SALES WARD
SALES MARTIN
SALES ALLEN
RESEARCH SMITH
RESEARCH FORD
RESEARCH ADAMS
RESEARCH SCOTT
RESEARCH JONES
ACCOUNTING MILLER
ACCOUNTING KING
ACCOUNTING CLARK
14 rows selected.
SQL>

find emp_names,max,min salary and number of employees on each department?

I use oracle 11g , so i have 2 tables(employees,departments):
desc employees: desc departments
EMPLOYEE_ID DEPARTMENT_ID
FIRST_NAME DEPARTMENT_NAME
LAST_NAME MANAGER_ID
EMAIL LOCATION_ID
PHONE_NUMBER
HIRE_DATE
JOB_ID
SALARY
COMMISSION_PCT
MANAGER_ID
DEPARTMENT_ID
and i want to get
employee_name,emp_names,emp_salary,dep_id,dep_names,max salary on each
dep , and min salary on each dep , and number of employees on each
department .
so i do this qouery:
select FIRST_NAME,DEPARTMENT_ID,max(SALARY),min(SALARY),count(EMPLOYEE_ID)
from employees join departments on employees.department_id = departments.departm
ent_id group by first_name,department_id;
but its give an error:
ERROR at line 1: ORA-00918: column ambiguously defined
however does my sql query right ?
I don't have your tables so I created views out of Scott's ones, to simulate what you have.
SQL> create or replace view employees as
2 select empno employee_id,
3 ename last_name,
4 deptno department_id,
5 sal salary
6 from emp;
View created.
SQL> create or replace view departments as
2 select deptno department_id,
3 dname department_name
4 from dept;
View created.
SQL>
Here's how I understood the question: list of employees per department should be separated from the rest (minimums, maximums, counts).
So: list of employees:
SQL> select d.department_name, e.last_name
2 from departments d join employees e on d.department_id = e.department_id
3 order by d.department_name;
DEPARTMENT_NAM LAST_NAME
-------------- ----------
ACCOUNTING CLARK
ACCOUNTING KING
ACCOUNTING MILLER
RESEARCH JONES
RESEARCH FORD
RESEARCH ADAMS
RESEARCH SMITH
RESEARCH SCOTT
SALES WARD
SALES TURNER
SALES ALLEN
SALES JAMES
SALES BLAKE
SALES MARTIN
14 rows selected.
Aggregates: outer join for departments that don't have any employees:
SQL> select d.department_name,
2 min(e.salary) min_sal,
3 max(e.salary) max_sal,
4 count(e.employee_id) cnt_emp
5 from departments d left join employees e on d.department_id = e.department_id
6 group by d.department_name
7 order by d.department_name;
DEPARTMENT_NAM MIN_SAL MAX_SAL CNT_EMP
-------------- ---------- ---------- ----------
ACCOUNTING 1300 5000 3
OPERATIONS 0
RESEARCH 800 3000 5
SALES 950 2850 6
LISTAGG allows you to list all employees per department in the same statement, though; see line 5. I, somehow, doubt that you learnt about that function yet (as you struggle with such a problem).
SQL> select d.department_name,
2 min(e.salary) min_sal,
3 max(e.salary) max_sal,
4 count(e.employee_id) cnt_emp,
5 listagg(e.last_name, ', ') within group (order by e.last_name) employees
6 from departments d left join employees e on d.department_id = e.department_id
7 group by d.department_name
8 order by d.department_name;
DEPARTMENT_NAM MIN_SAL MAX_SAL CNT_EMP EMPLOYEES
-------------- ---------- ---------- ---------- -------------------------------------------
ACCOUNTING 1300 5000 3 CLARK, KING, MILLER
OPERATIONS 0
RESEARCH 800 3000 5 ADAMS, FORD, JONES, SCOTT, SMITH
SALES 950 2850 6 ALLEN, BLAKE, JAMES, MARTIN, TURNER, WARD
SQL>

i want to find all managers names and their all managers name in oracle SQL

select e.last_name, ee.last_name
from employees e join employees ee on (e.manager_id = ee.employee_id)
where last_name in (select last_name
from employees
where employee_id in (select distinct manager_id
from employees
)
);
If I understood you correctly, you want to select managers only.
The following example is based on Scott's schema.
Here are all employees, along with their managers (hierarchically) so that it is easier to spot the managers.
SQL> select lpad(' ', (level - 1) * 2) || ename ename
2 from emp
3 start with mgr is null
4 connect by prior empno = mgr;
ENAME
------------------------------------------------------
KING -- manager
JONES -- manager
FORD -- manager
SMITH -- employee
BLAKE -- manager
ALLEN -- employee
WARD -- employee
MARTIN -- employee
TURNER -- employee
JAMES -- employee
CLARK -- manager
MILLER -- employee
12 rows selected.
SQL>
Additional WHERE clause returns managers only:
SQL> select lpad(' ', (level - 1) * 2) || ename ename
2 from emp
3 where empno in (select mgr from emp)
4 start with mgr is null
5 connect by prior empno = mgr;
ENAME
--------------------------------------------------------
KING
JONES
FORD
BLAKE
CLARK
SQL>
Substituting what is getting selected with SYS_CONNECT_BY_PATH, we'll get a different output:
SQL> select sys_connect_by_path(ename, ' / ') path
2 from emp
3 where empno in (select mgr from emp)
4 start with mgr is null
5 connect by prior empno = mgr;
PATH
----------------------------------------------------
/ KING
/ KING / JONES
/ KING / JONES / FORD
/ KING / BLAKE
/ KING / CLARK
SQL>
Or, following your steps, with a self-join of the EMP table, we get
SQL> select m1.ename manager, m2.ename his_manager
2 from emp m1 join emp m2 on m1.mgr = m2.empno
3 where m1.empno in (select mgr from emp);
MANAGER HIS_MANAGER
---------- -------------
FORD JONES
CLARK KING
BLAKE KING
JONES KING
SQL>
Pick the one that suits you best. I believe other members will suggest other options.

SQL join without data loss

I want a select that naturally joins 2 tables. Afer joining table A with table B the new temporary table C doesn't contain the row of table A if the primary key of this row is not used in any rows of table B. I understand why that happens, but I want the row also in table C.
Example
select deptno, dname, loc, count(deptno) empcount, round(avg(sal),2) avgsal
from dept natural join emp
group by deptno, dname, loc
Result:
DEPTNO DNAME LOC EMPCOUNT AVGSAL
---------- -------------- ------------- ---------- ----------
20 RESEARCH DALLAS 5 2175
10 ACCOUNTING NEW YORK 3 2916.67
30 SALES CHICAGO 6 1566.67
What I want:
DEPTNO DNAME LOC EMPCOUNT AVGSAL
---------- -------------- ------------- ---------- ----------
20 RESEARCH DALLAS 5 2175
10 ACCOUNTING NEW YORK 3 2916.67
30 SALES CHICAGO 6 1566.67
40 OPERATIONS BOSTON 0 0
Table dept (A):
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
Table emp (B):
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
7369 SMITH CLERK 7902 17-DEC-80 800 20
7499 ALLEN SALESMAN 7698 20-FEB-81 1600 300 30
7521 WARD SALESMAN 7698 22-FEB-81 1250 500 30
7566 JONES MANAGER 7839 02-APR-81 2975 20
7654 MARTIN SALESMAN 7698 28-SEP-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAY-81 2850 30
7782 CLARK MANAGER 7839 09-JUN-81 2450 10
7788 SCOTT ANALYST 7566 19-APR-87 3000 20
7839 KING PRESIDENT 17-NOV-81 5000 10
7844 TURNER SALESMAN 7698 08-SEP-81 1500 0 30
7876 ADAMS CLERK 7788 23-MAY-87 1100 20
7900 JAMES CLERK 7698 03-DEC-81 950 30
7902 FORD ANALYST 7566 03-DEC-81 3000 20
7934 MILLER CLERK 7782 23-JAN-82 1300 10
select deptno, dname, loc, count(deptno) empcount, round(avg(sal),2) avgsal
from dept
LEFT join emp
group by deptno, dname, loc
Is this what you are looking at? Check the output...
According to http://docs.oracle.com/javadb/10.8.3.0/ref/rrefsqljnaturaljoin.html you can do an outer natural join like so:
The following example is similar to the one above, but it also preserves
unmatched rows from the first (left) table:
SELECT * FROM COUNTRIES NATURAL LEFT JOIN CITIES
So you would do:
select deptno, dname, loc, count(deptno) empcount, round(avg(sal),2) avgsal
from dept natural left join emp
group by deptno, dname, loc
Use left join instead fo natural join.
select deptno, dname, loc, count(deptno) empcount, round(coalesce(avg(sal), 0),2) avgsal
from dept left join emp
group by deptno, dname, loc
Thanks a lot, it was the outer join I was looking for.
So here's the sql command that deliveres the result I was looking for:
SQL99-Syntax:
select deptno, dname, loc, nvl(ecount,0) empcount, nvl(round(avg(sal),2),0) avgsal
from dept left join emp using(deptno)
left join (select deptno, count(deptno) ecount
from emp
group by deptno) using(deptno)
group by deptno, dname, loc, ecount
Standard SQL-Syntax:
select d.deptno, d.dname, d.loc, nvl(n.ecount,0) empcount, nvl(round(avg(e.sal),2),0) avgsal
from dept d, emp e, (select deptno, count(deptno) ecount
from emp
group by deptno) n
where d.deptno = e.deptno (+)
and n.deptno (+) = e.deptno
group by d.deptno, d.dname, d.loc, n.ecount
The column empcount is 0 if there are no employees in the department. In the other solutions it was 1 because there was one row for each department after the outer join in the temporary table.

Why is this query displaying all the records?

I have a query:
SELECT emp.empno , emp.ename , emp.job , emp.sal , dept.dname , dept.loc
FROM emp ,
dept
WHERE emp.ename = 'SMITH';
Please tell me why this is displaying all the Records , as i am expecting only one record ?
EMPNO ENAME JOB SAL DNAME LOC
---------- ---------- --------- ---------- -------------- -------------
7369 SMITH CLERK 800 ACCOUNTING NEW YORK
7369 SMITH CLERK 800 RESEARCH DALLAS
7369 SMITH CLERK 800 SALES CHICAGO
7369 SMITH CLERK 800 OPERATIONS BOSTON
7369 SMITH CLERK 800 CREDIT
The reason is because your query lacks JOIN criteria to link the two tables, so the result will be a cartesian product. Every EMP record will have a copy of every row in the DEPT table...
Your query uses ANSI-89 join syntax, which requires the criteria to be in the WHERE clause:
SELECT e.empno, e.ename, e.job, e.sal, d.dname, d.loc
FROM EMP e,
DEPT d
WHERE d.deptno = e.deptno
AND e.ename = 'SMITH'
But it would be preferable to use the ANSI-92 format:
SELECT e.empno, e.ename, e.job, e.sal, d.dname, d.loc
FROM EMP e
JOIN DEPT d ON d.deptno = e.deptno
WHERE e.ename = 'SMITH'
You need to join the tables, not select from each. The DB doesn't know the relation to emp and dept without a join.
Try:
select emp.empno , emp.ename , emp.job , emp.sal , dept.dname , dept.loc from emp inner
join dept on emp.deptno = dept.deptno where
emp.ename = 'SMITH';
Because you have not specified how to join emp and dept, and so have a Cartesian product (all possible combinations).
Try:
select emp.empno , emp.ename , emp.job , emp.sal , dept.dname , dept.loc
from emp
join dept on dept.deptno = emp.deptno
where emp.ename = 'SMITH';
It is because of your FROM emp , dept clause. I expect that 'SMITH' only belongs in one department and you really need to do a JOIN instead.
Because this is the result of a cross-join between these two tables (emp, dept). You are missing join condition, i.e.
where dept.deptno = emp.deptno and emp.ename = 'SMITH';
You need to join your emp and dept tables, as you are currently getting 1 row for each dept as there is no restriction applied to that table.
There is probably an emp.deptid column on the emp table, or some other joining table in the database.