SQL join without data loss - sql

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.

Related

One query with combining data from two tables and grouping (sql, oracle)

I am trying to write which create a view that shows the number of departments for each location (Loc) and the number of employees working in the departments in one query.
Tables are provided below.
Table Dept
DEPTNO
DNAME
LOC
10
ACCOUNTING
NEW YORK
20
RESEARCH
DALLAS
30
SALES
CHICAGO
40
OPERATIONS
BOSTON
50
RISK
BOSTON
Table Emp
EMPNO ENAME JOB MGR HIREDATE SAL COMM DEPTNO
---------- ---------- --------- ---------- --------- ---------- ---------- ----------
9901 Kowalski SALESMAN 9345 23-JAN-90 300 100 12
7369 SMITH CLERK 7902 17-MAR-80 15355.58 20
7499 ALLEN SALESMAN 7698 20-MAR-81 1600 300 30
7521 WARD SALESMAN 7698 22-MAR-81 1250 500 30
7566 JONES MANAGER 7839 02-MAR-81 57103.26 20
7654 MARTIN SALESMAN 7698 28-MAR-81 1250 1400 30
7698 BLAKE MANAGER 7839 01-MAR-81 2850 30
7782 CLARK MANAGER 7839 09-MAR-81 2450 10
7788 SCOTT ANALYST 7566 09-MAR-82 57583.15 20
7839 KING PRESIDENT 17-MAR-81 5000 10
7844 TURNER SALESMAN 7698 08-MAR-81 1500 0 30
7876 ADAMS CLERK 7788 12-MAR-83 21113.82 20
7900 JAMES CLERK 7698 03-MAR-81 950 30
7902 FORD ANALYST 7566 03-MAR-81 57583.15 20
7934 MILLER CLERK 7782 23-MAR-82 1300 10
The output should be
LOC NO_DEP NO_EMP
------------- ---------- ----------
NEW YORK 1 3
DALLAS 1 1
CHICAGO 1 5
BOSTON 2 6
So far I was able to create two separate queries which are provided below and which gives this result however I must do it in a one query which will give output in one table.
SELECT DISTINCT d.Loc, Count(d.Deptno) AS No_Dep, d.Deptno
FROM Dept d
GROUP BY d.Loc, d.Deptno;
SELECT DISTINCT COUNT(e.Empno) OVER (PARTITION BY e.Deptno) AS No_Emp, e.Deptno
FROM Emp e;
Correct solution is
SELECT d.LOC, COUNT(DISTINCT d.DEPTNO) AS NO_DEP, COUNT(e.EMPNO) AS NO_EMP
FROM DEPT d
LEFT JOIN EMP e ON d.DEPTNO = e.DEPTNO
GROUP BY D.LOC;
You first need to join them and then and then need to do the aggregation -
SELECT D.LOC, COUNT(DISTINCT DEPTNO) NO_DEP, COUNT(EMPNO) NO_EMP
FROM DEPT D
JOIN EMP E ON D.DEPTNO = E.DEPTNO
GROUP BY D.LOC;
you can use "group by" and "having" to solve such queries
The HAVING clause is used instead of WHERE with aggregate functions. While the GROUP BY Clause groups rows that have the same values into summary rows. The having clause is used with the where clause in order to find rows with certain conditions. The having clause is always used after the group By clause.
example:
select D.LOC AS LOC, COUNT(D.DEPTNO) AS NO_DEP, COUNT(distinct E.EMPNO) AS NO_EMP
from Dept D, Emp E
where D.DEPTNO=E.DEPTNO
group by D.LOC;

How to find minimum and maximum of a column with all other columns from the table

