Tips/Database, ORM

JOIN 조건에 OR를 써야 한다면

dextto™ 2021. 10. 9. 22:46

결론부터 이야기하면,

⚠️ 어떤 테이블을 JOIN할 때 ON의 조건으로 두개의 컬럼을 OR로 연결하면 안된다.

 

예를 들어 t1, t2 두개의 테이블이 있다. c1 컬럼은 둘다 primary key이고, t2.c2에는 인덱스가 걸려 있다.

create table Test.t1
(
	c1 varchar(60) not null
		primary key,
	c2 varchar(60) null
);

create table Test.t2
(
	c1 varchar(60) not null
		primary key,
	c2 varchar(60) null
);

create index idx_t2_c2
	on Test.t2 (c2);

이 두 테이블을 조인해서 조회를 하는데 조건을 다음처럼 걸고 싶은 경우가 있다.

SELECT * FROM t1
    JOIN t2 on (t1.c1 = t2.c1 OR t1.c1 = t2.c2);

이럴 경우 t2의 t1.c1 컬럼을 t2의 모든 데이터와 비교하게 된다. 이게 인덱스가 이미 걸려있어도 그렇게 동작한다.

t2 테이블에 500개의 데이터를 넣고 위 커리를 EXPLAIN 으로 돌려보면 t2의 열이 500개가 적용되는 것을 알 수 있다.

mysql> EXPLAIN
    -> SELECT * FROM t1
    ->     JOIN t2 on (t1.c1 = t2.c1 OR t1.c1 = t2.c2);
+----+-------------+-------+------------+------+-------------------+------+---------+------+------+----------+------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys     | key  | key_len | ref  | rows | filtered | Extra                                          |
+----+-------------+-------+------------+------+-------------------+------+---------+------+------+----------+------------------------------------------------+
|  1 | SIMPLE      | t1    | NULL       | ALL  | PRIMARY           | NULL | NULL    | NULL |    1 |   100.00 | NULL                                           |
|  1 | SIMPLE      | t2    | NULL       | ALL  | PRIMARY,idx_t2_c2 | NULL | NULL    | NULL |  500 |    19.00 | Range checked for each record (index map: 0x3) |
+----+-------------+-------+------------+------+-------------------+------+---------+------+------+----------+------------------------------------------------+

따라서 이런 경우는 JOIN문을 수정해야 한다.

SELECT * FROM t1
    LEFT JOIN t2 AS t2A on t1.c1 = t2A.c1
    LEFT JOIN t2 AS t2B on t1.c1 = t2B.c2;

MySQL은 JOIN 으로만 하면 INNER JOIN으로 동작하기 때문에 LEFT JOIN으로 수정해서 JOIN한 테이블이 각각 조회되도록 수정한다. 그리고 각각에 대해 alias를 지정해서 구분한다. EXPLAIN을 해 보면 인덱스가 걸려 있기 때문에 단 1개의 열만 참조되는 걸 볼 수 있다.

mysql> EXPLAIN
    -> SELECT * FROM t1
    ->     LEFT JOIN t2 AS t2A on t1.c1 = t2A.c1
    ->     LEFT JOIN t2 AS t2B on t1.c1 = t2B.c2;
+----+-------------+-------+------------+--------+---------------+-----------+---------+------------+------+----------+-------------+
| id | select_type | table | partitions | type   | possible_keys | key       | key_len | ref        | rows | filtered | Extra       |
+----+-------------+-------+------------+--------+---------------+-----------+---------+------------+------+----------+-------------+
|  1 | SIMPLE      | t1    | NULL       | ALL    | NULL          | NULL      | NULL    | NULL       |    1 |   100.00 | NULL        |
|  1 | SIMPLE      | t2A   | NULL       | eq_ref | PRIMARY       | PRIMARY   | 242     | test.t1.c1 |    1 |   100.00 | NULL        |
|  1 | SIMPLE      | t2B   | NULL       | ref    | idx_t2_c2     | idx_t2_c2 | 243     | test.t1.c1 |    1 |   100.00 | Using index |
+----+-------------+-------+------------+--------+---------------+-----------+---------+------------+------+----------+-------------+

또는 UNION을 이용해서 쿼리를 만들어도 된다.

SELECT * FROM t1
    JOIN t2 on t1.c1 = t2.c1
UNION
SELECT * FROM t1
    JOIN t2 on t1.c1 = t2.c2;

 

반응형