问题
I have 2 tables j and c.
Both tables have columns ports and sec, and JOIN ON j.ports = c.ports and c.sec = j.sec.
For j.port = 'ABC'
, if there is no c.sec = j.sec
for the same ports, then JOIN ON LEFT(c.sec, 6) = LEFT(j.sec, 6)
For other j.ports
, I only want to join ON j.ports = c.ports and c.sec = j.sec
How can I do that?
Example Data
Table c
+------+------------+------------+
| Port | sec | Other |
+------+------------+------------+
| ABC | abcdefghij | ONE |
| ABC | klmnop | TWO |
| LMN | qwertyuiop | THREE |
| XYZ | asdfghjkl | FOUR |
+------+------------+------------+
Table j
+------+------------+
| Port | sec |
+------+------------+
| ABC | abcdefxxxx |
| ABC | klmnop |
| LMN | qwertyuiop |
| XYZ | zxcvbnm |
+------+------------+
EDITED: Desired Results
+------+------------+------------+
| Port | sec | other |
+------+------------+------------+
| ABC | abcdefghij | ONE | --> mactching on sec's 1st 6 characters
| ABC | klmnop | TWO | --> mactching on sec
| LMN | qwertyuiop | THREE | --> mactching on sec
+------+------------+------------+
回答1:
This does conditional joining:
select t1.*, t2.*
from j t1 inner join c t2
on t2.ports = t1.ports and
case
when exists (select 1 from c where sec = t1.sec) then t1.sec
else left(t1.sec, 6)
end =
case
when exists (select 1 from c where sec = t1.sec) then t2.sec
else left(t2.sec, 6)
end
I question its efficiency but I think it does what you need.
See the demo.
回答2:
You can do two outer joins and then do isnull type of operation. In oracle nvl is isnull of sqlserver
with c as
(
select 'ABC' port, 'abcdefghij' sec from dual
union all select 'ABC', 'klmnop' from dual
union all select 'LMN', 'qwertyuiop' from dual
union all select 'XYZ', 'asdfghjkl' from dual
),
j as
(
select 'ABC' port, 'abcdefxxxx' sec from dual
union all select 'ABC', 'klmnop' from dual
union all select 'LMN', 'qwertyuiop' from dual
union all select 'XYZ', 'zxcvbnm' from dual
)
select c.port, c.sec, nvl(j_full.sec, j_part.sec) j_sec
from c
left outer join j j_full on j_full.port = c.port and j_full.sec = c.sec
left outer join j j_part on j_part.port = c.port and substr(j_part.sec,1,6) = substr(c.sec,1,6)
order by 1,2
回答3:
One way would be to just inner join on the less strict predicate then use a ranking function to discard unwanted rows in the event that c.port = 'ABC'
and the stricter condition got a match for a particular c.port, c.sec
combination.
with cte as
(
select c.port as cPort,
c.sec as cSec,
c.other as other,
j.sec as jSec,
RANK() OVER (PARTITION BY c.port, c.sec ORDER BY CASE WHEN c.port = 'ABC' AND j.sec = c.sec THEN 0 ELSE 1 END) AS rnk
from c inner join j on left(j.sec,6) = left(c.sec,6)
)
SELECT cPort, cSec, other, jSec
FROM cte
WHERE rnk = 1
来源:https://stackoverflow.com/questions/55838361/conditionally-fallback-to-different-join-condition-if-stricter-condition-not-mat