How to do a Full Outer Join using old style Oracle syntax - oracle

I have two tables with the same columns, Item Code and Qty, for each Table:
TABLE A TABLE B
-------------- -------------
X 2 X 1
Y 1 S 2
Z 5 Z 5
The result that I am aiming to get is something like this:
Table C
---------------
X 2 1
Y 1 0
S 0 2
I only need the items where qty differs in both tables (including the nulls which should be shown as zeroes.
Note: I am using Oracle8 so I can't use the ANSI FULL OUTER JOIN.

Edit, Since the question is specific to Oracle 8 which does not use ANSI syntax, the following should work:
select col1,
nvl(a_col2, 0) as a_col2,
nvl(b_col2, 0) as b_col2
from
(
select a.col1, a.col2 as a_col2, b.col2 as b_col2
from TableA a, TableB b
where a.col1 = b.col1(+)
union
select b.col1, a.col2 as a_col2, b.col2 as b_col2
from TableA a, TableB b
where a.col1(+) = b.col1
)
where a_col2 <> b_col2
or (a_col2 is null or b_col2 is null)
See SQL Fiddle with Demo. This will return:
| COL1 | A_COL2 | B_COL2 |
--------------------------
| S | 0 | 2 |
| X | 2 | 1 |
| Y | 1 | 0 |
If you are using a version of Oracle that supports ANSI syntax then you can use the following FULL OUTER JOIN:
select
coalesce(a.col1, b.col1) col1,
coalesce(a.col2, 0) a_col2,
coalesce(b.col2, 0) b_col2
from tablea a
full outer join tableb b
on a.col1 = b.col1
where a.col2 <> b.col2
or (a.col2 is null or b.col2 is null);
See SQL Fiddle with Demo

Another writing of the query which should work in 8 and (probably earlier versions).
It uses neither FULL JOIN not the horrible (+) syntax for joins so it should work even when an upgrade deprecates it.
Assuming that there are no Nulls already on the tables, you won't need COALESCE() or NVL() either:
SELECT a.col1,
a.col2 AS a_col2,
b.col2 AS b_col2
FROM TableA a, TableB b
WHERE a.col1 = b.col1
AND ( a.col2 <> b.col2
OR a.col2 IS NULL
OR b.col2 IS NULL
)
UNION ALL
SELECT col1, col2, 0
FROM TableA a
WHERE NOT EXISTS
( SELECT *
FROM TableB b
WHERE a.col1 = b.col1
)
UNION ALL
SELECT col1, 0, col2
FROM TableB b
WHERE NOT EXISTS
( SELECT *
FROM TableA a
WHERE a.col1 = b.col1
) ;
Tests at SQL-Fiddle

select code, nvl(a.qty,0) a, nvl(b.qty,0) b
from tableA a full join tableB b using(code)
where decode(a.qty, b.qty, 0) is null
fiddle

Another option:
select
full_list.item_code,
nvl(table_a.qty,0) table_a_qty,
nvl(table_b.qty,0) table_b_qty
from
(select item_code from table_a
union
select item_code from table_b) full_list,
table_a,
table_b
where
full_list.item_code = table_a.item_code(+) and
full_list.item_code = table_b.item_code(+)

The following query should work just right:
SELECT * FROM (
SELECT nvl(a.c1, b.c2), nvl(a.col1, 0) qty1, nvl(b.col2, 0) qty2 FROM a FULL OUTER JOIN b ON a.c1 = b.c2
) where qty1 != qty2;
http://sqlfiddle.com/#!4/d37ff/5/0

Related

Query to retrieve rows with no data even if not satisfying the where clause condition in Oracle

I have a query two tables - TableA , TableB
Query :
select a.name, count(b.code)
from tableA a
join tableA b on b.id = a.id
where a.name = ('AA','BB','WWW')
group by a.name;
The data is available only for AA so it displays the result as
AA 5
But i want to display the data as :
AA 5
BB 0
WWW 0
You can collect expected names into separate table/subquery and then perform a left join with your data tables like
with exp_names as (
select 'AA' name from dual
union all
select 'BB' from dual
union all
select 'WWW' from dual
)
select en.name, count(b.code)
from exp_names en
left join tableA a on en.name = a.name
left join tableB b on b.id = a.id
group by en.name;
fiddle

Joining 2 tables for value comparision

I have 2 tables which contain data as follows:
Table1-
Col1. Col2
A1. B1
A1. B2
Table2-
Col1. Col2
A1. C1
A1. C2
Now when I am joining these 2 tables to compare col2, sometimes I get the output like A1-B1-C1;A1-B2-C2 and some time I get it as A1-B1-C2;A1-B2-C1.
But I don't want it to come in the second form ever.can anyone suggest any query which will help me achieve this.
Thanks in advance
You can do it with ROW_NUMBER() window function:
select t1.col1, t1.col2, t2.col2
from (
select t.*, row_number() over (partition by col1 order by col2) rn
from table1 t
) t1 inner join (
select t.*, row_number() over (partition by col1 order by col2) rn
from table2 t
) t2 on t2.col1 = t1.col1 and t2.rn = t1.rn
See the demo.
Results:
> COL1 | COL2 | COL2
> :--- | :--- | :---
> A1 | B1 | C1
> A1 | B2 | C2
The most obvious join is on col1 column which returns 4 rows:
SQL> with
2 t1 (col1, col2) as
3 (select 'A1', 'B1' from dual union all
4 select 'A1', 'B2' from dual
5 ),
6 t2 (col1, col2) as
7 (select 'A1', 'C1' from dual union all
8 select 'A1', 'C2' from dual
9 )
10 select a.col1, a.col2 a_col2, b.col2 b_col2
11 from t1 a join t2 b on a.col1 = b.col1
12 order by a.col1, a.col2, b.col2;
COL1 A_COL2 B_COL2
------- ------- -------
A1 B1 C1
A1 B1 C2
A1 B2 C1
A1 B2 C2
SQL>
These are all combinations you named, so - how do you "sometimes" get only two of them, and "sometimes" another two? What's wrong with another two results (as you said you don't want them to show at all)?
Yet another option is to use the RANK windows function as follows:
SELECT T1C1, T1C2, T2C2 FROM
(SELECT T1.COL1 T1C1, T1.COL2 T1C2, T2.COL1 T2C1, T2.COL2 T2C2,
RANK() OVER (PARTITION BY T1.COL1 ORDER BY T1.COL2) AS T1RN,
RANK() OVER (PARTITION BY T1.COL1 ORDER BY T2.COL2) AS T2RN
FROM TABLE1 T1 JOIN TABLE2 T2 ON T1.COL1 = T2.COL1)
WHERE T1RN = T2RN;

