결론부터 이야기하면,
⚠️ 어떤 테이블을 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;
반응형
'Tips > Database, ORM' 카테고리의 다른 글
[MySQL] EXPLAIN을 이용한 쿼리/테이블 최적화 (0) | 2021.10.09 |
---|---|
[TypeORM] Embedded Entity의 Column 이름 지정하기 (2) | 2020.05.12 |