Oracle SQL Ansi vs Non Ansi joins [duplicate] - oracle

Can any one tell me whether below 2 queries are an example of Left Outer Join or Right Outer Join??
Table Part:
Name Null? Type
PART_ID NOT NULL VARCHAR2(4)
SUPPLIER_ID VARCHAR2(4)
PART_ID SUPPLIER_ID
P1 S1
P2 S2
P3
P4
Table Supplier:
Name Null? Type
SUPPLIER_ID NOT NULL VARCHAR2(4)
SUPPLIER_NAME NOT NULL VARCHAR2(20)
SUPPLIER_ID SUPPLIER_NAME
S1 Supplier#1
S2 Supplier#2
S3 Supplier#3
Display all the parts irrespective of whether any supplier supplies them or not:
SELECT P.Part_Id, S.Supplier_Name
FROM Part P, Supplier S
WHERE P.Supplier_Id = S.Supplier_Id (+)
SELECT P.Part_Id, S.Supplier_Name
FROM Part P, Supplier S
WHERE S.Supplier_Id (+) = P.Supplier_Id

TableA LEFT OUTER JOIN TableB is equivalent to TableB RIGHT OUTER JOIN Table A.
In Oracle, (+) denotes the "optional" table in the JOIN. So in your first query, it's a P LEFT OUTER JOIN S. In your second query, it's S RIGHT OUTER JOIN P. They're functionally equivalent.
In the terminology, RIGHT or LEFT specify which side of the join always has a record, and the other side might be null. So in a P LEFT OUTER JOIN S, P will always have a record because it's on the LEFT, but S could be null.
See this example from java2s.com for additional explanation.
To clarify, I guess I'm saying that terminology doesn't matter, as it's only there to help visualize. What matters is that you understand the concept of how it works.
RIGHT vs LEFT
I've seen some confusion about what matters in determining RIGHT vs LEFT in implicit join syntax.
LEFT OUTER JOIN
SELECT *
FROM A, B
WHERE A.column = B.column(+)
RIGHT OUTER JOIN
SELECT *
FROM A, B
WHERE B.column(+) = A.column
All I did is swap sides of the terms in the WHERE clause, but they're still functionally equivalent. (See higher up in my answer for more info about that.) The placement of the (+) determines RIGHT or LEFT. (Specifically, if the (+) is on the right, it's a LEFT JOIN. If (+) is on the left, it's a RIGHT JOIN.)
Types of JOIN
The two styles of JOIN are implicit JOINs and explicit JOINs. They are different styles of writing JOINs, but they are functionally equivalent.
See this SO question.
Implicit JOINs simply list all tables together. The join conditions are specified in a WHERE clause.
Implicit JOIN
SELECT *
FROM A, B
WHERE A.column = B.column(+)
Explicit JOINs associate join conditions with a specific table's inclusion instead of in a WHERE clause.
Explicit JOIN
SELECT *
FROM A
LEFT OUTER JOIN B ON A.column = B.column
These
Implicit JOINs can be more difficult to read and comprehend, and they also have a few limitations since the join conditions are mixed in other WHERE conditions. As such, implicit JOINs are generally recommended against in favor of explicit syntax.