How to get MIN(salary) and MAX(salary) and all other columns such as name, DOB, phone number ...etc in oracle SQL query.
I have tried the following and its working fine but is there any other way using analytical functions or something like that .
SELECT
a.*
FROM
employees a
JOIN (
SELECT
MIN(salary) min_sal,
department_id
FROM
employees
GROUP BY
department_id
) b ON a.salary = min_sal
AND a.department_id = b.department_id
UNION
SELECT
a.*
FROM
employees a
JOIN (
SELECT
MAX(salary) max_sal,
department_id
FROM
employees
GROUP BY
department_id
) b ON a.salary = max_sal
AND a.department_id = b.department_id;
Something like this, I presume (based on Scott's sample emp table); query returns min and max salary per each department (that's what partition by clause says):
SQL> select deptno, empno, ename, job, sal,
2 min(sal) over (partition by deptno) min_sal,
3 max(sal) over (partition by deptno) max_sal
4 from emp
5 order by deptno, sal;
DEPTNO EMPNO ENAME JOB SAL MIN_SAL MAX_SAL
---------- ---------- ---------- --------- ---------- ---------- ----------
10 7934 MILLER CLERK 1495 1495 5750
10 7782 CLARK MANAGER 2818 1495 5750
10 7839 KING PRESIDENT 5750 1495 5750
20 7369 SMITH CLERK 920 920 3450
20 7876 ADAMS CLERK 1265 920 3450
20 7566 JONES MANAGER 3421 920 3450
20 7788 SCOTT ANALYST 3450 920 3450
20 7902 FORD ANALYST 3450 920 3450
30 7900 JAMES CLERK 998 998 2993
30 7654 MARTIN SALESMAN 1313 998 2993
30 7521 WARD SALESMAN 1313 998 2993
30 7844 TURNER SALESMAN 1575 998 2993
30 7499 ALLEN SALESMAN 1680 998 2993
30 7698 BLAKE MANAGER 2993 998 2993
14 rows selected.
SQL>

ORA-00913: too many values error when I run a query in SQL*Plus

I am trying to get the dname, loc, and count the ename's, plus I want to include the sal from the table. Can someone tell me what I am doing wrong.
Heres my statement with the error I get
SQL> select dname, loc, (select count(ename), sal from emp where DEPTNO = dept.deptno) as Number_of_people from dept;
select dname, loc, (select count(ename), sal from emp where DEPTNO = dept.deptno) as Number_of_people from dept
*
ERROR at line 1:
ORA-00913: too many values
SQL>
Heres my table
SQL> select empno, ename, job, hiredate, sal from emp;
EMPNO ENAME JOB HIREDATE SAL
---------- ---------- --------- --------- ----------
7839 KING PRESIDENT 17-NOV-81 5000
7698 BLAKE MANAGER 01-MAY-81 2850
7782 CLARK MANAGER 09-JUN-81 2450
7566 JONES MANAGER 02-APR-81 2975
7654 MARTIN SALESMAN 28-SEP-81 1250
7499 ALLEN SALESMAN 20-FEB-81 1600
7844 TURNER SALESMAN 08-SEP-81 1500
7900 JAMES CLERK 03-DEC-81 950
7521 WARD SALESMAN 22-FEB-81 1250
7902 FORD ANALYST 03-DEC-81 3000
7369 SMITH CLERK 17-DEC-80 800
EMPNO ENAME JOB HIREDATE SAL
---------- ---------- --------- --------- ----------
7788 SCOTT ANALYST 09-DEC-82 3000
7876 ADAMS CLERK 12-JAN-83 1100
7934 MILLER CLERK 23-JAN-82 1300
14 rows selected.
SQL>
Heres the second table
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
To get number of people and total Salary, try this...
select dept.dname, dept.loc,
count(emp.ename) as Number_of_people, sum(emp.sal) as Total_Salary
from emp
join dept on emp.DEPTNO = dept.deptno
group by dept.dname,dept.loc
You can get other things as well
select dept.dname, dept.loc,
count(emp.ename) as Number_of_people,
sum(emp.sal) as Total_Salary,
avg(emp.sal) as Average_salary,
min(emp.hiredate) as First_dept_hire,
max(emp.hireDate) as Last_dept_hire
from emp
join dept on emp.DEPTNO = dept.deptno
group by dept.dname,dept.loc
The scalar (inline) cursor can only have only one value in its projection.
If you want to have more than one value, use a join and aggregate all the values, as Sparky suggests.

