You Asked
Hi Tom,
I am currently rewriting sql server views into our oracle for an application.
One of the views that I have rewritten contains outer joins in the where clause along with the "or" clause.This will not compile in the database it gives me ORA-01719: outer join operator (+) not allowed in operand of OR or IN.I rewrote the where portion that failed into a select statement in the from clause in order to have this compile.Could you explain to me why it will not let me have outer join and or in where portion of statement.
It also appears that there
are left outer joins on primary key columns.Shouldn't these be equi join or have the (+) on the column other then pkey.Would there be an instance where I would want (+) join on a primary key column.
Could you please clarify this area for me so I can better understand
under what conditions (+) join is appropriate.
Below is the original sql server view followed by it being rewritten with the "or" clause failing and then the compiled copy with the "or" portion written in the from clause.
As usual any advice is greatly appreciated and thanks for your help.
(sql server)
CREATE VIEW imrpt_work_load_schd_by_craft
AS
SELECT
wor.pkey,
wor.id ,
wor.wo_num,
wor.date_scheduled,
wor.date_completed,
wos.id as status_id,
wor.description as wo_description,
CASE WHEN (wor.id IS NULL OR wor.id = ' ')
THEN CONVERT(NVARCHAR(25), wor.wo_num) ELSE
RTRIM(wor.id) + '-' + CONVERT(NVARCHAR(25), wor.wo_num) END AS WO_ID,
c.id as resource_id,
cnt.id as 'craft_id',
st.id AS site_id,
st.description AS site_description
FROM imtbl_woap_est_res war LEFT OUTER JOIN
imtbl_craft cnt ON cnt.pkey = war.craft LEFT OUTER JOIN
imtbl_contact c ON war.resources = c.pkey LEFT OUTER JOIN
imtbl_woap woa ON woa.pkey = war.work_order_asset_procedure LEFT OUTER JOIN imtbl_work_order wor ON wor.pkey = woa.work_order LEFT OUTER JOIN imtbl_work_order_status wos ON wor.status = wos.pkey,
imtbl_site st
WHERE wos.category = 1 OR wos.category = 2 OR wos.category = 3 AND war.Remaining_Hours > 0
(oracle failed view)
CREATE OR REPLACE VIEW imrpt_work_load_schd_by_craft (
pkey,
id,
wo_num,
date_scheduled,
date_completed,
status_id,
wo_description,
wo_id,
resource_id,
craft_id,
site_id,
site_description )
AS
SELECT
wor.pkey,
wor.id ,
wor.wo_num,
wor.date_scheduled,
wor.date_completed,
wos.id as status_id,
wor.description as wo_description,
CASE WHEN (wor.id IS NULL OR wor.id = ' ')
THEN to_char(wor.wo_num) ELSE
RTRIM(wor.id)||'-'||to_char( wor.wo_num) END AS WO_ID,
c.id as resource_id,
cnt.id as craft_id,
st.id AS site_id,
st.description AS site_description
FROM imtbl_woap_est_res war,
imtbl_craft cnt,
imtbl_contact c,
imtbl_woap woa,
imtbl_work_order wor,
imtbl_work_order_status wos,
imtbl_site st
WHERE
WOS.CATEGORY = 1 OR WOS.category = 2 OR WOS. category = 3 AND
war.Remaining_Hours > 0 AND
cnt.pkey(+) = war.craft AND
war.resources = c.pkey(+) AND
woa.pkey(+) = war.work_order_asset_procedure AND
wor.pkey(+) = woa.work_order AND
wor.status = wos.pkey
(or claues moved into from clause)
CREATE OR REPLACE VIEW imrpt_work_load_schd_by_craft (
pkey,
id,
wo_num,
date_scheduled,
date_completed,
status_id,
wo_description,
wo_id,
resource_id,
craft_id,
site_id,
site_description )
AS
SELECT
wor.pkey,
wor.id ,
wor.wo_num,
wor.date_scheduled,
wor.date_completed,
wos.id as status_id,
wor.description as wo_description,
CASE WHEN (wor.id IS NULL OR wor.id = ' ')
THEN to_char(wor.wo_num) ELSE
RTRIM(wor.id)||'-'||to_char( wor.wo_num) END AS WO_ID,
c.id as resource_id,
cnt.id as craft_id,
st.id AS site_id,
st.description AS site_description
FROM imtbl_woap_est_res war,
imtbl_craft cnt,
imtbl_contact c,
imtbl_woap woa,
imtbl_work_order wor,
(select id,pkey from imtbl_work_order_status where CATEGORY 1 OR category = 2 OR category = 3) wos,
imtbl_site st
WHERE
war.Remaining_Hours > 0 AND
cnt.pkey(+) = war.craft AND
war.resources = c.pkey(+) AND
woa.pkey(+) = war.work_order_asset_procedure AND
wor.pkey(+) = woa.work_order AND
wor.status = wos.pkey
and Tom said...
No chance of using a database written this century?
but anyway, there is NO outer join using an OR in the original query???
That, and the use of the OUTER JOIN is in VAIN in this case (eg: the original author did not understand what they were doing! hope it wasn't you ;)
using OR's and AND's without paren's is a recipe for disaster as well! Your where clause should use parens so it is obvious that:
WHERE wos.category = 1 OR wos.category = 2 OR wos.category = 3 AND
war.Remaining_Hours > 0
is being intrepreted as:
WHERE (wos.category = 1 OR wos.category = 2 OR wos.category = 3) AND
war.Remaining_Hours > 0)
(hope that's what you meant to have it mean!!!)
FROM
imtbl_woap_est_res war LEFT OUTER JOIN
imtbl_craft cnt ON cnt.pkey = war.craft LEFT OUTER JOIN
imtbl_contact c ON war.resources = c.pkey LEFT OUTER JOIN
imtbl_woap woa ON woa.pkey = war.work_order_asset_procedure LEFT OUTER JOIN
imtbl_work_order wor ON wor.pkey = woa.work_order LEFT OUTER JOIN
imtbl_work_order_status wos ON wor.status = wos.pkey,
imtbl_site st
WHERE wos.category = 1
OR wos.category = 2
OR wos.category = 3
AND war.Remaining_Hours > 0
Looking at that, you must understand that the WHERE clause is done AFTER the joining. So, just using THREE tables (to make it readable):
ops$tkyte@ORA920.US.ORACLE.COM> create table t1 ( x int );
Table created.
ops$tkyte@ORA920.US.ORACLE.COM> create table t2 ( x int );
Table created.
ops$tkyte@ORA920.US.ORACLE.COM> create table t3 ( x int );
Table created.
ops$tkyte@ORA920.US.ORACLE.COM> insert into t1 values ( 1 );
1 row created.
ops$tkyte@ORA920.US.ORACLE.COM> insert into t3 values ( 2 );
1 row created.
ops$tkyte@ORA920.US.ORACLE.COM> insert into t3 values ( 3 );
1 row created.
ops$tkyte@ORA920.US.ORACLE.COM> select *
2 from t1 LEFT OUTER JOIN t2 wos on (t1.x = wos.x), t3
3 /
X X X
---------- ---------- ----------
1 2
1 3
So, this is similar to your query. We have a bunch of tables "left outer joined" to each other and the CARTESIAN producted with a last table (imtbl_site st in your case).
Without a where clause, we get values back (t2 wos -- has NO rows). Now, we add the where clause -- which HAPPENS AFTER THE OUTER JOIN REMEMBER!!!
ops$tkyte@ORA920.US.ORACLE.COM> select *
2 from t1 LEFT OUTER JOIN t2 wos on (t1.x = wos.x), t3
3 where wos.x = 1 or wos.x = 2
4 /
no rows selected
and since we have a predicate on a column that would be NULL if we actually outer join to it -- it immediate drops out of the result set. The outer join to WOS in your case is *totally and 100% a waste of time and resources*. The only thing it is doing is removing other possibly better access paths from consideration by the optimzer!!!!!
If we EVER outer join to WOS, it can *never ever* satisfy the where clause (cause the columns are NULL).
So -- three things here:
o you are not having an outer join in the JOINS at all
o your outer join to WOS is totally useless here
o if you used the current production release of Oracle -- the outer join syntax would just "go", no rewrite.
This is the 8i rewrite:
FROM imtbl_woap_est_res war,
imtbl_craft cnt ,
imtbl_contact c ,
imtbl_woap woa ,
imtbl_work_order wor ,
imtbl_work_order_status wos ,
imtbl_site st
WHERE cnt.pkey(+) = war.craft
and war.resources = c.pkey (+)
and woa.pkey(+) = war.work_order_asset_procedure
and wor.pkey(+) = woa.work_order
and wor.status = wos.pkey(+)
and wos.category in (1,2,3)
and war.Remaining_Hours > 0
Although, as I mentioned -- the outer join to WOS is toally not needed -- you can drop the (+) on the wos.pkey join condition without changing the meaning at all!!! In fact, that'll make the optimizer have a much easier go at it.
Rating
(7 ratings)
Is this answer out of date? If it is, please let us know via a Comment