You can see answers from previous posts
However I added little more information
create table r2020 (id int, name varchar2(50),rank number);
insert into r2020 values (101,'Rob Rama',1);
insert into r2020 values (102,'Ken Krishna',3);
insert into r2020 values (108,'Ray Rama',2);
insert into r2020 values (109,'Kat Krishna',4);
create table r2021 (id int, name varchar2(50),rank number);
insert into r2021 values (102,'Ken Krishna',1);
insert into r2021 values (103,'Tom Talla',2);
insert into r2021 values (108,'Ray Rama',2);
--LEFT OUTER JOIN
--oracle notation
select * from r2020 r1, r2021 r2
where r1.id = r2.id (+)
order by r1.id;
--ANSI notation
select * from r2020 r1
left outer join r2021 r2 on r1.id = r2.id
order by r1.id;
--OUT PUT
NAME ID RANK NAME_1 ID_1 RANK_1
---- -- ---- ---- ---- ------
Rob Rama 101 1 (null) (null) (null)
Ken Krishna 102 3 Ken Krishna 102 1
Ray Rama 108 2 Ray Rama 108 2
Kat Krishna 109 4 (null) (null) (null)
--RIGHT OUTER JOIN
--oracle notation
select * from r2020 r1, r2021 r2
where r1.id (+) = r2.id
order by r1.id;
--ANSI notation
select * from r2020 r1
right outer join r2021 r2 on r1.id = r2.id
order by r1.id;
--OUT PUT
NAME ID RANK NAME_1 ID_1 RANK_1
---- -- ---- ---- ---- ------
Ken Krishna 102 3 Ken Krishna 102 1
Ray Rama 108 2 Ray Rama 108 2
(null) (null) (null) Tom Talla 103 2
--<b>MULTIPLE COLUMNS IN JOIN CONDITION</b>
--LEFT OUTER JOIN
--oracle notation
select * from r2020 r1, r2021 r2
where r1.id = r2.id (+) and
r1.rank = r2.rank (+)
order by r1.id;
--ANSI notation
select * from r2020 r1
left outer join r2021 r2 on r1.id = r2.id and
r1.rank = r2.rank
order by r1.id;
--OUT PUT
NAME ID RANK NAME_1 ID_1 RANK_1
---- -- ---- ---- ---- ------
Rob Rama 101 1 (null) (null) (null)
Ken Krishna 102 3 (null) (null) (null)
Ray Rama 108 2 Ray Rama 108 2
Kat Krishna 109 4 (null) (null) (null)
--RIGHT OUTER JOIN
--oracle notation
select * from r2020 r1, r2021 r2
where r1.id (+) = r2.id and
r1.rank(+) = r2.rank
order by r1.id;
--ANSI notation
select * from r2020 r1
right outer join r2021 r2 on r1.id = r2.id and
r1.rank = r2.rank
order by r1.id;
--OUT PUT
NAME ID RANK NAME_1 ID_1 RANK_1
---- -- ---- ---- ---- ------
(null) (null) (null) Ken Krishna 102 1
Ray Rama 108 2 Ray Rama 108 2
(null) (null) (null) Tom Talla 103 2

There is some incorrect information in this thread. I copied and pasted the incorrect information:
LEFT OUTER JOIN
SELECT *
FROM A, B
WHERE A.column = B.column(+)
RIGHT OUTER JOIN
SELECT *
FROM A, B
WHERE B.column(+) = A.column
The above is WRONG!!!!! It's reversed. How I determined it's incorrect is from the following book:
Oracle OCP Introduction to Oracle 9i: SQL Exam Guide. Page 115 Table 3-1 has a good summary on this. I could not figure why my converted SQL was not working properly until I went old school and looked in a printed book!
Here is the summary from this book, copied line by line:
Oracle outer Join Syntax:
from tab_a a, tab_b b,
where a.col_1 + = b.col_1
ANSI/ISO Equivalent:
from tab_a a left outer join
tab_b b on a.col_1 = b.col_1
Notice here that it's the reverse of what is posted above. I suppose it's possible for this book to have errata, however I trust this book more so than what is in this thread. It's an exam guide for crying out loud...

Related

How to join hive tables based on condition of the joining column

