Linking multiple tables with an Oracle outer join - sql

Struggling with Oracle outer join syntax.
We have this query with inner and outer joins;
SELECT A.aa, B.bb, C.cc, D.dd
FROM
TABLEA A, TABLEB B, TABLEC C, TABLED D
WHERE
A.XX = B.XX AND
B.YY = C.YY AND
C.ZZ = D.WW (+)
The query works fine. A change is now it's possible that the link between table A and B (on XX) may not be present.
So we'd like to turn this into an outer join which returns data regardless of whether the existing joins are satisfied OR if there is no link between A and B (and the other tables).
How can you do this?

Say you have your tables like the following:
insert into tableA values (1);
insert into tableA values (2);
insert into tableB values ( 1, 10);
insert into tableB values ( -2, 20);
insert into tableC values ( 10, 100);
insert into tableC values ( 20, 200);
insert into tableD values ( 200);
insert into tableD values ( 999);
If I understand well, you need to use on outer join even on B and C, not only D; in the old Oracle syntax this is:
SELECT *
FROM
TABLEA A, TABLEB B, TABLEC C, TABLED D
WHERE
A.XX = B.XX(+) AND
B.YY = C.YY(+) AND
C.ZZ = D.WW (+)
And in (better) ANSI SQL:
select *
from tableA A
left outer join
tableB B on ( A.xx = B.xx)
left outer join
tableC C on ( B.yy = C.yy)
left outer join
tableD D on ( C.zz = D.ww)
They both give:
XX XX YY YY ZZ WW
---------- ---------- ---------- ---------- ---------- ----------
2
1 1 10 10 100

Related

Multiple tables joined to a table via single column

I am trying to create query, on below scenario.
with my skills I am able to join Table A,A1,B and A,A1,C and A,A1,D individually and union them.
Is there any better way to achieve same. I am using Oracle as Database.
It all depends on what they mean and if you need to know the columns the values are from.
This would get all the columns and you would have NULL values from the non-matching B, C, D tables:
SELECT *
FROM a1
INNER JOIN a ON a1.aid = a.id
LEFT OUTER JOIN b ON a.extid = b.extid
LEFT OUTER JOIN c ON a.extid = c.extid
LEFT OUTER JOIN d ON a.extid = d.extid
Or, this would get only the relevant values and give you the type they belong to in fewer columns:
SELECT *
FROM a1
INNER JOIN a ON a1.aid = a.id
INNER JOIN (
SELECT extid, 'B' AS type, pqr_col AS col1, qrs_col AS col2 FROM b
UNION ALL
SELECT extid, 'C', abc_col, bcd_col FROM c
UNION ALL
SELECT extid, 'D', xyz_col, yza_col FROM d
) bcd
ON a.extid = bcd.extid
Union was my first thought when I read your question. Though, for simplicity, you could first create a view and then join it to other table(s):
create view v_ext as
select b.extid, b.pqr_col, b.qrs_col from b
union all
select c.extid, c.abc_col, c.bcd_col from c
union all
select d.extid, d.xyz_col, d.yza_col from d;
select *
from a join a1 on a.id = a1.aid
join v_ext v on v.extid = a.extid;
you can try the query with 'with' clause. Something like below, I havent tested it though
with union_output as
( select b.extid, b.pqr_col, b.qrs_col from b
union
select c.extid, c.abc_col, c.bcd_col from c
union
select d.extid, d.xyz_col, d.yza_col from d)
select *
from a join a1 on a.id = a1.aid
join union_output uo on uo.extid = a.extid;
Select *from tableA A
Inner join tableA1 A1 on A1.A1ID=A.AID
Inner join tableB b on b.ExtID=A.ExtID
Inner join tableC c on c.ExtID=A.ExtID
Inner join tableD d on d.ExtID=A.ExtID

is there a syntactic shortcut similar to coalesce for union?

What I'm trying to do is:
select
A.Fuzz
,A.Fizz
,B.Whiz
from A
left outer join B
on A.Fuzzy B=Wuzzy
To replace:
select
A.Fuzz
,A.Fizz
,B.Whiz
from A
left outer join B
on A.Fuzzy B=Wuzzy
UNION ALL
select
B.wuzz
,A.Fizz
,B.Whiz
from A
left outer join B
on A.Fuzzy B=Wuzzy
May be this is something "near" what you think (done on MSSQL)?. Pls in every question post sample data, expected result, etc.
CREATE TABLE A (ID INT, DESC_A VARCHAR(10));
INSERT INTO A VALUES (1,'A');
INSERT INTO A VALUES (2,'B');
CREATE TABLE B (ID INT, DESC_B VARCHAR(10));
INSERT INTO B VALUES (2,'Z');
INSERT INTO B VALUES (3,'Y');
SELECT COALESCE(A.ID, B.ID) AS ID
, A.DESC_A
, B.DESC_B
FROM A
FULL JOIN B ON A.ID = B.ID
Output:
ID DESC_A DESC_B
1 A NULL
2 B Z
3 NULL Y

