skip to Main Content

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

2

Answers


  1. 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
    
    Login or Signup to reply.
  2. 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.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search