Left Outer Join using + sign in Oracle 11g
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.
Oracle using (+) for left outer join
Need to include the (+)
Operator on the Condition with the Literal
The older outer join approach requires that you include the (+)
operator for the condition on the literal.
Just put your "retro" mental cap on and return back to the 90's. :)
Here is how it should look with this approach:
SELECT
c
.DATE,
u.user_id
FROM
order_detail_trans c,
order_detail_trans u
WHERE
u.trans_id (+) = c.trans_id
AND u.trans_type (+) = 'UPDATE DETAIL'
Here is an example with the tables, emp
and dept
:
SCOTT@db>SELECT
2 d.deptno,
3 e.job
4 FROM
5 dept d
6 LEFT OUTER JOIN emp e ON d.deptno = e.deptno
7 and e.job = 'CLERK'
8 GROUP BY
9 d.deptno,
10 e.job
11 ORDER BY
12 1,
13 2;
DEPTNO JOB
10 CLERK
20 CLERK
30 CLERK
40
SCOTT@db>SELECT
2 d.deptno,
3 e.job
4 FROM
5 dept d,
6 emp e
7 WHERE
8 d.deptno = e.deptno (+)
9 AND e.job (+) = 'CLERK'
10 GROUP BY
11 d.deptno,
12 e.job;
DEPTNO JOB
20 CLERK
30 CLERK
10 CLERK
40
Believe it or not, I believe most Oracle Applications shops just use this older outer join approach.
How to do LEFT JOIN with double condition in Oracle syntax?
That would be
SQL> select owner.name, owner.value,
2 coalesce (insur.name, 'insurance') in_name,
3 insur.value, count(*)
4 from car, param owner, param insur
5 where car.id = owner.car_id
6 and car.id = insur.car_id (+)
7 and insur.name (+) = 'insur'
8 and owner.name = 'owner'
9 group by owner.name, owner.value, insur.name, insur.value
10 having count(*) > 1;
NAME VALUE IN_NAME VALUE COUNT(*)
-------- -------- -------------------- -------- ----------
owner John insurance 2
SQL>
Though, why would you want to use the old Oracle outer-join syntax? When compared to ANSI joins, it has only drawbacks and no advantages (at least, I can't think of any). Actually, I know one - if you use outer join in old Forms & Reports 6i (or even older? I don't think that anyone uses those versions nowadays), their built-in PL/SQL engine might not speak ANSI outer joins so you're doomed to use the old (+)
outer join operator. Other than that ... nope, no idea.
Oracle outer join(+) syntax
The columns that are joined on are just before the plus sign.
... FROM CustomerShip s LEFT OUTER JOIN (....) i ON s.ShipSeq = i.ShipSeq
left outer join in oracle issues
If I'm understanding your question correctly, you are negating your outer join
with the where
criteria. Move that criteria to the join
instead:
select b.sales_regn, b.sales_area, b.terr_num, a.terr_num, a.terr_name
from kp_terr_region b
left outer join kap_terr a on a.terr_num = b.terr_num
and a.valid_to > sysdate
and a.ptr_type = 'JPN'
and a.status != 1
and a.slr_num is null
where b.valid_to > sysdate
Left outer join and necessary subquery with date in Oracle
As MT0 suggested using partitioning and sorting by row number helped. Only had to include value_code in the partitioning for my purpose.
So this query finally did what I wanted:
SELECT *
FROM CONTRACTS C
LEFT JOIN
( SELECT *
FROM (
SELECT V.*,
ROW_NUMBER() OVER ( PARTITION BY CUSTOMER_ID, VALUE_CODE ORDER BY BEGIN_DATE DESC )
AS rn
FROM VALUES V
)
WHERE rn = 1
) V
ON ( C.CUSTOMER_ID = V.CUSTOMER_ID
AND VALUE_CODE = 'VOLUME' )
WHERE C.STATUS = 'ACTIVE'
left outer join on table with conditional select of record Oracle SQL/PL/SQL
This is a classical greatest-n-per-group problem, but on 300m records. The canonical question on this topic has a number of options, all of which should, logically, work. However, most require performing additional table- or index-scans or performing a lot of sorting. So, I'd highly recommend going with either the FIRST
or LAST
aggregate functions (aka the most difficult thing to search for in Oracle's documentation). You may know these as the KEEP
clause.
FIRST and LAST are Oracle-specific extensions to the SQL standard and their usefulness with large datasets comes from the fact that a SORT GROUP BY is immediately performed on the data-set, which can massively reduce the amount of records considered for the remainder of your query. I'd highly recommend reading Rob van Wijk's blog post for a fuller explanation.
You're correct that you need to provide a sort order, but you want to sort within the context of each group; to go through your logic:
SYS_CR_DT date closest to the ROLLUPDATE( a date that will be provided, without going over it). For the sake of the test case we can say '2012-12-12'
Assuming the maximum date is included in the ORDER BY clause you want to order by SYS_CR_DATE desc
.
If two or more SYS_CR_DT dates exist that are the same, choose the record with a CURR_SSN_IND = 'Y'.
Then, by a statement that priorities a specific value. I'd normally implement this with a CASE statement: case when CURR_SSN_IND = 'Y' then 0 else 1 end asc
(ascending is the default sort order but when doing complicated things it's clearer if you're explicit).
If a record cannot be selected based on CURR_SSN_IND = 'Y' ( in the case that they are both 'Y' or 'N') select the record where PSEUDO_SSN_IND = 'R'
This is the same logic as above, save with a different column and value; giving you: case when PSEUDO_SSN_IND = 'R' then 0 else 1 end asc
If a match still cannot be selected, choose record at random.
This is where you might run into problems. Oracle will pick a random record for you, there's no need to explicitly state randomness. However, because FIRST
and LAST
will sort randomly per column you need to explicitly provide an ORDER BY that removes randomness. I'd use the [ROWID
s]5 of the columns in the tables. This is not guaranteed to be consistent over time but will remain so for the duration of a transaction.
Looking at your last query I assume that this hasn't been thought through completely as you're not selecting from both your tables and I'm a little confused about your logic with the dates. I'm also a little unsure about the LEFT OUTER JOIN. I'm assuming here that you want a table unique on STU_NO
and STU_SEQ_NO
that contains all of those present in ACCOUNT_TBL
and it doesn't matter whether they're present in STU_SSN_TBL
.
So, putting the logic together this is what you'd end up with:
select a.stu_no
, a.stu_seq_no
, max(s.curr_ssn_id) keep (
dense_rank first order by sys_cr_date desc
, case when curr_ssn_ind = 'Y' then 0 else 1 end asc
, case when pseudo_ssn_ind = 'R' then 0 else 1 end asc
, s.rowid asc
, a.rowid asc
) as curr_ssn_id
, max(s.no) keep (
dense_rank first order by sys_cr_date desc
, case when curr_ssn_ind = 'Y' then 0 else 1 end asc
, case when pseudo_ssn_ind = 'R' then 0 else 1 end asc
, s.rowid asc
, a.rowid asc
) as no
, -- ...
, -- keep repeating for every column
, -- ...
, max(s.sys_cr_dt) keep (
dense_rank first order by sys_cr_date desc
, case when curr_ssn_ind = 'Y' then 0 else 1 end asc
, case when pseudo_ssn_ind = 'R' then 0 else 1 end asc
, s.rowid asc
, a.rowid asc
) as sys_cr_date
, -- keep repeating for every column
from account_tbl a
left outer join stu_ssn_tbl s
on a.stu_no = s.stu_no
and a.stu_seq_no = s.stu_seq_no
where s.sys_cr_date < date '2012-12-12'
group by a.stu_no
, a.stu_seq_no
As you can see this is pretty... ugly. But, it should be the most efficient way of performing it.
Your next problem is the 300m rows. On any semi-decent box/disks I wouldn't expect any problems with this, even single-threaded. But, if you have Enterprise edition it'd be worth using the parallel hint and if you don't DBMS_PARALLEL_EXECUTE
is more complicated but could be used to chunk ACCOUNT_TBL
so that you can parallelise. Ensure that you do this via SQL so that you don't have STU_NO
/STU_SEQ_NO
combinations in multiple chunks.
You mention that you have a lot of disk space, which is good but if you can avoid the disks you're in a better position. You'll have to do to table accesses here, and one hash join, Oracle will be writing all of this to your temporary tablespace, and will then perform some sort operations. You want to avoid performing these joins and sorts more times than you need to.
Your next concern should be whether you need to reproduce this. If you do you have a 2 options:
- If you need to keep all the data available then use your idea of a
DO_NOT_USE
flag. This is, I hope, a contradiction and you don't need to do this. - Create a new set of tables that has the information you need and archive off the rest - there's no point keeping huge amounts of completely unused data "hot" for use.
If you're going to be performing this sort of operation frequently look at sizing the [buffer cache][9] to be able to put the entire lot (and your normal operations) in RAM.
Oracle left-outer-join syntax shorthand notation (+) available in HQL?
Is there any way to specify a left outer join
in HQL?
Yes, HQL does support left outer joins via explicit join syntax, and the syntax is identical to SQL's (with the implied navigation property as the join key):
from Cat as cat
left join cat.kittens as kitten
outer
is optional, viz left [outer] join
Source: HQL Query Reference
Is there a shorthand abbreviation for left outer join
, like Oracle's (+)
No, AFAIK the HQL documentation makes no reference to a shorthand left outer join
notation, and if HQL adheres to the trend in RDBMS to remove proprietary join syntax, as per @a_horse_with_no_name's comment, Oracle recommends against using the proprietary (+) syntax, and similarly *= in MSSql Server has been deprecated.
Related Topics
Sql: Between VS ≪= and ≫=
Is There Something Like a Zip() Function in Postgresql That Combines Two Arrays
SQL Select Speed Int VS Varchar
MySQL Not Using Indexes With Where in Clause
How to Use Join For Two Different Database Tables
What Is Sysname Data Type in SQL Server
SQL Server:Dynamic Pivot Over 5 Columns
Top N Records Per Group SQL in Access
How to Find the Employee with the Second Highest Salary
Option (Recompile) Is Always Faster; Why
How to Do Pagination in SQL Server 2008
Concatenate and Group Multiple Rows in Oracle
Why Is There No Product Aggregate Function in SQL
Query Error with Ambiguous Column Name in SQL