We have a hive table like below:
num value
123 A
456 B
789 C
101 D
The joining table is:
num Symbols
123 ASC
456001 JEN
456002 JEN
456003 JEN
789001 CON
101 URB
Our expected result:
num value symbols
123 A ASC
456 B JEN
789 C CON
101 D URB
Currently we are joining the tables twice in order to get the results.
Like first time insert into some tmp table using the below query:
select
a.num,
a.value,
b.symbols
from mytable a
join mytable b on a.num = b.num;
This query is producing the results for keys 123,101.
Next, we are running another query like below:
select
a.num,
a.value,
b.symbols
from mytable a
join mytable b on CONCAT(a.num,'001') = b.num;
This query is producing the results for keys 456, 789.
These two queries results are inserted into some tmp hive table and we select the final results from the tmp table.
This looks a bad design overall. but I would like to know if there is a better way to achieve this. Thanks.
Query Result
for
Select
a.num
,a.value
,b.symbols
from
(select substr(num,3) as num, value from table)a
join
(select substr(num,3) as num, symbols from table) b
on a.num = b.num
a.num a.value b.symbols
3 A ASC
1 D URB
OK, just one sql can implement your requirement.see below, table a is the table with value column and table b is the table with the symbols column, the SQL:
select
distinct a.num,
a.value,
b.symbols
from
mytable1 a
join
mytable2 b on substr(cast(b.num as string),0,3) = cast(a.num as string)
If datatype of num is String then you can try with Substr
Select
a.num
,a.value
,b.symbols
from a join b on
substr(a.num,3) = substr(b.num,3)
Can you pls try this
Select
a.num
,a.value
,b.symbols
from
(select substr(num,3) as num, value from table)a
join
(select substr(num,3) as num, symbols from table) b
on a.num = b.num
Can you try with left semi join with above query as shown below.
Select
a.num,
a.value,
b.symbols
from
mytable1 a
Left semi join
mytable2 b on substr(cast(b.num as string),0,3) = cast(a.num as string)

Oracle SQL Full Outer Joins

My tables are as follows:
SELECT * FROM r_a;
CLASS SECTION
----- -------
1 A
1 B
2 A
2 B
SELECT * FROM r_b;
CLASS SECTION
----- -------
1 B
1 C
2 B
2 C
I want to perform a full outer join on these tables over the column SECTION where CLASS=1 in both tables. My desired output is :
SECTION SECTION_1
------- ---------
B B
A (Null)
(Null) C
However, the following query yields only the matching row, similar to result of an inner join.
**QUERY 1**
SELECT a.section, b.section
FROM
r_a a
FULL OUTER JOIN
r_b b
ON a.section=b.section
WHERE A.class=1 AND B.class=1
SECTION SECTION_1
------- ---------
B B
I am able to achieve desired result by taking the where conditions inside a nested query:
**QUERY 2**
SELECT a.section, b.section
FROM
(SELECT SECTION FROM r_a WHERE class=1) a
FULL OUTER JOIN
(SELECT SECTION FROM r_b WHERE class=1) b
ON a.section=b.section
SECTION SECTION_1
------- ---------
B B
(Null) C
A (Null)
The result is more surprising when the where conditions in **Query 1** is moved to the on clause:
** Query 3**
SELECT a.section, b.section
FROM
r_a a
FULL OUTER JOIN
r_b b
ON a.section=b.section AND A.class=1 AND B.class=1
SECTION SECTION_1
------- ---------
B B
(Null) C
(Null) B
(Null) C
B (Null)
A (Null)
A (Null)
MORE CLARITY:
*************
SELECT *
FROM
r_a a
FULL OUTER JOIN
r_b b
ON a.section=b.section AND A.class=1 AND B.class=1
CLASS SECTION CLASS_1 SECTION_1
------ ------- ------- ---------
1 B 1 B
(Null) (Null) 1 C
(Null) (Null) 2 B
(Null) (Null) 2 C
2 B (Null) (Null)
2 A (Null) (Null)
1 A (Null) (Null)
Explanation on why Query 1 doesn't produce the desired result and why Query 3 joins on class=2 even when the on clause says AND A.class=1 AND B.class=1 is greatly appreciated.
You first query is 100% perfect, just change your WHERE statement to OR instead of AND.
SELECT a.section, b.section
FROM
r_a a
FULL OUTER JOIN
r_b b
ON a.section=b.section
WHERE A.class=1 OR B.class=1
That is essentially what you are doing when you do your second attempt with the subqueries.
Keeping in mind that your WHERE clause is executed AFTER the FROM clause. When you use AND here you are saying "Only take results AFTER the join where both conditions are true." Obviously this dumps results when one table has a condition = 1 and the other does not. With the "OR" you are saying "Take results AFTER the join where either condition is true".
Be careful, condition where vs condition on is difference:
where is a filter which is applied after rows are selected using the join. It is not always the case that a join ... on condition is sematically equivalent to a where condition. You can see more detail here.
Your Query 1 <=>
select asection, bsection from(
SELECT a.section asection, b.section bsection, a.CLASS a, b.CLASS b
FROM r_a a
FULL OUTER JOIN r_b b
ON a.section=b.section
)
WHERE a=1 AND b=1

