Multiple column Union Query without duplicates - sql

I'm trying to write a Union Query with multiple columns from two different talbes (duh), but for some reason the second column of the second Select statement isn't showing up in the output. I don't know if that painted the picture properly but here is my code:
Select empno, job
From EMP
Where job = 'MANAGER'
Union
Select empno, empstate
From EMPADDRESS
Where empstate = 'NY'
Order By empno
The output looks like:
EMPNO JOB
4600 NY
5300 MANAGER
5300 NY
7566 MANAGER
7698 MANAGER
7782 MANAGER
7782 NY
7934 NY
9873 NY
Instead of 5300 and 7782 appearing twice, I thought empstate would appear next to job in the output. For all other empno's I thought the values in the fields would be (null). Am I not understanding Unions correctly, or is this how they are supposed to work?
Thanks for any help in advance.

If you want the data in a separate column you will want a JOIN not a UNION:
Select e.empno, e.job, a.empstate
From EMP e
left join EMPADDRESS a
on e.empno = a.empno
Where job = 'MANAGER'
AND empstate = 'NY'
Order By e.empno
A UNION combines the two results into a single set but the data is listed in the same columns. So basically they are placed on top of one another:
select col1, col2, 'table1' as src
from table1
union all
select col1, col2, 'table2' as src
from table2
Will result in:
col1 | col2 | src
t1 | t1 | table1
t2 | t2 | table2
If you want to have the data in a separate column which is sounds like you do then you will use a join of the tables.

Bluefeet has the correct answer.
Think of joins as combining tables horizontally - you're adding more columns to the original query with each table you join.
Think of unions as stacking record sets vertically - you're adding extra rows to the same set of columns.

You need a JOIN for this..
Select e.empno, e.job, ea.empstate
From EMP e LEFT OUTER JOIN EMPADDRESS ea ON e.empno = ea.empno
Where e.job = 'MANAGER'
And ea.empstate = 'NY'
Order By e.empno
UNION is for taking 2 result sets with the same column names and merging them into one. In your example, its lumping column 2 (job and empstate) together, and taking the name from the first select.

i think you meant to write is as a join instead?
ie if you wanted empstate to be null for those employee's not in NY.
select empno, job, empstate
from emp e
left outer join empaddress a
on a.empno = e.empno
and e.empstate = 'NY'
where e.job = 'MANAGER';

this one works in oracle..by using union ..here inner query will fetch out the all the columns after that grouping with empno and rest of the columns is string concatenated
select EMPNO
,wm_concat(job) job
,wm_concat(EMPSTATE) EMPSTATE
from
( select EMPNO,job,'' as EMPSTATE from EMP Where job ='MANAGER'
union select EMPNO,'' as job, EMPSTATE from EMPADDRESS Where empstate ='NY'
)
group by EMPNO order by 1

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.

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.

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>

how can I select the other departments from this query

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.

Oracle SQL Syntax: Inner Join

I don't have access to an Oracle Database right now, so I'm posting my question here:
Is the following statement valid Oracle SQL Syntax?
SELECT a1
FROM t1 INNER JOIN t2
I'm particularly wondering whether we need to specify a join attribute for the inner join.
Best,
Will
You're missing ON
Like
SELECT a1
FROM t1 INNER JOIN t2
ON t1.SomeID = t2.SomeID
So, this is the query you're thinking of....
SQL> select e.ename
2 , d.dname
3 from emp e inner join dept d
4 /
from emp e inner join dept d
*
ERROR at line 3:
ORA-00905: missing keyword
SQL>
As we can see, it fails. The INNER JOIN syntax demands that we provide columns to join on ...
SQL> select e.ename
2 , d.dname
3 from emp e inner join dept d
4 on ( d.deptno = e.deptno )
5 /
ENAME DNAME
---------- --------------
SCHNEIDER ACCOUNTING
BOEHMER ACCOUNTING
KISHORE ACCOUNTING
ROBERTSON RESEARCH
...
FEUERSTEIN HOUSEKEEPING
PODER HOUSEKEEPING
TRICHLER HOUSEKEEPING
21 rows selected.
SQL>
There is an alternative syntax, the NATURAL JOIN. This syntax will automatically join the two tables on the basis of all columns which share the same name.
SQL> select e.ename
2 , d.dname
3 from emp e natural join dept d
4 /
ENAME DNAME
---------- --------------
SCHNEIDER ACCOUNTING
BOEHMER ACCOUNTING
KISHORE ACCOUNTING
ROBERTSON RESEARCH
...
FEUERSTEIN HOUSEKEEPING
PODER HOUSEKEEPING
TRICHLER HOUSEKEEPING
21 rows selected.
SQL>
This is a neat trick but really shouldn't be relied upon in production code; it is a bug waiting to happen.
You will need to add an ON clause
SELECT a1
FROM t1 INNER JOIN t2 on t1.a1=t2.a1
Yes, you have to specify join condition:
FROM t1 INNER JOIN t2 on t1.f = t2.f