Home > Mobile >  How to conditionally use an inner join?
How to conditionally use an inner join?

Time:09-23

I'm working on a CTE (probably not relevant), and I have two possible inner joins, but the second one should only be used if the first one is empty (it could be used anyway, in fact, IT MUST be executed alongside the first inner join:

with recursive "test_table" as (
     select stuff from test_table 
          inner join base_table bt on bt.property = test_table.property // THIS MIGHT FIND A SET OF ROWS
          inner join base_table bt2 on bt2.id = test_table.innerProperty // THIS MIGHT FIND SOME OTHER ROWS
)

The thing is, this doesn't work, as it brings 0 rows, but if I execute the first inner join OR the second one separetely, it works just fine. I tried using an OR but it loops infinitely.

I'm using postgres 13, btw

CodePudding user response:

Assuming some sample data on your structures:

create table test_table (
  stuff text,
  property text,
  innerProperty text
);

create table base_table (
  property text,
  id text,
  stuff text
);

insert into test_table values
('foot1', 'foot', 'ball'),
('foot2', 'ultimate', 'frisbee'),
('foot3', 'base', 'ball'),
('foot4', 'nix', 'nein');

insert into base_table values
('foot', 'foot', 'feet'),
('flat', 'ball', 'pancake'),
('flit', 'frisbee', 'float');

I think in general using left outer joins instead of inner joins is the basis of the solution:

select t.stuff, bt.stuff, bt2.stuff
from
  test_table t 
  left join base_table bt on bt.property = t.property
  left join base_table bt2 on bt2.id = t.innerProperty
where
  bt.property is not null or bt2.id is not null

Where the where clause mimics basically what you are trying to do -- you want a match from one or the other.

However, this yields these results:

foot1   feet    pancake    <- Bt2.stuff should be empty if BT matches?
foot2           float
foot3           pancake

My understanding is that if there is a match on bt then you don't want to even evaluate the second join -- make bt2 conditional on NO match from bt. If that is the case you can simply change the join condition on bt2 from this:

left join base_table bt2 on bt2.id = t.innerProperty

to this:

left join base_table bt2 on bt2.id = t.innerProperty and bt.property is null

Meaning, don't do the BT2 join if BT was successful.

Which you can see skips removes bt2.stuff = pancake from the query:

foot1   feet    
foot2           float
foot3           pancake

Let me know if this is what you had in mind.

CodePudding user response:

INNER JOIN only keeps rows that meet the join condition on both sides, and in that sense it acts like a filter. When you have two inner joins, you are performing this filter twice, so when you get 0 rows returned it means that no row meets both of the 2 join conditions you used.

If I understand your question correctly, rather than do 2 inner joins, you should opt for 2 left joins and use COALESCE to set the condition that when the first join didn't produce any results, use the second join.

SELECT
    -- If bt.yourcolumn is NULL, 
    -- because the JOIN condition was not met, use b2.yourcolumn
    COALESCE(bt.yourcolumn, bt2.yourcolumn) AS yourcolumn
FROM ... --use left joins
  • Related