left outer join - what's the difference?

I sometimes get confused with LEFT JOINS and OR.
Here in the below two code samples, there is only one difference. There is a bracket in the WHERE clause in one sample and no bracket in other sample.
Obviously, I am getting different results. I want to understand what's happening.
Thanks.
SELECT FROM TABLE_A as A
LEFT OUTER JOIN TABLE_B as B
ON
A.col1 = B.col1 AND
A.col2 = B.col2 AND
A.col3 = B.col3
WHERE
B.col1 IS NULL OR B.col2 IS NULL OR B.col3 IS NULL AND
B.col4= 'R' AND
B.col5 != 'DR'
SELECT FROM TABLE_A as A
LEFT OUTER JOIN TABLE_B as B
ON
A.col1 = B.col1 AND
A.col2 = B.col2 AND
A.col3 = B.col3
WHERE
(B.col1 IS NULL OR B.col2 IS NULL OR B.col3 IS NULL) AND
B.col4= 'R' AND
B.col5 != 'DR'
(B.col1 IS NULL OR B.col2 IS NULL OR B.col3 IS NULL)
In second case, first the expression in the bracket is evaluated and then the following "AND" operators are applied. So the results if above bracket expression is ANDed with B.col4= 'R' and then it is ANDed with B.col5 != 'DR'
B.col1 IS NULL OR B.col2 IS NULL OR B.col3 IS NULL AND
B.col4= 'R' AND
B.col5 != 'DR'
In first case, it is evaluated like following:
(B.col1 IS NULL) OR (B.col2 IS NULL) OR (B.col3 IS NULL AND B.col4= 'R' AND B.col5 != 'DR')
The AND operator has higher precedence than OR, so your first WHERE clause can be written as this:
WHERE
B.col1 IS NULL OR B.col2 IS NULL OR (B.col3 IS NULL AND
B.col4= 'R' AND
B.col5 != 'DR')
But this may not be what you intended, if the second version of your WHERE clause is any evidence. If you want to check for either of the three columns being NULL ANDed with the other two conditions then you should use parentheses.
I think you've gotten very confused about left joins as well as the AND/OR operator precedence.
In your first query, the order is applied left to right, with ANDS being applied first.
That means that your first query is the equivalent of:
SELECT *
FROM table_a a
LEFT OUTER JOIN table_b b ON (a.col1 = b.col1
AND a.col2 = b.col2
AND a.col3 = b.col3)
WHERE b.col1 IS NULL
OR b.col2 IS NULL
OR (b.col3 IS NULL
AND b.col4 = 'R'
AND b.col5 != 'DR');
However, your second query is checking for nulls in any of the three join conditions (which would either be all null, or all not null, since you haven't catered for null values in your join conditions) as well as checking for the presence of values in the remaining two columns. Which doesn't make sense - if col1, col2 and col3 are null, it means that table_b row isn't part of the join, so col4 and col5 would be null as well.
Instead, what I think you meant to do was to make the and b.col4 = 'R' and b.col5 != 'DR' part of the left outer join conditions - ie. only join the rows from table_b where col4 = 'R' and col5 != 'DR' - like so:
WITH table_a AS (SELECT 1 col1, 1 col2, 1 col3 FROM dual UNION ALL
SELECT 2 col1, 2 col2, 2 col3 FROM dual UNION ALL
SELECT 3 col1, 3 col2, 3 col3 FROM dual),
table_b AS (SELECT 1 col1, 1 col2, 1 col3, 'R' col4, 'DP' col5 FROM dual UNION ALL
SELECT 2 col1, 2 col2, 2 col3, 'R' col4, 'DR' col5 FROM dual)
SELECT *
FROM table_a a
LEFT OUTER JOIN table_b b ON (a.col1 = b.col1
AND a.col2 = b.col2
AND a.col3 = b.col3
AND b.col4 = 'R'
AND b.col5 != 'DR');
COL1 COL2 COL3 COL1 COL2 COL3 COL4 COL5
---------- ---------- ---------- ---------- ---------- ---------- ---- ----
1 1 1 1 1 1 R DP
2 2 2
3 3 3
Your second query is not a LEFT OUTER JOIN:
SELECT *
FROM TABLE_A A
LEFT OUTER JOIN TABLE_B B
ON ( A.col1 = B.col1
AND A.col2 = B.col2
AND A.col3 = B.col3 )
WHERE ( B.col1 IS NULL OR B.col2 IS NULL OR B.col3 IS NULL )
AND B.col4= 'R'
AND B.col5 != 'DR'
The WHERE clause will only accept rows where B.col4 and B.col5 are NOT NULL and this will only ever be the case when the tables are joined and this means the join condition is effectively an INNER JOIN.
However, the conditions ( B.col1 IS NULL OR B.col2 IS NULL OR B.col3 IS NULL ) and ( A.col1 = B.col1 AND A.col2 = B.col2 AND A.col3 = B.col3 ) can never be fulfilled simultaneously since a NULL value cannot equal any other value (including another NULL). So the query should never return any rows.
The first query is:
SELECT *
FROM TABLE_A A
LEFT OUTER JOIN TABLE_B B
ON ( A.col1 = B.col1
AND A.col2 = B.col2
AND A.col3 = B.col3 )
WHERE B.col1 IS NULL
OR B.col2 IS NULL
OR ( B.col3 IS NULL AND B.col4= 'R' AND B.col5 != 'DR' )
This will return rows when B.col1 IS NULL OR B.col2 IS NULL but will never match on ( B.col3 IS NULL AND B.col4= 'R' AND B.col5 != 'DR' ) since the join condition will enforce the either B.col3 is not NULL (when there is a match in the LEFT OUTER JOIN) or B.col4 and B.col5 are both NULL (when there is no match in the LEFT OUTER JOIN).
What you might actually be trying to achieve (although it is unclear from your question) is:
SELECT *
FROM TABLE_A A
LEFT OUTER JOIN TABLE_B B
ON ( A.col1 = B.col1
AND A.col2 = B.col2
AND A.col3 = B.col3
AND B.col4 = 'R'
AND B.col5 != 'DR' )

Oracle Hierarchical Queries

I am struggling with a query with the below requirement:
Table A
ID Name Key
1 A1 Key1
2 A2 Key2
3 A3 Key3
Table B
ID A_ID NAME CONTAINER_A_ID
1 1 B1 NULL
2 1 B2 NULL
3 1 B3 2
4 2 B4 NULL
5 2 B5 NULL
6 3 B6 NULL
7 3 B7 NULL
The Key column in table A is unique
The A_ID column in table B is a foreign key of table A
The CONTAINER_A_ID column in table B means the row in table B can be a
container, it contains other data rows indicated by the CONTAINER_A_ID value.
Below is the example:
the input parameter is table A key column value, let's say A.Key = 'key1', and the result based on the above sample data will be:
A.ID A.NAME A.KEY B.ID B.A_ID B.NAME B.CONTAINER_A_ID
1 A1 KEY1 1 1 B1 NULL
1 A1 KEY1 2 1 B2 NULL
1 A1 KEY1 3 1 B3 2
2 A2 KEY2 4 2 B4 NULL
2 A2 KEY2 5 2 B5 NULL
if the input parameter is A.Key = 'key2', then the result will be:
A.ID A.NAME A.KEY B.ID B.A_ID B.NAME B.CONTAINER_A_ID
2 A2 KEY2 4 2 B4 NULL
2 A2 KEY2 5 2 B5 NULL
Thanks
This is on Oracle 11g.
If you are specifically looking for CONNECT BY I am not aware of that yet.
drop table t1; drop table t2;
create table t1 (id int primary key, name char(5), key char(5));
create table t2 (id int primary key, a_id int, name char(5) , container int);
insert into t1 values (1, 'A1', 'K1');
insert into t1 values (2, 'A2', 'K2');
insert into t1 values (3, 'A3', 'K3');
insert into t2 values (1, 1, 'B1', null);
insert into t2 values (2, 1, 'B2', null);
insert into t2 values (3, 1, 'B3', 2);
insert into t2 values (4, 2, 'B4', null);
insert into t2 values (5, 2, 'B5', null);
insert into t2 values (6, 3, 'B6', null);
insert into t2 values (7, 3, 'B7', null);
with t(id, name, key, bid, aid, bname, con) as (
select a.id, a.name, a.key, b.id, b.a_id, b.name, b.container
from t1 a
inner join
t2 b
on a.id = b.a_id
where a.key = 'K1'
union all
select a.id, a.name, a.key, b.id, b.a_id, b.name, b.container
from t t
inner join
t1 a
on a.id = t.con
inner join
t2 b
on a.id = b.a_id
) select * from t;
EDIT: Reponse to Jorge's comment
insert into t2 values (4, 2, 'B4', 3);
This is for Hierarchical Query
with TableA as
(
select 1 id, 'A1' Name, 'Key1' key from dual union all
select 2, 'A2', 'Key2' from dual union all
select 3, 'A3', 'Key3' from dual
)
, tableb as
(
select 1 id, 1 a_id, 'B1' name , null CONTAINER_A_ID from dual union all
select 2 , 1 , 'B2' , null from dual union all
select 3 , 1 , 'B3' , 2 from dual union all
select 4 , 2 , 'B4' , null from dual union all
select 5 , 2 , 'B5' , null from dual union all
select 6 , 3 , 'B6' , null from dual union all
select 7 , 3 , 'B7' , null from dual
)
select
a.id, a.name, a.key, b.id, b.a_id, b.name, b.container_a_id
from
tableb b
left join
tablea a
on
a.id = b.a_id
start with
A.Key = 'Key1'
connect by
prior b.container_a_id = b.a_id;
If you need order then add order by a.id, b.id,a.name,...; to the end.
CTEs are fine to use in 11g Oracle. I just saw Jorge is in before me. It is easier to see how the recursion works if you just use tableB in the CTE and then join to the CTE to get all the fields, like this
with
recurse (a_id, b_id, parent_id)
as
(select a_id, id, container_a_id as parent_id
from tableB
WHERE A_ID = 1 -- Put your parameter here
union all
select b.a_id, b.id, b.container_a_id
from recurse r, tableB b
where b.a_id = r.parent_id
)
select r.a_id, a.name, a.key, b.id, b.a_id, b.name, b.container_a_id
from recurse r, tableA a, tableB b
where r.a_id = a.id and r.b_id = b.id
;
This gets the same results, but although you have to use a_id and not a_key for the condition, it is a little bit easier to understand the recursion.
So, leaving this here in case it helps someone to understand a bit about CTEs.

oracle insert into column using subquery

I want to insert data into a column in the table.
Table a
ID col1 col2
1 A null
2 B null
Table b
ID col1
1 C
2 D
Expected results:
Table A
ID col1 col2
1 A C
2 B D
I tried this:
insert into tableA (col2)
select b.col1
from tableB b , tableA a
where b.id = a.id
and I received
0 row inserted.
How do I insert the col1 in B into col2 in A for the matching 'id' columns?
Thank you.
You must use Merge statement when inserting based on joins.
Also in table tableA col2 already exist but you want to insert a value on join then you must update that column.
merge into tablea a
using tableb b
on (b.id = a.id)
when matched
then
update set a.col2 = b.col1;
What you want to do shouldn't require a subquery. I'm not a huge fan of the table a, table b notation, try this:
update a
set col2 = b.col1
from tableB b
join tableA a
on a.id = b.id

Resources