Generally speaking, RDBMS are not able to optimise subqueries as well as they can optimise proper table joins. As documented under Rewriting Subqueries as Joins (emphasis added):
Sometimes there are other ways to test membership in a set of values than by using a subquery. Also, on some occasions, it is not only possible to rewrite a query without a subquery, but it can be more efficient to make use of some of these techniques rather than to use subqueries. One of these is the
IN()
construct:For example, this query:
SELECT * FROM t1 WHERE id IN (SELECT id FROM t2);
Can be rewritten as:
SELECT DISTINCT t1.* FROM t1, t2 WHERE t1.id=t2.id;
In your abstract case (i.e. ignoring other obvious improvements one would make to this query in reality):
SELECT DISTINCT t1.*
FROM test t1
JOIN test t2 USING (id)
JOIN test t3 USING (id)
WHERE t2.id < 5
OR t3.id > 45;
For which the execution plan is:
+----+-------------+-------+--------+---------------+---------+---------+------------------+------+-------------------------------------------+ | ID | SELECT_TYPE | TABLE | TYPE | POSSIBLE_KEYS | KEY | KEY_LEN | REF | ROWS | EXTRA | +----+-------------+-------+--------+---------------+---------+---------+------------------+------+-------------------------------------------+ | 1 | SIMPLE | t1 | range | PRIMARY | PRIMARY | 4 | (null) | 9 | Using where; Using index; Using temporary | | 1 | SIMPLE | t2 | eq_ref | PRIMARY | PRIMARY | 4 | db_2_129b4.t1.id | 1 | Using index; Distinct | | 1 | SIMPLE | t3 | eq_ref | PRIMARY | PRIMARY | 4 | db_2_129b4.t1.id | 1 | Using index; Distinct | +----+-------------+-------+--------+---------------+---------+---------+------------------+------+-------------------------------------------+
See it on sqlfiddle.