how can I select the other departments from this query - sql

So I have the following query
SELECT deptno, COUNT(deptno) number_of_jobs
FROM emp
WHERE job = 'SALESMAN'
GROUP BY deptno
ORDER BY number_of_jobs ASC;
What this does is to return me just department 30, while I want departments 10 and 20 aswell.
Like this
deptno number_of_jobs
10 (here it can be null or 0 doesn't really matter)
20 (same like dept 10)
30 4
I know this is fairly easy and I believe it's done using a join condition, but I just can't get my head around it.
Thanks guys!

You need to LEFT JOIN this with your department table. A LEFT JOIN returns all the rows from the left table, even if they don't have matches in the right table.
SELECT d.deptno, COUNT(e.deptno) number_of_jobs
FROM dept d
LEFT JOIN emp e ON e.deptno = d.deptno AND e.job = 'SALESMAN'
GROUP BY d.deptno
ORDER BY number_of_jobs ASC
Note that you have to use COUNT(e.deptno) rather than COUNT(*), otherwise it will count the row with null values from the emp table. When you give a column name to COUNT, it only counts the non-null values of that column, so you'll get 0 for the rows with no match.

Related

Left outer join with a condition

I haven't been able to confirm an answer to this question anywhere... I have some code like this (PL/SQL):
select
tbl1.ID
, tbl1.col1
, tbl1.col2
, tbl2.col1
from
table1 tbl1
, table2 tbl2
where
(tbl1.ID = tbl2.ID(+)
and tbl2.col2 = 12345)
I'm left outer joining to table2 (tbl2 could have NULLs), indicated by the "(+)" notation, but I also need to filter by tbl2.col2 = 12345. Is it still a left outer join if I'm adding that condition, or is there a way to specify that tbl2.col2 = 12345 should also be a left join? In other words, I'm concerned that I might need to do something like "tbl2.col2 = 12345(+)" but I'm not sure either.
Thanks for any information. Not very familiar with PL/SQL, and having debug auto-generated PL/SQL code at that!
You don't have to use the "old" Oracle's outer join operator (+); Oracle supports JOINs since version 9i (unless I'm wrong), and that's quite a long ago.
I didn't understand what you want to get as a result, but - it makes a difference whether you put that condition into the join, or apply it as a filter in where clause.
If you know Scott's sample schema, dept table contains department 40 while no employees in emp table work in that department. Therefore, it can be used to illustrate what you are asking. Compare the following queries:
Here, deptno = 40 is part of the JOIN:
SQL> select d.deptno, d.dname, e.ename
2 from dept d left join emp e on e.deptno = d.deptno and d.deptno = 40
3 order by d.deptno, e.ename;
DEPTNO DNAME ENAME
---------- -------------- ----------
10 ACCOUNTING
20 RESEARCH
30 SALES
40 OPERATIONS
In another example, deptno = 40 is in where clause and is used as a filter:
SQL> select d.deptno, d.dname, e.ename
2 from dept d left join emp e on e.deptno = d.deptno
3 where d.deptno = 40
4 order by d.deptno, e.ename;
DEPTNO DNAME ENAME
---------- -------------- ----------
40 OPERATIONS
SQL>
As I said: I don't know which option you want, but I hope that those examples will help you decide whether you need the 1st or the 2nd query.

How does the WITH clause know which table to work with?

I came across the following SQL statement in a book:
WITH dcount AS
(
SELECT deptno, COUNT(*) AS dcount
FROM employees
GROUP BY deptno
)
SELECT
e.lname EMP_lastname,
e.deptno e_dept,
d1.dcount edept_count,
m.lname manager_name,
m.deptno mdept,
d2.dcount mdept_count
FROM
employees e,
dcount d1,
employees m,
dcount d2
WHERE
e.deptno = d1.deptno
AND e.mgr = m.empno
AND m.deptno = d2.deptno
AND e.mgr = 7839
The purpose of the statement is to display an employees name, the department they work in, the number of coworkers in the department, the manager's name, and the number of people in the manager's department.
The part that confuses me is where it says d1.count and d2.count. How is it counting different things if the count is based on the same WITH statement?
The main query is joining the same CTE but is using different joining predicates each time:
It's joining d1 using e.deptno = d1.deptno.
It's joining d2 using m.deptno = d2.deptno.
Therefore each resulting row is picking a different row from the CTE.

joining 2 table and bringing particular record at top

I have 2 tables. first table has lot of rows but second table has only either 1 or 0 record. If second table contains one record then that record of first table should come at top. if second table contains no record then all the record of first table should display.
first table:
DEPT:-
10 ACCOUNTING NEW YORK
20 RESEARCH DALLAS
30 SALES CHICAGO
40 OPERATIONS BOSTON
Second table
EMP:-
1000 Shruti 40
for eg if I join table then deptno 40 of dept table should come at top. I wrote below query and it is working
select d.deptno,d.dname ,d.loc,e.empno,e.deptno from dept d ,emp e where d.deptno= e.deptno(+) and d.deptno in(select deptno from emp)
UNION ALL
select d.deptno,d.dname ,d.loc,e.empno,e.deptno from dept d ,emp e where d.deptno= e.deptno(+) and d.deptno not in(select deptno from emp);
How can I modify above query so that all the record of dept table should display if table emp has no record.
I think you want a left join and a conditional sort:
select d.*, e.empno
from dept d
left join emp e on e.deptno = d.deptno
order by case when e.deptno is not null then 0 else 1 end, d.deptno
Depending on your data, you might be able to simplify this as:
order by e.deptno, d.deptno
This takes advantage of the fact that an ascending sort puts null values last.

order by and rownum in inner query

I've 2 linked tables A an B 1 to n
For all records of A I'd like to have the record of B matching the following conditions
A.dep=B.dep
There is no record in B having the same year of A
I want the maching record of B with the year beeing the latest year before the year of A
If There is more than one record take the one with the latest updateDate
If There is more than one record take the one with the latest creationDate
If There is more than one record take the one with the latest Id
I tried the Following
Select ...
from A
...
left join B same on A.dep=same.dep and A.year=same.year
left join B last on A.dep=last.dep and A.year>last.year
where ...
and same.id is null
and (last.id is null or
last.id = (select id from B where dep=A.dep and rownum=1 order by year desc, updateDate desc, creationDate desc, id desc))
But I've an error :
00907. 00000 - "missing right parenthesis"
But all the parenthesis are ok.
Looks like the inner select doesn't support the order by
Any idea?
It is ORDER BY in a subquery.
If you run it in SQL*Plus (or some tool that shows error position), you'd get
SQL> select *
2 from dept d
3 where 1 = 1
4 and d.deptno = (select e.deptno from emp e where e.ename = 'KING' order by e.sal);
and d.deptno = (select e.deptno from emp e where e.ename = 'KING' order by e.sal)
*
ERROR at line 4:
ORA-00907: missing right parenthesis
So:
SQL> select *
2 from dept d
3 where 1 = 1
4 and d.deptno = (select e.deptno from emp e where e.ename = 'KING');
DEPTNO DNAME LOC
---------- -------------------- --------------------
10 ACCOUNTING NEW YORK
SQL>
Indeed, inner sub-query does not support order by because it will give a partial resultset which will be manipulated further by the outer query.
Use order by to sort the result based on your requirements once the resultset corresponding to other filter conditions is obtained.
So, in your case, get the query to match your filter conditions first and then apply order by based on the requirements. But, if you do want the inner sub-query results to be ordered first before applying it to outer/main query, use a join. Create a resultset based on the inner sub-query first and join it with an alias to the other/main query results with appropriate join conditions.
Assuming b.id is unique, you can do:
select a.*, b.*
from a left join
b
on b.dep = a.dep and
b.year <= a.year and
b.id = (select max(b2.id) keep (dense_rank first order by b2.year desc, b2.updatedate desc, b2.creationdate desc, b2.id desc)
from b b2
where b2.dep = b.dep
);

showing columns with Oracle SQL

I work with tables that have lots of columns that don't fit on my screen. When I create SQL I am really looking at a few columns worth of information.
What is a better way of moving a column from the right side to the first left most column? That way my results will show the column I am interested in first.
Currently I am using
Select ColumnNumber201, t.*
from TableName t
I have a feeling this will add extra results to my query if I was concerned about counts, because it is a join. I would like avoid joins if possible.
Thanks for your help in advance.
We are using Oracle DB SQL
Well, this is the way to display columns you're interested in - name them. In order to put them to the "front" (i.e. to be the leftmost), do exactly what you are doing.
I don't understand your concern about counts. What "join" are you talking about? There's no join in a statement you posted. This:
Select ColumnNumber201, t.*
from TableName t
selects only from one table.
Even if you join it to another table, it is the join condition and where clause that matters, not number of columns you select (OK, aggregates change things, but that's another story).
For example:
SQL> select count(*) one_column_one_table
2 from (select e.ename
3 from emp e);
ONE_COLUMN_ONE_TABLE
--------------------
14
SQL>
SQL> select count(*) many_columns_one_table
2 from (select e.ename, e.*
3 from emp e);
MANY_COLUMNS_ONE_TABLE
----------------------
14
SQL>
SQL> select count(*) many_columns_two_tables
2 from (select d.dname, e.ename, e.*, d.*
3 from emp e join dept d on e.deptno = d.deptno);
MANY_COLUMNS_TWO_TABLES
-----------------------
14
SQL>
SQL> select count(*) many_columns_two_tables_where
2 from (select d.dname, e.ename, e.*, d.*
3 from emp e join dept d on e.deptno = d.deptno
4 where d.deptno = 20);
MANY_COLUMNS_TWO_TABLES_WHERE
-----------------------------
5
SQL>