1

I have table in a PostgreSQL 9.2.24 database:

CREATE TABLE episodes_list (
  mid      serial PRIMARY KEY  -- movie id
, title    text NOT NULL
, is_movie integer NOT NULL    -- 1 for movie, 0 for episode
);

I need to display on the left titles of episodes, on the right titles of all movies (even those which don't have episodes).

103 | Microcosmos (high quality)                    |        1
103 | Snails' sexual act                            |        0
103 | Landing                                       |        0

Here are a few records. So eg. 'Microcosmos' should be on the right twice, with snail's sex act and landing on the left. That's how it should look like:

 Landing                                       | Microcosmos (high quality)
 Snails' sexual act                            | Microcosmos (high quality)

I wrote something like this:

select e1.title, e2.title from episodes_list e1 right join episodes_list e2 on e1.is_movie = 0 and e2.is_movie = 1 and e1.mid = e2.mid;

But it still shows on the right titles of episodes, rather then only movie titles (so I'm getting additional lines which I shouldn't be getting). So it looks like this: But it looks like this:

 Landing                                       | Microcosmos (high quality)
 Snails' sexual act                            | Microcosmos (high quality)
                                               | Snails' sexual act

What am I doing wrong here and how the query should look like?

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228

2 Answers2

2

This could be your query:

SELECT epi.title AS episode, mov.title AS movie
FROM        episodes_list epi
RIGHT  JOIN episodes_list mov ON epi.is_movie = 0
                             AND mov.mid = epi.mid
WHERE  mov.is_movie = 1;

This return all movies to the right, extended with episodes to the left where available.

is_movie should really be a boolean flag instead. integer is not the right type.

The most important bit to take away from this: format your queries (in any consistent fashion, not necessarily like mine) and use descriptive aliases when things get complicated.

The logic problem you had: conditions in the WHERE clause are equivalent to conditions in an INNER JOIN clause, but not to conditions in an OUTER JOIN.(LEFT JOIN / RIGHT JOIN is short for LEFT OUTER JOIN / RIGHT OUTER JOIN.)
See:

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
1

You need a self LEFT JOIN on the table that selects episode records, with a WHERE clause that filters on movies only.

The below query should get the job done :

select 
    e2.title, 
    e1.title 
from episodes_list e1 
left join episodes_list e2 
    on e2.is_movie = 0
    and e1.mid = e2.mid
where e1.is_movie = 1

e1 represents movie records. e2 represents episodes records. The left join allows movies that have no episodes to be taken into account.

GMB
  • 216,147
  • 25
  • 84
  • 135
  • That works perfectly, but why doesn't the 'on e1.is_movie = 1' filter those records out and i have to add the 'where e1.is_movie = 1'? – ItsNotFunny Dec 17 '18 at 17:55
  • The LEFT JOIN is an optional join, meaning that even if there is no episode the query will still return the movie. You don’t want the condition that filters on movies to be optional, you need it to be mandatory. Else, you will end up with episodes being displayed in the first column (with second column empty) – GMB Dec 17 '18 at 18:28