Let's say we have these (simplified from a more complex one) example tables:
== st ==    == pr ===   == rn ===  <– tables
sta   pg    pg    rou   sta   rou  <– fields
========    =========   =========
H1    aa    aa    aaA   H1    aaA
H2    aa    aa    aaB   H2    aaB
H3    aa                H3    aaB
H4    aa    aa    aaC   H4    aaC
H5    aa                H5    aaC
H6    aa                H6    aaC
H7    aa
H8    bb    bb    NULL
I wanted to execute this (also simplified) query with an inner left join:
SELECT st.*, pr.*, rn.*
FROM         st
INNER JOIN ( pr
LEFT  JOIN   rn  ON pr.rou = rn.rou
                AND          rn.sta = st.sta -- ERROR here
           )     ON pr.pg =           st.pg
-- filter out bad rows
WHERE ( rn.id )                          -- a: not null
   OR ( pr.rou ='aaC' AND rn.id IS NULL) -- b: no joinable rn found: choose
                                         --    by a predefined pr.rou value
   OR ( pr.rou IS NULL )                 -- c: no need to join
to have this result:
== st ==    == pr ===   == rn ===
sta   pg    pg    rou   sta   rou
========    =========   =========
H1    aa    aa    aaA   H1    aaA
H2    aa    aa    aaB   H2    aaB
H3    aa    aa    aaB   H3    aaB
H4    aa    aa    aaC   H4    aaC
H5    aa    aa    aaC   H5    aaC
H6    aa    aa    aaC   H6    aaC
H7    aa    aa    aaA   NULL  NULL \  H7 has no rn, so choose
H7    aa    aa    aaB   NULL  NULL  } 1 row of these at the
H7    aa    aa    aaC   NULL  NULL /  WHERE / b condition
H8    bb    bb    NULL  NULL  NULL
but MySQL throws up this error: #1054 - Unknown column 'st.sta' in 'on clause'.
I tried to solve this with no success, until somebody posted (and deleted) a nice idea of joining the rn column twice. Thanks to him/her, I was able to create a working solution:
SELECT st.*, pr.*, rn.*, rn2.*
FROM         st
INNER JOIN   pr         ON st.pg = pr.pg
LEFT  JOIN   rn         ON st.sta = rn.sta
LEFT  JOIN   rn AS rn2  ON pr.rou = rn2.rou -- the two rn's join order is important
                       AND rn.id  = rn2.id  -- if first exists, second must match or null, first null => second null
WHERE ( rn.id = rn2.id )                    -- a: both not null
   OR ( pr.rou ='aaC' AND rn.id IS NULL)    -- b: no joinable rn found: choose by predefined pr.rou value
   OR ( pr.rou IS NULL )                    -- c: no need to join
While this query works, it requires the duplicate joins in the right order, very fragile, and ugly.
Can you give a cleaner solution, which joins the rn table only once?
Here is the sample database in sql for copypasting, if you want to play with it:
DROP    TABLE IF     EXISTS st;
CREATE  TABLE IF NOT EXISTS st (
  id  int  AUTO_INCREMENT,
  sta varchar(9),
  pg  varchar(9),
  PRIMARY KEY ( id )
)  AUTO_INCREMENT=1;
DROP    TABLE IF     EXISTS pr;
CREATE  TABLE IF NOT EXISTS pr (
  id  int  AUTO_INCREMENT,
  pg  varchar(9),
  rou varchar(9),
  PRIMARY KEY ( id )
)  AUTO_INCREMENT=1;
DROP    TABLE IF     EXISTS rn;
CREATE  TABLE IF NOT EXISTS rn (
  id  int  AUTO_INCREMENT,
  sta varchar(9),
  rou varchar(9),
  PRIMARY KEY ( id )
)  AUTO_INCREMENT=1;
INSERT INTO st 
(sta , pg ) VALUES
('H1','aa'),
('H2','aa'),
('H3','aa'),
('H4','aa'),
('H5','aa'),
('H6','aa'),
('H7','aa'),
('H8','bb');
INSERT INTO pr
( pg , rou ) VALUES
('aa','aaA'),
('aa','aaB'),
('aa','aaC'),
('bb', NULL);
INSERT INTO rn
(sta , rou ) VALUES
('H1','aaA'),
('H2','aaB'),
('H3','aaB'),
('H4','aaC'),
('H5','aaC'),
('H6','aaC');
                The plus sign is Oracle syntax for an outer join. There isn't a minus operator for joins. An outer join means return all rows from one table. Also return the rows from the outer joined where there's a match on the join key.
Yes, it is possible. We would use a query with two LEFT OUTER JOINs to retrieve the hierarchy. The relationships were "zero or more" and it's the zero that tips us off to the need for an OUTER join.
The Left Outer Join in MySQL is used to retrieve all the matching records from both the tables as well as non-matching records from the left side table. In that case, the non-matching data will take a null value. The LEFT OUTER JOIN or LEFT JOIN keyword is used to perform the left join in MySQL.
There really is no difference between a LEFT JOIN and a LEFT OUTER JOIN. Both versions of the syntax will produce the exact same result in PL/SQL. Some people do recommend including outer in a LEFT JOIN clause so it's clear that you're creating an outer join, but that's entirely optional.
I think I've finally got it:
SELECT st.*, pr.*, rn.*
FROM st
  LEFT JOIN rn ON st.sta = rn.sta
  LEFT JOIN pr ON st.pg  = pr.pg
              AND (rn.rou = pr.rou OR rn.rou IS NULL)
                        select st.*, pr.*, rn.*  
from st
left join  pr on pr.pg = st.pg
left  join   rn  on rn.sta = st.sta
                        If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With