Hive - OR condition with left outer join

I have referred all the queries on SO for a similar case. Although the error may be common, I am looking for solution for the specific case. Please refrain from marking the question duplicate, unless you get exactly the same scenario with an accepted solution.
I have two tables
Main table:
c1 c2 c3 c4 c5
1 2 3 4 A
Other table
c1 c2 c3 c4 c5
1 8 5 6 B
8 2 8 9 C
8 7 3 9 C
8 7 9 4 C
5 6 7 8 D
Now, from the other table, I should only be able to pick only unique record across all the column. e.g. the last row (5,6,7,8, D) only.
Row 1 from other table rejected, because c1 value (1) is same as c1 value (1) in main table, Row 2 rejected because c2 value of other and main table matches and likewise...
In a nutshell, none of the columns from other table should have the same value (in corresponding column) in main table in the output of the query.
I tried creating below query
select t1.* from otherTable t1
LEFT OUTER JOIN mainTable t2
ON ( t1.c1 = t2.c1 OR t1.c2 = t2.c2 OR t1.c3 = t2.c3 OR t1.c4 = t2.c4 )
Where t2.c5 is null;
However, hive throws below exception
OR not supported in JOIN currently
I understand the hive limitation and many time I have used UNION (ALL | DISTINCT) with inner join to overcome this limitation; but not able to use the same strategy with this.
Please help.
EDIT 1 : I have hive version restriction - Can only use version 1.2.0
You can do cartesian product join (an inner join with no conditions):
select t1.* from otherTable t1
,mainTable t2
WHERE t1.c1 != t2.c1 AND t1.c2 != t2.c2
AND t1.c3 != t2.c3 AND t1.c4 != t2.c4 AND t1.c5 != t2.c5;
Assuming you have one row in the mainTable this query should be as efficient as one that uses OUTER JOIN
Another option is to break your proposed query into 5 different LEFT OUTER JOIN sub-queries:
select t1.* from (
select t1.* from (
select t1.* from (
select t1.* from (
select t1.* from otherTable t1
LEFT OUTER JOIN (select distinct c1 from mainTable) t2
ON ( t1.c1 = t2.c1) Where t2.c1 is null ) t1
LEFT OUTER JOIN (select distinct c2 from mainTable) t2
ON ( t1.c2 = t2.c2) Where t2.c2 is null ) t1
LEFT OUTER JOIN (select distinct c3 from mainTable) t2
ON ( t1.c3 = t2.c3) Where t2.c3 is null ) t1
LEFT OUTER JOIN (select distinct c4 from mainTable) t2
ON ( t1.c4 = t2.c4) Where t2.c4 is null ) t1
LEFT OUTER JOIN (select distinct c5 from mainTable) t2
ON ( t1.c5 = t2.c5) Where t2.c5 is null
;
Here, for each column, I first get the distinct columns from the mainTable and join it with what's left of otherTable. The downside is that I pass 5 times over mainTable - once for each column. If the values in main table are unique, you can remove the distinct from the subqueries.

Need help creating a view on top of a JOIN query that needs to return only the latest value