SQL Server : joins new syntax(ANSI vs. non-ANSI SQL JOIN syntax)

I have tried to convert old MS sql join syntax to new join syntax but number of rows in the results not matching.
Original SQL:
select
b.Amount
from
TableA a, TableB b,TableC c, TableD d
where
a.inv_no *= b.inv_no and
a.inv_item *= b.inv_item and
c.currency *= b.cash_ccy and
d.tx_code *= b.cash_receipt
Converted SQL:
SELECT
b.AMOUNT
FROM
(TableA AS a
LEFT OUTER JOIN
TableB AS b ON a.INV_NO = b.INV_NO
AND a.inv_item = b.inv_item
LEFT OUTER JOIN
TableC AS c ON c.currency = b.cash_ccy)
LEFT OUTER JOIN
TableD as d ON d.tx_code = b.cash_receipt
Findings
Results are same on both original SQL and modified SQL upto joining of 3 tables but when joining the fourth table (TableD) to the modified SQL, the number of rows returned is different.
The order of fields within predicates is important when using SQL Server's (deprecated) proprietary ANSI 89 join syntax *= or =*
So while
SELECT *
FROM TableA AS A
LEFT JOIN TableB AS B
ON A.ColA = B.ColB;
Is exactly the same as
SELECT *
FROM TableA AS A
LEFT JOIN TableB AS B
ON B.ColB = A.ColA; -- NOTE ORDER HERE
The eqivalent
SELECT *
FROM TableA AS A, TableB AS b
WHERE A.ColA *= B.ColB;
Is not the same as
SELECT *
FROM TableA AS A, TableB AS b
WHERE B.ColA *= A.ColB;
This last query's ANSI 92 equivalent would be
SELECT *
FROM TableA AS A
RIGHT JOIN TableB AS B
ON A.ColA = B.ColB;
Or if you dislike RIGHT JOIN as much as I do you would probably write:
SELECT *
FROM TableB AS B
LEFT OUTER JOIN TableA AS A
ON B.ColB = A.ColA;
So actually the equivalent query in ANSI 92 join syntax would involve starting with TableA, TableC and TableD (since these are the leading fields in the original WHERE Clause). Then since there is no direct link between the three, you end up with a cross join
SELECT b.Amount
FROM TableA AS a
CROSS JOIN TableD AS d
CROSS JOIN TableC AS c
LEFT JOIN TableB AS B
ON c.currency = b.cash_ccy
AND d.tx_code = b.cash_receipt
AND a.INV_NO = b.INV_NO
AND a.inv_item = b.inv_item;
This is the equivalent rewrite, and explans the difference in the number of rows
WORKING EXAMPLE
Needs to be run on SQL Server 2008 or earlier with compatibility level 80 or less
-- SAMPLE DATA --
CREATE TABLE #TableA (Inv_No INT, Inv_item INT);
CREATE TABLE #TableB (Inv_No INT, Inv_item INT, cash_ccy INT, cash_receipt INT, Amount INT);
CREATE TABLE #TableC (currency INT);
CREATE TABLE #TableD (tx_code INT);
INSERT #TableA (inv_no, inv_item) VALUES (1, 1), (2, 2);
INSERT #TableB (inv_no, inv_item, cash_ccy, cash_receipt, Amount) VALUES (1, 1, 1, 1, 1), (2, 2, 2, 2, 2);
INSERT #TableC (currency) VALUES (1), (2), (3), (4);
INSERT #TableD (tx_code) VALUES (1), (2), (3), (4);
-- ORIGINAL QUERY(32 ROWS)
SELECT
b.Amount
FROM
#TableA a, #TableB b,#TableC c, #TableD d
WHERE
a.inv_no *= b.inv_no and
a.inv_item *= b.inv_item and
c.currency *= b.cash_ccy and
d.tx_code *= b.cash_receipt
-- INCORRECT ANSI 92 REWRITE (2 ROWS)
SELECT b.AMOUNT
FROM #TableA AS a
LEFT OUTER JOIN #TableB AS b
ON a.INV_NO = b.INV_NO
and a.inv_item = b.inv_item
LEFT OUTER JOIN #TableC AS c
ON c.currency = b.cash_ccy
LEFT OUTER JOIN #TableD as d
ON d.tx_code = b.cash_receipt;
-- CORRECT ANSI 92 REWRITE (32 ROWS)
SELECT b.Amount
FROM #TableA AS a
CROSS JOIN #TableD AS d
CROSS JOIN #TableC AS c
LEFT JOIN #TableB AS B
ON c.currency = b.cash_ccy
AND d.tx_code = b.cash_receipt
AND a.INV_NO = b.INV_NO
AND a.inv_item = b.inv_item;