I want to get all jobs that are in dept 30 including the location

I want to get all jobs that are in dept 30 including the location
SQL> select deptno,job from emp where deptno =30 (select loc from dept);
select deptno,job from emp where deptno =30 (select loc from dept)
*
ERROR at line 1:
ORA-00933: SQL command not properly ended
SQL>
Table emp
SQL> select empno, ename, job, hiredate, deptno from emp;
EMPNO ENAME JOB HIREDATE DEPTNO
---------- ---------- --------- --------- ----------
7839 KING PRESIDENT 17-NOV-81 10
7698 BLAKE MANAGER 01-MAY-81 30
7782 CLARK MANAGER 09-JUN-81 10
7566 JONES MANAGER 02-APR-81 20
7654 MARTIN SALESMAN 28-SEP-81 30
7499 ALLEN SALESMAN 20-FEB-81 30
7844 TURNER SALESMAN 08-SEP-81 30
7900 JAMES CLERK 03-DEC-81 30
7521 WARD SALESMAN 22-FEB-81 30
7902 FORD ANALYST 03-DEC-81 20
7369 SMITH CLERK 17-DEC-80 20
EMPNO ENAME JOB HIREDATE DEPTNO
---------- ---------- --------- --------- ----------
7788 SCOTT ANALYST 09-DEC-82 20
7876 ADAMS CLERK 12-JAN-83 20
7934 MILLER CLERK 23-JAN-82 10
14 rows selected.
Table dept
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
You need to join the two tables
SELECT deptno,
e.job,
d.loc
FROM emp e
JOIN dept d USING (deptno)
WHERE deptno = 30
SELECT empno, ename, job, hiredate, emp.deptno, location FROM emp
LEFT JOIN dept ON emp.deptno = dept.deptno
WHERE emp.deptno = 30

Getting data from two tables?

I am trying to get data from two different table and put into on statement but its not working. This is what I am looking to get as a complete statement: I want the query to display the dname, loc, Number of People. I am having a problems with the sub query.
SQL> select dname, loc from dept where ename in (count(ename) AS Number_of_People from emp);
select dname, loc from dept where ename in (count(ename) AS Number_of_People from emp)
*
ERROR at line 1:
ORA-00934: group function is not allowed here
SQL>
Table emp
SQL> select empno, ename, job, hiredate, deptno from emp;
EMPNO ENAME JOB HIREDATE DEPTNO
---------- ---------- --------- --------- ----------
7839 KING PRESIDENT 17-NOV-81 10
7698 BLAKE MANAGER 01-MAY-81 30
7782 CLARK MANAGER 09-JUN-81 10
7566 JONES MANAGER 02-APR-81 20
7654 MARTIN SALESMAN 28-SEP-81 30
7499 ALLEN SALESMAN 20-FEB-81 30
7844 TURNER SALESMAN 08-SEP-81 30
7900 JAMES CLERK 03-DEC-81 30
7521 WARD SALESMAN 22-FEB-81 30
7902 FORD ANALYST 03-DEC-81 20
7369 SMITH CLERK 17-DEC-80 20
EMPNO ENAME JOB HIREDATE DEPTNO
---------- ---------- --------- --------- ----------
7788 SCOTT ANALYST 09-DEC-82 20
7876 ADAMS CLERK 12-JAN-83 20
7934 MILLER CLERK 23-JAN-82 10
14 rows selected.
SQL>
Table dept
SQL> select * from dept;
DEPTNO DNAME LOC
---------- -------------- -------------
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
SQL>
Are you trying to achieve something like this?
select dname, loc, (select count(ename) from emp where DEPTNO = dept.deptno) as Number_of_people
from dept;
I'm not sure what you're trying to do, but something that sticks out is that you probably need to use a select in your subquery. Try:
select dname, loc
from dept
where ename in (select count(ename) AS Number_of_People from emp);
Try this
select dept.dname, dept.loc,count(*)
from emp
join dept on emp.deptNo=dept.deptno
group by dept.dname,dept.loc