I need help with my SQL Query I have Two tables that i need to join using a LEFT OUTER JOIN, then i need to create a database view over that particular view. If i run a query on the join to look for name A i need to get that A's latest brand "AP".
Table 1
ID name address
-----------------------
1 A ATL
2 B ATL
TABLE 2
ID PER_ID brand DATEE
--------------------------------------------
1 1 MS 5/19/17:1:00pm
2 1 XB 5/19/17:1:05pm
3 1 AP 5/19/17:2:00pm
4 2 RO 5/19/17:3:00pm
5 2 WE 5/19/17:4:00pm
I tried query a which returns correct result but i get problem 1 when i try to build the database view on top of the join. I tried query b but when i query my view in oracle sql developer i still get all the results but not the latest.
query a:
SELECT * from table_1
left outer join table_2 on table_1.ID = Table_2.PER_ID
AND table_2.DATE = (SELECT MAX(DATE) from table_2 z where z.PER_ID = table_2.PER_ID)
Problem 1
Error report -
ORA-01799: a column may not be outer-joined to a subquery
01799. 00000 - "a column may not be outer-joined to a subquery"
*Cause: <expression>(+) <relop> (<subquery>) is not allowed.
*Action: Either remove the (+) or make a view out of the subquery.
In V6 and before, the (+) was just ignored in this case.
Query 2:
SELECT * from table_1
left outer join(SELECT PER_ID,brand, max(DATEE) from table_2 group by brand,PER_ID) t2 on table_1.ID = t2.PER_ID
Use row_number():
select t1.id, t1.name, t1.address, t2.id as t2_id, t2.brand, t2.datee
from table_1 t1 left outer join
(select t2.*,
row_number() over (partition by per_id order by date desc) as seqnum
from table_2 t2
) t2
on t1.ID = t2.PER_ID and t2.seqnum = 1;
When defining a view, you should be in the habit of listing the columns explicitly.

Oracle join tables and always include all possible results

I am trying to join two tables that look like the following:
Table 1
Letter | Value
A 2
B 5
Table 2
Letter | Number
A 1
C 7
I am trying to join these tables so that, regardless of what is in the tables at the time, there will always be an A,B,C record in the result. In other words, this would be displayed:
Letter | Value | Number
A 2 1
B 5 null
C null 7
The three letter records should always be displayed regardless of if they are in the tables. So assume table2 looks like this:
Letter | Number
A 1
Then I want the following results even though there is now no 'C' record:
Letter | Value | Number
A 2 1
B 5 null
C null null
Could anyone show how to do this?
You can use a SELECT ... FROM DUAL with a CONNECT BY clause to generate a stable set of letters in case some letters don't appear in either table.
with base_set_of_letters as (
select chr(rownum + 64) as Letter -- ascii 65=A, 66=B, 67=C, ...
from dual
connect by rownum <= 3) -- increase this number if you want more letters
select l.letter, t1.value, t2.xNumber
from base_set_of_letters l
left join Table1 t1 on t1.letter = l.letter
left join Table2 t2 on t2.letter = l.letter
order by l.letter;
SQL Fiddle Demo
If you were just looking to get all rows that existed joined, irregardless of whether data existed on both sides of the relationship, you'd be looking for a FULL OUTER JOIN:
SELECT COALESCE(t1.LETTER, t2.LETTER) AS LETTER,
t1.VALUE,
t2.NUM
FROM TABLE_1 t1
FULL OUTER JOIN TABLE_2 t2
ON t2.LETTER = t1.LETTER
ORDER BY COALESCE(t1.LETTER, t2.LETTER);
which produces
LETTER VALUE NUM
A 2 1
B 5 (null)
C (null) 7
But this would only give you rows where you've got a key value ('A', 'B', or 'C') - however, if one is missing you'd get nothing. IMO you need a third table, perhaps called ALPHABET, containing all possible letters:
CREATE TABLE ALPHABET (LETTER CHAR(1));
and populated with 'A', 'B', 'C', etc. In this case your join would become:
SELECT a.LETTER,
t1.VALUE,
t2.NUM
FROM ALPHABET a
FULL OUTER JOIN TABLE_1 t1
ON t1.LETTER = a.LETTER
FULL OUTER JOIN TABLE_2 t2
ON t2.LETTER = a.LETTER
which, if ALPHABET is only populated with ('A, 'B', 'C', and 'D'), produces
LETTER VALUE NUM
A 2 1
B 5 (null)
C (null) 7
D (null) (null)
SQLFiddle here (and note that in the fiddle I changed the variable name NUMBER to NUM to eliminate the need to double-quote it everywhere it's used).

Resources