JOIN은 간단하면서도 어렵다. JOIN을 더욱 쉽게 이해할 수 없을까 고민하며 정리를 해보겠다.
JOIN에 앞서..
쿼리 실행 순서
MySQL 등의 DBMS가 DB로부터 데이터를 추출하는 과정은 다음과 같다.
💡 SQL 실제 실행 순서
어디서 찾을지 ?
- FROM
- JOIN
어떤 조건의 데이터 중 ?- WHERE
- GROUP BY
- HAVING
어떤 속성 ?- SELECT
순서 ?- ORDER BY
한 테이블에서 데이터를 찾고자 한다면 쿼리 작성은 매우 간단하다.
SELECT 속성
FROM 테이블
WHERE 조건
GROUP BY 기준
ORDER BY 정렬
두개 이상의 테이블에서 여러 속성을 찾을 쿼리를 작성한다면 어떻게 할 수 있을까?
- FROM에 찾고 싶은 대상 테이블들을 설정한다.
- 테이블들을 공통의 속성으로 이어준다.
- 원하는 속성을 테이블 기준으로 뽑아낸다.
테이블들의 공통의 속성으로 이어준다는 건 무엇일까? 이어주지 않으면 DBMS는 하나의 테이블로 결과 테이블을 생성하기 위해 기본적으로 FROM에 테이블들의 레코드 수를 곱한 만큼 레코드를 뽑아낸다.
쿼리에 앞서 테이블의 정보는 이렇다.
FK를 설정하진 않았지만, pl이 programming_language의 PK를 참조한다고 가정하자. 그리고 다음과 같은 쿼리를 실행해보자.
SELECT *
FROM person, programming_language;
/*
결과
+----+-------+------------+--------+------------+------------+------+------+----+------------+-----------------------------------------------------------------------+
| id | name | birth_year | gender | job | blood_type | MBTI | pl | id | name | description |
+----+-------+------------+--------+------------+------------+------+------+----+------------+-----------------------------------------------------------------------+
| 1 | 표*희 | 1961 | 남 | 무직 | O | ISTP | 0 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 1 | 표*희 | 1961 | 남 | 무직 | O | ISTP | 0 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 1 | 표*희 | 1961 | 남 | 무직 | O | ISTP | 0 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 1 | 표*희 | 1961 | 남 | 무직 | O | ISTP | 0 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 2 | 김*주 | 1963 | 남 | 경비원 | A | ENFP | 0 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 2 | 김*주 | 1963 | 남 | 경비원 | A | ENFP | 0 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 2 | 김*주 | 1963 | 남 | 경비원 | A | ENFP | 0 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 2 | 김*주 | 1963 | 남 | 경비원 | A | ENFP | 0 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 3 | 탁*진 | 1977 | 남 | 무직 | A | ESTP | 0 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 3 | 탁*진 | 1977 | 남 | 무직 | A | ESTP | 0 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 3 | 탁*진 | 1977 | 남 | 무직 | A | ESTP | 0 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 3 | 탁*진 | 1977 | 남 | 무직 | A | ESTP | 0 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 4 | 안*현 | 1989 | 여 | 프로그래머 | B | ESTJ | 2 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 4 | 안*현 | 1989 | 여 | 프로그래머 | B | ESTJ | 2 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 4 | 안*현 | 1989 | 여 | 프로그래머 | B | ESTJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 4 | 안*현 | 1989 | 여 | 프로그래머 | B | ESTJ | 2 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 5 | 이*훈 | 1973 | 남 | 프로그래머 | B | ENFJ | 2 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 5 | 이*훈 | 1973 | 남 | 프로그래머 | B | ENFJ | 2 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 5 | 이*훈 | 1973 | 남 | 프로그래머 | B | ENFJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 5 | 이*훈 | 1973 | 남 | 프로그래머 | B | ENFJ | 2 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 6 | 노*주 | 1999 | 여 | 프로그래머 | B | ISTJ | 1 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 6 | 노*주 | 1999 | 여 | 프로그래머 | B | ISTJ | 1 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 6 | 노*주 | 1999 | 여 | 프로그래머 | B | ISTJ | 1 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 6 | 노*주 | 1999 | 여 | 프로그래머 | B | ISTJ | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 7 | 추*수 | 2001 | 남 | 운동선수 | AB | INFP | 0 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 7 | 추*수 | 2001 | 남 | 운동선수 | AB | INFP | 0 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 7 | 추*수 | 2001 | 남 | 운동선수 | AB | INFP | 0 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 7 | 추*수 | 2001 | 남 | 운동선수 | AB | INFP | 0 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 8 | 풍*연 | 2003 | 여 | 대학생 | A | ISFJ | 1 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 8 | 풍*연 | 2003 | 여 | 대학생 | A | ISFJ | 1 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 8 | 풍*연 | 2003 | 여 | 대학생 | A | ISFJ | 1 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 8 | 풍*연 | 2003 | 여 | 대학생 | A | ISFJ | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 9 | 송*진 | 2000 | 남 | 대학생 | A | ESFP | 0 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 9 | 송*진 | 2000 | 남 | 대학생 | A | ESFP | 0 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 9 | 송*진 | 2000 | 남 | 대학생 | A | ESFP | 0 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 9 | 송*진 | 2000 | 남 | 대학생 | A | ESFP | 0 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 10 | 임*진 | 2012 | 여 | 학생 | O | ENTJ | 0 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 10 | 임*진 | 2012 | 여 | 학생 | O | ENTJ | 0 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 10 | 임*진 | 2012 | 여 | 학생 | O | ENTJ | 0 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 10 | 임*진 | 2012 | 여 | 학생 | O | ENTJ | 0 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 11 | 양*선 | 2019 | 남 | 유치원생 | O | ENFJ | 0 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 11 | 양*선 | 2019 | 남 | 유치원생 | O | ENFJ | 0 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 11 | 양*선 | 2019 | 남 | 유치원생 | O | ENFJ | 0 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 11 | 양*선 | 2019 | 남 | 유치원생 | O | ENFJ | 0 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 12 | 황*호 | 1992 | 남 | 회사원 | O | ISFP | 1 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 12 | 황*호 | 1992 | 남 | 회사원 | O | ISFP | 1 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 12 | 황*호 | 1992 | 남 | 회사원 | O | ISFP | 1 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 12 | 황*호 | 1992 | 남 | 회사원 | O | ISFP | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 13 | 하*영 | 1981 | 여 | 회사원 | O | ISFJ | 0 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 13 | 하*영 | 1981 | 여 | 회사원 | O | ISFJ | 0 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 13 | 하*영 | 1981 | 여 | 회사원 | O | ISFJ | 0 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 13 | 하*영 | 1981 | 여 | 회사원 | O | ISFJ | 0 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 14 | 전*준 | 1979 | 남 | 회사원 | B | ESFJ | 0 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 14 | 전*준 | 1979 | 남 | 회사원 | B | ESFJ | 0 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 14 | 전*준 | 1979 | 남 | 회사원 | B | ESFJ | 0 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 14 | 전*준 | 1979 | 남 | 회사원 | B | ESFJ | 0 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 15 | 황*윤 | 1965 | 남 | 회사원 | B | ESTP | 0 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 15 | 황*윤 | 1965 | 남 | 회사원 | B | ESTP | 0 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 15 | 황*윤 | 1965 | 남 | 회사원 | B | ESTP | 0 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 15 | 황*윤 | 1965 | 남 | 회사원 | B | ESTP | 0 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 16 | 안*빈 | 1997 | 남 | 가수 | B | ESTJ | 0 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 16 | 안*빈 | 1997 | 남 | 가수 | B | ESTJ | 0 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 16 | 안*빈 | 1997 | 남 | 가수 | B | ESTJ | 0 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 16 | 안*빈 | 1997 | 남 | 가수 | B | ESTJ | 0 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 17 | 표*곤 | 1983 | 남 | 강사 | B | INFJ | 3 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 17 | 표*곤 | 1983 | 남 | 강사 | B | INFJ | 3 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 17 | 표*곤 | 1983 | 남 | 강사 | B | INFJ | 3 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 17 | 표*곤 | 1983 | 남 | 강사 | B | INFJ | 3 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 18 | 설*호 | 1981 | 남 | 운동선수 | A | INFP | 0 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 18 | 설*호 | 1981 | 남 | 운동선수 | A | INFP | 0 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 18 | 설*호 | 1981 | 남 | 운동선수 | A | INFP | 0 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 18 | 설*호 | 1981 | 남 | 운동선수 | A | INFP | 0 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 19 | 서*영 | 1972 | 여 | 무직 | O | ENTJ | 0 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 19 | 서*영 | 1972 | 여 | 무직 | O | ENTJ | 0 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 19 | 서*영 | 1972 | 여 | 무직 | O | ENTJ | 0 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 19 | 서*영 | 1972 | 여 | 무직 | O | ENTJ | 0 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 20 | 정*현 | 2000 | 남 | 학생 | O | ISTP | 0 | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
| 20 | 정*현 | 2000 | 남 | 학생 | O | ISTP | 0 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 20 | 정*현 | 2000 | 남 | 학생 | O | ISTP | 0 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 20 | 정*현 | 2000 | 남 | 학생 | O | ISTP | 0 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
+----+-------+------------+--------+------------+------------+------+------+----+------------+-----------------------------------------------------------------------+
80 rows in set (0.00 sec)
*/
결과 테이블의 row 수는 전체 사람 수 (20) * 프로그래밍 언어 수(4)
만큼의 테이블을 반환하는데, 이는 DBMS가 서로 다른 두 테이블의 레코드들을 어떻게 하나로 묶을지 모르기 때문에 모든 레코드를 이어서 결과를 만들어낸다.
이를 이어주기 위한 방법은 간단하다. WHERE 절에 두 테이블의 공통의 속성이 같은 것만 뽑아내도록 정하면 되지 않겠는가?
SELECT *
FROM person, programming_language
WHERE person.pl = programming_language.id;
/*
결과
+----+-------+------------+--------+------------+------------+------+------+----+------------+--------------------------------------------------------------+
| id | name | birth_year | gender | job | blood_type | MBTI | pl | id | name | description |
+----+-------+------------+--------+------------+------------+------+------+----+------------+--------------------------------------------------------------+
| 4 | 안*현 | 1989 | 여 | 프로그래머 | B | ESTJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 5 | 이*훈 | 1973 | 남 | 프로그래머 | B | ENFJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 6 | 노*주 | 1999 | 여 | 프로그래머 | B | ISTJ | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 8 | 풍*연 | 2003 | 여 | 대학생 | A | ISFJ | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 12 | 황*호 | 1992 | 남 | 회사원 | O | ISFP | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 17 | 표*곤 | 1983 | 남 | 강사 | B | INFJ | 3 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
+----+-------+------------+--------+------------+------------+------+------+----+------------+--------------------------------------------------------------+
6 rows in set (0.00 sec)
*/
이와 같이 FROM에서 두 테이블의 공통된 속성을 이어준다면, 의도했던 유효한 데이터만 확인할 수 있다.
쿼리 실행 순서에 따르면 다음과 같다.
- FROM
- person테이블을 준비한다.
- programming_language를 준비한다
- WHERE
- person의 레코드를 하나 선택한다.
- 해당 레코드의 pl 속성을 가지고 programming_language에서 같은 값의 id를 가진 레코드를 찾는다.
이렇게 테이블을 이어주는 작업을 FROM과 WHERE을 이용한 것이 아닌, 명시적인 어떤 작업으로 표시하고 싶지 않은가? 이를 위해 JOIN이 존재한다.
SELECT *
FROM person
JOIN programming_language ON person.pl=programming_language.id;
/*
결과
+----+-------+------------+--------+------------+------------+------+------+----+------------+--------------------------------------------------------------+
| id | name | birth_year | gender | job | blood_type | MBTI | pl | id | name | description |
+----+-------+------------+--------+------------+------------+------+------+----+------------+--------------------------------------------------------------+
| 4 | 안*현 | 1989 | 여 | 프로그래머 | B | ESTJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 5 | 이*훈 | 1973 | 남 | 프로그래머 | B | ENFJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 6 | 노*주 | 1999 | 여 | 프로그래머 | B | ISTJ | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 8 | 풍*연 | 2003 | 여 | 대학생 | A | ISFJ | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 12 | 황*호 | 1992 | 남 | 회사원 | O | ISFP | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 17 | 표*곤 | 1983 | 남 | 강사 | B | INFJ | 3 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
+----+-------+------------+--------+------------+------------+------+------+----+------------+--------------------------------------------------------------+
6 rows in set (0.00 sec)
*/
FROM person
- 기준이 되는 person
테이블의 레코드를 조회할 것인데,
JOIN programming_language
- programming_language 테이블의 레코드들 또한 참여시키겠다.
ON person.pl=programming_language.id
- person의 pl과 programming_language의 id가 같은 레코드를 이어서!
즉, JOIN은 기준 테이블에 JOIN 대상 테이블들의 속성들을 참여시켜주는 것이다.
테이블 명이 너무 길다고 느껴진다면? Alias를 활용하면 된다.
SELECT *
FROM person p
JOIN programming_language pl ON p.pl=pl.id;
만약 세 개 이상의 테이블을 이어준다면 어떻게 하면 될까?
JOIN이 대상을 좁히는 법
두 개 이상의 테이블 대상
FROM, JOIN 등으로 대상 테이블이 필요할 때마다 DBMS는 WHERE 절이나 ON 이후의 조건을 처리하며 하나의 대상 테이블로 대상을 좁힌다. 더 나은 이해를 위해 세 개의 테이블로 가정해보자. 각각 100개의 레코드를 가진 A, B, C의 테이블에서 데이터를 찾기 위해 FROM 절에 A, B, C를 넣었다면 DBMS의 동작은 다음과 같다.
# 예시 쿼리
SELECT *
FROM A, B, C
WHERE A.id=B.aid and B.id=C.bid;
WHERE절의 A.id=B.aid
를 실행하기 위해 A와 B테이블을 대상으로 작업한다. 이후 겹치는 50개의 레코드로 이루어진 임시 테이블으로 대상이 바뀐다. 이후, 임시 테이블의 50개 레코드 중에서 B.id=C.bid
의 조건을 만족하는 레코드를 찾아 결과 테이블로 출력한다.
즉, A, B, C를 한 번에 찾는 것이 아니라 SQL작성자가 작성한 순서에 따라서 조회 대상 테이블을 순차적으로 갱신해나간다. FROM이 아닌 JOIN을 사용하더라도 이는 마찬가지이다.
SELECT *
FROM A
JOIN B ON A.id=B.aid
JOIN C ON B.id=C.bid
처음 대상을 A로 잡고, B를 이에 참여시켜 ON 이후의 조건으로 데이터를 필터링한 임시 테이블을 만든다. 이후, 50개의 레코드를 가진 임시 테이블에 C를 참여시켜 ON 이후의 조건으로 데이터를 필터링한다.
이해를 쉽게 하기 위해 위와 같은 예시를 들었지만, DBMS의 JOIN 순서는 각 벤더사의 DBMS마다의 옵티마이저가 알아서 최적으로 정한다. JOIN의 순서보단 ON 뒤에 나오는 조건으로 이러한 순서에 관여할 수 있고, 성능 최적화를 진행할 수 있다. 이건 나중에..
JOIN .. ON과 FROM .. WHERE
여기까지 봤다면, JOIN .. ON과 FROM .. WHERE는 사실 같은 동작을 한다고 느낄 것 같다. 그렇다면, 이어주는 작업이 아니라 WHERE 절에서 사용한 것처럼 특정 조건을 넣어주는 것도 가능할까?
SELECT *
FROM person p, programming_language pl
WHERE p.pl = pl.id and p.birth_year < 2000;
/*
+----+-------+------------+--------+------------+------------+------+------+----+------------+--------------------------------------------------------------+
| id | name | birth_year | gender | job | blood_type | MBTI | pl | id | name | description |
+----+-------+------------+--------+------------+------------+------+------+----+------------+--------------------------------------------------------------+
| 4 | 안*현 | 1989 | 여 | 프로그래머 | B | ESTJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 5 | 이*훈 | 1973 | 남 | 프로그래머 | B | ENFJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 6 | 노*주 | 1999 | 여 | 프로그래머 | B | ISTJ | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 12 | 황*호 | 1992 | 남 | 회사원 | O | ISFP | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 17 | 표*곤 | 1983 | 남 | 강사 | B | INFJ | 3 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
+----+-------+------------+--------+------------+------------+------+------+----+------------+--------------------------------------------------------------+
5 rows in set (0.00 sec)
*/
굉장히 친숙한 쿼리이다. 같은 속성을 가진 결과에서 person의 탄생년도가 2000 이하인 데이터를 찾는. 이를 JOIN으로 나타낸다면 어떻게 가능할까? 답은 생각했던대로다.
SELECT *
FROM person p
JOIN programming_language pl ON p.pl = pl.id and p.birth_year < 2000;
/*
+----+-------+------------+--------+------------+------------+------+------+----+------------+--------------------------------------------------------------+
| id | name | birth_year | gender | job | blood_type | MBTI | pl | id | name | description |
+----+-------+------------+--------+------------+------------+------+------+----+------------+--------------------------------------------------------------+
| 4 | 안*현 | 1989 | 여 | 프로그래머 | B | ESTJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 5 | 이*훈 | 1973 | 남 | 프로그래머 | B | ENFJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 6 | 노*주 | 1999 | 여 | 프로그래머 | B | ISTJ | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 12 | 황*호 | 1992 | 남 | 회사원 | O | ISFP | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 17 | 표*곤 | 1983 | 남 | 강사 | B | INFJ | 3 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
+----+-------+------------+--------+------------+------------+------+------+----+------------+--------------------------------------------------------------+
5 rows in set (0.00 sec)
*/
여기까지 읽었다면 내가 의도했던 바가 전해졌으면 좋겠다. JOIN은 이해하기 어려운 것이 아니다.
JOIN 종류
INNER JOIN
우리는 여태까지 두 테이블의 공통된 속성을 기준으로 이어진 레코드들을 찾았다. 테이블을 집합이라고 본다면, 두 집합의 교집합을 찾은 것이다. 이를 Inner Join이라고 한다.
JOIN문을 작성하며 기준이 되는 대상 테이블이 지속적으로 변한다. 이 때 기준이 되는 테이블을 왼쪽에 있다고 가정하고, 새롭게 들어오는 테이블을 오른쪽에 있다고 볼 수 있다.
SELECT *
FROM person p
JOIN programming_language pl ON p.pl=pl.id;
이렇게 작성된 쿼리는 아래의 그림으로 볼 수 있다.
person테이블이 왼쪽에 있고 programming_language 테이블이 오른쪽에 존재하는 집합들의 관계에서, 두 집합의 교집합을 찾는 명령어가 위에 작성된 쿼리인 것이다.
세 개의 테이블을 참여시켰던 A, B, C의 쿼리는 다음과 같다.
SELECT *
FROM A
JOIN B ON A.id=B.aid
JOIN C ON B.id=C.bid
A와 B의 교집합을 Temp라고 한다면, Temp와 C의 교집합이 결과로 출력되는 것이다. 이 때, 처음 JOIN으로 B를 참여시킬 땐 A가 왼쪽, B가 오른쪽에 존재하고 둘의 교집합인 Temp가 만들어진 후 다음 JOIN으로 C를 참여시킬 땐 Temp가 왼쪽, C가 오른쪽에 위치한다. 이러한 집합의 왼쪽, 오른쪽 위치는 중요하다.
실제로는 A와 B가 먼저 JOIN할지, A와 C가 먼저 JOIN할지, B와 C가 먼저 JOIN할지는 DBMS가 최적으로 정해준다. 더 중요한 것은 인덱스의 활용이다.
LEFT, RIGHT JOIN
집합의 위치에 따라 JOIN의 작동을 조정할 수 있다.
위와 같이 왼쪽에 존재하는 person 테이블의 모든 레코드를 조회하되, programming_language의 속성들을 같이 조회하고 싶다면 LEFT JOIN을 사용할 수 있다.
SELECT *
FROM person p
LEFT JOIN programming_language pl ON p.pl=pl.id;
/*
결과
+----+-------+------------+--------+------------+------------+------+------+------+------------+--------------------------------------------------------------+
| id | name | birth_year | gender | job | blood_type | MBTI | pl | id | name | description |
+----+-------+------------+--------+------------+------------+------+------+------+------------+--------------------------------------------------------------+
| 1 | 표*희 | 1961 | 남 | 무직 | O | ISTP | 0 | NULL | NULL | NULL |
| 2 | 김*주 | 1963 | 남 | 경비원 | A | ENFP | 0 | NULL | NULL | NULL |
| 3 | 탁*진 | 1977 | 남 | 무직 | A | ESTP | 0 | NULL | NULL | NULL |
| 4 | 안*현 | 1989 | 여 | 프로그래머 | B | ESTJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 5 | 이*훈 | 1973 | 남 | 프로그래머 | B | ENFJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 6 | 노*주 | 1999 | 여 | 프로그래머 | B | ISTJ | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 7 | 추*수 | 2001 | 남 | 운동선수 | AB | INFP | 0 | NULL | NULL | NULL |
| 8 | 풍*연 | 2003 | 여 | 대학생 | A | ISFJ | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 9 | 송*진 | 2000 | 남 | 대학생 | A | ESFP | 0 | NULL | NULL | NULL |
| 10 | 임*진 | 2012 | 여 | 학생 | O | ENTJ | 0 | NULL | NULL | NULL |
| 11 | 양*선 | 2019 | 남 | 유치원생 | O | ENFJ | 0 | NULL | NULL | NULL |
| 12 | 황*호 | 1992 | 남 | 회사원 | O | ISFP | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 13 | 하*영 | 1981 | 여 | 회사원 | O | ISFJ | 0 | NULL | NULL | NULL |
| 14 | 전*준 | 1979 | 남 | 회사원 | B | ESFJ | 0 | NULL | NULL | NULL |
| 15 | 황*윤 | 1965 | 남 | 회사원 | B | ESTP | 0 | NULL | NULL | NULL |
| 16 | 안*빈 | 1997 | 남 | 가수 | B | ESTJ | 0 | NULL | NULL | NULL |
| 17 | 표*곤 | 1983 | 남 | 강사 | B | INFJ | 3 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 18 | 설*호 | 1981 | 남 | 운동선수 | A | INFP | 0 | NULL | NULL | NULL |
| 19 | 서*영 | 1972 | 여 | 무직 | O | ENTJ | 0 | NULL | NULL | NULL |
| 20 | 정*현 | 2000 | 남 | 학생 | O | ISTP | 0 | NULL | NULL | NULL |
+----+-------+------------+--------+------------+------------+------+------+------+------------+--------------------------------------------------------------+
20 rows in set (0.00 sec)
*/
LEFT JOIN을 사용한다면 왼쪽에 위치한 person 테이블에 programming_language의 속성들을 참여시키는데, 만약 공통의 속성으로 이어지지 않는 레코드가 있다면 위와 같이 전부 NULL로 주입해 결과를 출력한다. JOIN은 기준이 되는 왼쪽 테이블에 오른쪽 테이블의 속성들을 참여시켜준다는 점을 잊지말자!
RIGHT JOIN은 자연스럽게 예상하기 쉬울 것이다.
SELECT *
FROM person p
RIGHT JOIN programming_language pl ON p.pl=pl.id;
/*
결과
+------+-------+------------+--------+------------+------------+------+------+----+------------+-----------------------------------------------------------------------+
| id | name | birth_year | gender | job | blood_type | MBTI | pl | id | name | description |
+------+-------+------------+--------+------------+------------+------+------+----+------------+-----------------------------------------------------------------------+
| 12 | 황*호 | 1992 | 남 | 회사원 | O | ISFP | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 8 | 풍*연 | 2003 | 여 | 대학생 | A | ISFJ | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 6 | 노*주 | 1999 | 여 | 프로그래머 | B | ISTJ | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 5 | 이*훈 | 1973 | 남 | 프로그래머 | B | ENFJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 4 | 안*현 | 1989 | 여 | 프로그래머 | B | ESTJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 17 | 표*곤 | 1983 | 남 | 강사 | B | INFJ | 3 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
+------+-------+------------+--------+------------+------------+------+------+----+------------+-----------------------------------------------------------------------+
7 rows in set (0.00 sec)
*/
오른쪽에 위치한 테이블을 기준으로 매칭되는 왼쪽 테이블의 레코드가 없다면, NULL을 주입해서 결과를 낸다.
LEFT JOIN은 왼쪽에 위치한 테이블의 레코드 수만큼 결과를 내고, RIGHT JOIN은 오른쪽에 위치한 테이블의 레코드 수만큼 결과를 낸다. 물론 기타 조건들을 붙인다면 달라질 것이다.
FULL OUTER JOIN
이렇게 왼쪽, 오른쪽 구분 없이 모든 데이터를 출력하는 것도 가능하다. 어떤 결과가 나올까?
왼쪽 테이블에 있는 모든 레코드를 출력하면서 만약 공통의 속성으로 오른쪽 테이블에 있다면 오른쪽 테이블의 속성들을 이어줄 것이고 (INNER), 이어진 것이 없다면 해당 속성들을 NULL로 채워줄 것이다 (LEFT). 여기까진 LEFT JOIN의 동작이지만, 오른쪽 테이블의 남는 속성들이 있다면 왼쪽 테이블의 속성 값을 NULL로 채워주는 것 (RIGHT) 까지가 두 집합의 합집합을 찾는 과정이다. 이를 FULL OUTER JOIN이라고 한다.
이러한 개념이 존재하지만 FULL OUTER JOIN은 자주 사용되는 MySQL에선 지원하지 않는 명령어이다. 하지만 이러한 동작을 만들 수는 있다!
/*
+------+-------+------------+--------+------------+------------+------+------+------+------------+-----------------------------------------------------------------------+
| id | name | birth_year | gender | job | blood_type | MBTI | pl | id | name | description |
+------+-------+------------+--------+------------+------------+------+------+------+------------+-----------------------------------------------------------------------+
| 1 | 표*희 | 1961 | 남 | 무직 | O | ISTP | 0 | NULL | NULL | NULL |
| 2 | 김*주 | 1963 | 남 | 경비원 | A | ENFP | 0 | NULL | NULL | NULL |
| 3 | 탁*진 | 1977 | 남 | 무직 | A | ESTP | 0 | NULL | NULL | NULL |
| 4 | 안*현 | 1989 | 여 | 프로그래머 | B | ESTJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 5 | 이*훈 | 1973 | 남 | 프로그래머 | B | ENFJ | 2 | 2 | Java | 플랫폼에 독립적인 객체지향 프로그래밍 언어 |
| 6 | 노*주 | 1999 | 여 | 프로그래머 | B | ISTJ | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 7 | 추*수 | 2001 | 남 | 운동선수 | AB | INFP | 0 | NULL | NULL | NULL |
| 8 | 풍*연 | 2003 | 여 | 대학생 | A | ISFJ | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 9 | 송*진 | 2000 | 남 | 대학생 | A | ESFP | 0 | NULL | NULL | NULL |
| 10 | 임*진 | 2012 | 여 | 학생 | O | ENTJ | 0 | NULL | NULL | NULL |
| 11 | 양*선 | 2019 | 남 | 유치원생 | O | ENFJ | 0 | NULL | NULL | NULL |
| 12 | 황*호 | 1992 | 남 | 회사원 | O | ISFP | 1 | 1 | Python | 사용하기 쉽고 쉬운 문법을 갖춘 프로그래밍 언어 |
| 13 | 하*영 | 1981 | 여 | 회사원 | O | ISFJ | 0 | NULL | NULL | NULL |
| 14 | 전*준 | 1979 | 남 | 회사원 | B | ESFJ | 0 | NULL | NULL | NULL |
| 15 | 황*윤 | 1965 | 남 | 회사원 | B | ESTP | 0 | NULL | NULL | NULL |
| 16 | 안*빈 | 1997 | 남 | 가수 | B | ESTJ | 0 | NULL | NULL | NULL |
| 17 | 표*곤 | 1983 | 남 | 강사 | B | INFJ | 3 | 3 | JavaScript | 웹 브라우저에서 동적인 웹 페이지를 만들기 위한 스크립트 언어 |
| 18 | 설*호 | 1981 | 남 | 운동선수 | A | INFP | 0 | NULL | NULL | NULL |
| 19 | 서*영 | 1972 | 여 | 무직 | O | ENTJ | 0 | NULL | NULL | NULL |
| 20 | 정*현 | 2000 | 남 | 학생 | O | ISTP | 0 | NULL | NULL | NULL |
| NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | 4 | SQL | 관계형 데이터베이스에서 데이터를 관리하고 조작하기 위해 표준화된 언어 |
+------+-------+------------+--------+------------+------------+------+------+------+------------+-----------------------------------------------------------------------+
21 rows in set (0.00 sec)
*/
임시 테이블을 포함한 SQL의 테이블들은 집합 개념이고, 이를 생각해본다면 어렵지 않게 구현이 가능하다! 명령어는 잘 생각해보자.
집합 놀이
이런 활용이 가능하다.
CROSS JOIN
이 글의 맨 처음에서 WHERE 조건이 없는 두 테이블의 FROM을 기억하는가? 모든 레코드를 이어서 결과를 만들어냈던 것도 CROSS JOIN이라는 JOIN의 종류로 정의할 수 있다. 집합의 원소 개수들의 곱이기 때문에 Cartesion Product라고도 한다.
CROSS JOIN 명령어가 존재하지만, 여기까지 읽으며 JOIN을 이해했다면 CROSS JOIN은 다양하게 가능하다는 것을 알 수 있을 것이다.
SELECT *
FROM person, programming_language;
SELECT *
FROM person p
CROSS JOIN programming_language pl;
SELECT *
FROM person
JOIN programming_language;
DBMS가 어떤 속성을 기준으로 두 테이블을 연결시킬지 모르게하면 된다!
SELF JOIN
JOIN은 기준이 되는 왼쪽의 테이블에서 오른쪽 테이블의 속성들을 참여시킨다. 이 때, 두 집합은 모두 한 테이블이 될 수 있다! 왜 사용하는지 감이 안 잡히는가? 다음 쿼리를 한 번 보자.
SELECT target.id id, target.name 이름, same_job_blood.name "같은 성과 같은 혈액형을 가진 사람"
FROM person target
JOIN person same_job_blood
on target.job=same_job_blood.job # 같은 성을 가지고
and target.blood_type=same_job_blood.blood_type # 같은 혈액형을 가진 사람
and target.id!=same_job_blood.id; # 다만 본인은 제외
/*
결과
+----+-------+-----------------------------------+
| id | 이름 | 같은 성과 같은 혈액형을 가진 사람 |
+----+-------+-----------------------------------+
| 19 | 서*영 | 표*희 |
| 6 | 노*주 | 안*현 |
| 5 | 이*훈 | 안*현 |
| 6 | 노*주 | 이*훈 |
| 4 | 안*현 | 이*훈 |
| 5 | 이*훈 | 노*주 |
| 4 | 안*현 | 노*주 |
| 9 | 송*진 | 풍*연 |
| 8 | 풍*연 | 송*진 |
| 20 | 정*현 | 임*진 |
| 13 | 하*영 | 황*호 |
| 12 | 황*호 | 하*영 |
| 15 | 황*윤 | 전*준 |
| 14 | 전*준 | 황*윤 |
| 1 | 표*희 | 서*영 |
| 10 | 임*진 | 정*현 |
+----+-------+-----------------------------------+
16 rows in set (0.00 sec)
*/
이 테이블은 SELF JOIN의 예시를 위한 테이블은 아니었기에 조금 억지스러운 면도 있지만, 같은 테이블 내에서 특정 조건을 만족하는 레코드를 이어서 사용하고 싶을 때 가능하다! 위 쿼리의 SELF JOIN을 그림으로 나타내면 다음과 같다.
자주 사용되는 예시로 좀 더 의미있는 사용을 생각해본다면, 고용인들의 테이블에서 멘토 멘티 관계를 생각해볼 수 있다!
JOIN 수행 원리
DBMS가 JOIN을 수행하는 방식은 여러가지가 있다. 이 중 세가지만 알아보도록.
Nested Loop Join
NL Join이라고도 불리는 Nested Loop Join은 중첩 반복문처럼 작동한다.
왼쪽에 위치한 선행테이블에서 조건에 부합하는 레코드를 먼저 선택하고, 선택된 레코드 별로 오른쪽에 위치한 후행테이블을 탐색해 맞는 조건의 레코드를 추출버퍼에 입력한다. 이 때, JOIN의 조건이 되는 속성의 인덱스 테이블을 먼저 스캔하게 된다. 즉, 인덱스가 반드시 걸려있어야만 가능한 방식이다.
조건에 부합하는 레코드를 파악하는 방법은 랜덤하게 레코드를 찝는 것이다. 선행테이블의 레코드를 랜덤하게 고르고, 해당 레코드의 JOIN 기준 속성을 찾아 그 속성의 인덱스 테이블을 스캔하고, 후행테이블에서 그 인덱스와 연결된 레코드를 찾아 버퍼에 넣어놓는다. 선행레코드를 랜덤하게 선정하니, 선행테이블의 크기가 적게 하는 것이 당연히 좋다. 인덱스 테이블을 활용해 선행레코드를 선정하면 조회 속도를 더욱 높일 수 있다.
Sort Merge Join
조건을 가지고 선행과 후행테이블에서 레코드를 먼저 선정한 후, JOIN의 기준으로 되는 속성들을 기준으로 정렬 후 병합하는 방식이다.
1. 선행 테이블에서 조건에 만족하는 행 찾음.
2. 선행 테이블의 조인 키 기준으로정렬.
3. 1-2 작업을 선행 테이블의 만족하는 모든 행에 대해반복.
4. 후행 테이블에서 조건에 만족하는 행 찾음.
5. 후행 테이블의 조인 키 기준으로정렬.
6. 4-5 작업을 후행 테이블의 만족하는 모든 행에 대해반복.
7. 정렬된 결과를 이용하여조인을 수행 후 버퍼에 넣어 출력
Source: https://nebulaisme.tistory.com/106
보다시피 정렬과 병합이 들어가서 연산이 많다. Spark 등의 일부에서만 사용되는 JOIN 방식이다.
Hash Join
Nested Loop Join은 기본적으로 Join 컬럼의 인덱스가 존재해야한다. 만약 인덱스가 없는 컬럼을 기준으로 JOIN을 시도해 Sort Merge 방식을 채택할 때, 테이블들이 너무 크다면 어떻게 될까? 정렬을 하는데 리소스가 많이 들 것이다. 혹은 인덱스가 있는 칼럼으로 JOIN을 시도하지만 테이블이 너무 커서 레코드에 랜덤하게 접근하는 경우가 너무 많다면 비효율적일 것이다. 이로 인해 인덱스가 걸려있지 않은 컬럼으로 JOIN을 시도하거나 테이블이 너무 커서 랜덤 엑세스가 비효율적이라면 DBMS는 Hash Join을 채택할 수 있다.
- DBMS가 상대적으로 작은 테이블에서 조건을 탐색해 데이터를 추림
- 상대적으로 큰 테이블을 스캔해 각 값의 해시값이 존재하는지 확인
- 추출
MySQL의 JOIN 방식
이러한 Join 방식은 DBMS가 옵티마이저를 통해 알아서 정해준다. 이를 확인하기 위해 MySQL의 Workbench를 이용해 실행 계획을 확인할 수 있다.
SELECT *
FROM person p
JOIN programming_language pl ON pl.id=p.pl;
보다시피 인덱스가 걸려있는 programming_language 테이블을 선행테이블 삼아 Nested Loop 방식을 실행하는 것을 확인할 수 있다.
직관적으로 빨간색은 오래 걸리는 작업이고, 초록색은 빠른 작업이다. person 테이블에 오래 걸리는 작업인 Full Table Scan이 실행된 이유는 무엇일까? DBMS가 관계성을 모르기 때문이다. 비교를 위해서 person 테이블의 pl 속성에 인덱스를 추가하고 JOIN을 시도해보자.
CREATE INDEX person_programming_language_idx
ON person (pl);
이번엔 person 테이블에서 인덱스가 걸린 속성으로 테이블을 스캔해 비용이 감소된 것을 확인할 수 있다. 하지만 programming_language를 Join할 때 여전히 Full Table Scan을 실행한다. NL Join 방식은 위에서 언급했던 것처럼 선별된 키를 가지고 후행테이블을 스캔하기 때문에 후행테이블의 인덱스 유무에 크게 영향받지 않는다. 다만, 테이블들의 인덱스와 크기에 따라 위와 같이 선행테이블을 조정할 수 있으며 예상되는 실행 계획을 변경해 비용을 줄일 수 있다.
인덱스가 걸려있지 않는 속성을 기준으로 Join을 시도하면 어떨까? 이를 확인해보고자 별도의 테이블을 만들어 실행해보았다.
CREATE TABLE test (
id INT PRIMARY KEY AUTO_INCREMENT,
pl_name VARCHAR(30),
person_id INT
);
INSERT INTO test (pl_name, person_id) VALUES ("Python", 6);
INSERT INTO test (pl_name, person_id) VALUES ("Python", 4);
INSERT INTO test (pl_name, person_id) VALUES ("Python", 17);
인덱스를 걸지 않기 위해 programming_language의 name 속성을 기준으로 Join을 시도해보고자 했다.
SELECT *
FROM programming_language pl
JOIN test t ON t.pl_name=pl.name
JOIN person p on p.pl=pl.id;
위와 같은 SQL문을 실행하면 실행 계획을 다음과 같이 확인할 수 있다.
programming_language와 test 테이블을 스캔해 Hash 방식으로 조인한 테이블을 만든다. 이를 후행테이블로 두고, person의 id를 기반으로 선행테이블을 만들어 두 테이블의 NL Join을 실행하는 것을 확인할 수 있다. 만약 JOIN의 순서를 바꾼다면 어떻게 될까?
SELECT *
FROM programming_language pl
JOIN person p on p.pl=pl.id
JOIN test t ON t.pl_name=pl.name;
실행 계획이 완전히 같은 것을 확인할 수 있다.