Differences between forms of LEFT JOIN

What is the difference between these query
SELECT
A.AnyField
FROM
A
LEFT JOIN B ON B.AId = A.Id
LEFT JOIN C ON B.CId = C.Id
and this another query
SELECT
A.AnyField
FROM
A
LEFT JOIN
(
B
JOIN C ON B.CId = C.Id
) ON B.AId = A.Id
Original answer:
They are not the same.
For example a left join b left join c will return a rows, plus b rows even if there are no c rows.
a left join (b join c) will never return b rows if there are no c rows.
Added later:
SQL>create table a (id int);
SQL>create table b (id int);
SQL>create table c (id int);
SQL>insert into a values (1);
SQL>insert into a values (2);
SQL>insert into b values (1);
SQL>insert into b values (1);
SQL>insert into c values (2);
SQL>insert into c values (2);
SQL>insert into c values (2);
SQL>insert into c values (2);
SQL>select a.id from a left join b on a.id = b.id left join c on b.id = c.id;
id
===========
1
1
2
3 rows found
SQL>select a.id from a left join (b join c on b.id = c.id) on a.id = b.id;
id
===========
1
2
2 rows found
The first query is going to take ALL records from table a and then only records from table b where a.id is equal to b.id. Then it's going to take all records from table c where the resulting records in table b have a cid that matches c.id.
The second query is going to first JOIN b and c on the id. That is, records will only make it to the resultset from that join where the b.CId and the c.ID are the same, because it's an INNER JOIN.
Then the result of the b INNER JOIN c will be LEFT JOINed to table a. That is, the DB will take all records from a and only the records from the results of b INNER JOIN c where a.id is equal to b.id
The difference is that you may end up with more data from b in your first query since the DB isn't dropping records from your result set just because b.cid <> c.id.
For a visual, the following Venn diagram shows which records are available
SELECT
A.AnyField
FROM
A
LEFT JOIN B ON B.AId = A.Id
LEFT JOIN C ON B.CId = C.Id
In this query you are LEFT JOINing C with B which will give you all records possible with B whether or not there is a match to any records in C.
SELECT
A.AnyField
FROM
A
LEFT JOIN
(
B
JOIN C ON B.CId = C.Id
) ON B.AId = A.Id
In this query you are INNER JOINing C with B which will result in matching B and C records.
Both queries will give you the same result set as you are only pulling records from A so you will not see what records had matches and what did not in regards to B and C.

2 different condition on one SQL query

I have 2 tables. I need to pick some ids from table one and based one condition and insert into table 2. The second column again must come from tableA but based on a different condition
Table A
NC 1
NC 2
SC 3
SC 4
Table B
1 100
1 200
2 100
2 200
I want to insert rows to table B so it would look like this....
1 100
1 200
2 100
2 200
3 100
3 200
4 100
4 200
I am picking 3 and 4 from table A based on the state condtion = 'SC'and now I want to know how to pick the values of 100 and 200 which NC has...
Sorry if I havent worded it correctly
-- sample data
create table tbla (code char(2), id int);
insert into tbla values ('NC', 1);
insert into tbla values ('NC', 2);
insert into tbla values ('SC', 3);
insert into tbla values ('SC', 4);
create table tblb (id int, value int);
insert into tblb values (1, 100);
insert into tblb values (1, 200);
insert into tblb values (2, 100);
insert into tblb values (2, 200);
-- your query to INSERT the new rows into tblb
insert into tblb
select x.id, y.value
from
(
select distinct a.id
from tbla a
where a.code = 'SC'
) x
cross join
(
select distinct b.value
from tbla a
join tblb b on a.id = b.id
where a.code = 'NC'
) y
left join tblb b on b.id = x.id and b.value = y.value
where b.id is null;
You can do it by a query like :
Select a.id, b.value
from "Table A" a
join "Table B" b
on b.id=1 --This condition pick the two first rows on table B.
where a.condtion = 'SC'
This is not a elegant solution, but it work.