Cc Cc
Cc Cc

Reputation: 1

LEFT JOIN tables but exclude if conditions don't match

I have dynamic conditions. Sometimes there are conditions and sometimes not. I want to get all results from the left but exclude rows that do not match conditions. Is there any way I can achieve this? With what I have it always gets all rows from the left. If I use inner join it won't get the records from the base table if they can't join. I need to be able to get all records from the anime table, however if there is a condition set, then only get the records that match the conditions.

SELECT anime.id, anime.title, IF(english IS NULL or english = '', anime.title, english) as english, anime.slug
FROM anime
LEFT JOIN genres g ON g.anime_id = anime.id
LEFT JOIN episodes e ON e.anime_id = anime.id
LEFT JOIN videos v on v.episode_id = e.id
$conds
GROUP BY anime.id
ORDER BY $order
LIMIT $limit

$conds, $order, and $limit is the dynamically generated with php.

an example for a condition is

WHERE anime.status = 'completed' AND v.type = 'subbed'

The problem here is if I use left join the condition for v.type is completely ignored and it gets all records from the anime table.

Doing a left join will get all rows from the anime table regardless. But I want to not get all rows if there is a condition and only the rows that match the condition. But if there is no conditions then get all rows anyways.

If I use INNER JOIN instead of LEFT JOIN, if an anime has no episodes or videos it won't get them, but I NEED to get them.

Upvotes: 0

Views: 1978

Answers (3)

I had a similar trouble, you need to use EXISTS, as the following example:

SELECT t1.* FROM table_1 t1 
WHERE EXISTS (SELECT t2.* FROM table2 t2
WHERE t1.id=t2.t1_id AND t2.some_field='some value')

So your query should look like this:

SELECT anime.id, anime.title, IF(english IS NULL or english = '', anime.title, english) as english, anime.slug
FROM anime
LEFT JOIN genres g ON g.anime_id = anime.id
LEFT JOIN episodes e ON e.anime_id = anime.id
WHERE EXISTS (SELECT v.* FROM videos v WHERE v.episode_id = e.id AND v.type = 'subbed')
AND anime.status = 'completed'
GROUP BY anime.id
ORDER BY $order
LIMIT $limit

i know that your question is old, but i submit this for anyone with a similar problem :)

Upvotes: 1

Sergey Menshov
Sergey Menshov

Reputation: 3906

Maybe you need the following

$query_with_condition = "SELECT ...
FROM anime
LEFT JOIN genres g ON g.anime_id = anime.id
LEFT JOIN episodes e ON e.anime_id = anime.id
LEFT JOIN videos v on v.episode_id = e.id
$conds
GROUP BY anime.id
ORDER BY $order
LIMIT $limit";

$query_without_condition = "SELECT ...
FROM anime
LEFT JOIN genres g ON g.anime_id = anime.id
LEFT JOIN episodes e ON e.anime_id = anime.id
LEFT JOIN videos v on v.episode_id = e.id
GROUP BY anime.id
ORDER BY $order
LIMIT $limit";

$result = mysql_query($query_with_condition, $conn);
$num_rows = mysql_num_rows($result);

// if the query with conditions doesn't return any rows
if(!$num_rows){
  // then we use the second query where no conditions
  $result = mysql_query($query_without_condition, $conn);
}

// use $result

I don't know but maybe you need the following

WHERE anime.status = 'completed' AND IFNULL(v.type,'subbed') = 'subbed'

It also returns row from the left-table if this row haven't link with a video.

If I'm mistaken please, add test data for tables anime and videos. And show an expected result for this data.

Upvotes: 0

Paul Maxwell
Paul Maxwell

Reputation: 35613

The "generic answer" is if you want the left join to "survive" a where clause condition you must also allow for NULL to be returned by the left join.

WHERE anime.status = 'completed' AND (v.type = 'subbed' OR v.type IS NULL)

nb: If you use functions to mimic the same effect then you removed access to indexes and that may badly affect query performance.

Upvotes: 0

Related Questions