시작 오늘 분석할 코드
EXISTS는 서브쿼리 결과의 실제 값을 가져오는 것이 아니라, 조건을 만족하는 행이 하나라도 존재하는지를 검사한다.
SELECT U.userID, U.name, U.addr FROM usertbl U WHERE EXISTS ( SELECT * FROM buytbl B WHERE U.userID = B.userID );
실제 결과 미리보기
| userID |
name |
prodName |
addr |
해석 |
| JYP |
조용필 |
운동화 |
경기 |
구매 행과 회원 행이 userID로 연결됨 |
| BBK |
바비킴 |
노트북 |
서울 |
조인 조건을 만족하는 행만 결과에 표시됨 |
용어 용어 정리
JOIN두 테이블의 행을 연결해서 하나의 결과처럼 조회하는 구문
INNER JOIN양쪽 테이블에 매칭되는 행이 모두 있을 때만 결과에 포함하는 조인
ON두 테이블을 어떤 조건으로 연결할지 적는 조인 조건 절
WHERE조인된 결과 중 필요한 행만 다시 걸러내는 필터 절
별칭(alias)긴 테이블명을 B, U처럼 짧게 줄여 쓰는 이름
기준 테이블FROM 뒤에 먼저 적어 조인의 출발점처럼 읽는 테이블
외래키다른 테이블의 주요 키를 참조해 관계를 만드는 컬럼
카티션 곱조인 조건 없이 모든 행 조합이 만들어지는 위험한 결과
흐름 JOIN을 읽는 순서
FROM 기준 테이블
→
JOIN 대상 테이블
→
ON 연결 조건
→
WHERE 결과 필터
SQL은 작성 순서와 실제 실행 계획이 항상 같지는 않지만, 학습 단계에서는 이 순서대로 읽으면 조인 의도를 파악하기 쉽다.
1 EXISTS의 핵심 질문
이 회원과 연결되는 구매 행이 buytbl에 하나라도 있는가를 묻는다. 있으면 TRUE, 없으면 FALSE처럼 동작한다.
조인 쿼리는 "무엇을 연결하는가"와 "연결된 결과 중 무엇을 볼 것인가"를 분리해서 읽으면 훨씬 단순해진다.
2 상관 서브쿼리 구조
서브쿼리 안에서 바깥 쿼리의 U.userID를 참조한다. 그래서 회원 한 명마다 buytbl에 대응 행이 있는지 검사한다.
조인 쿼리는 "무엇을 연결하는가"와 "연결된 결과 중 무엇을 볼 것인가"를 분리해서 읽으면 훨씬 단순해진다.
3 SELECT *의 의미
EXISTS에서는 어떤 컬럼을 SELECT하는지가 중요하지 않다. 행이 존재하는지만 보므로 SELECT 1로 써도 의도는 같다.
조인 쿼리는 "무엇을 연결하는가"와 "연결된 결과 중 무엇을 볼 것인가"를 분리해서 읽으면 훨씬 단순해진다.
4 JOIN과 다른 사용감
JOIN은 연결된 행을 펼쳐 보여주고, EXISTS는 존재 여부로 바깥 행을 남길지 말지만 결정한다.
조인 쿼리는 "무엇을 연결하는가"와 "연결된 결과 중 무엇을 볼 것인가"를 분리해서 읽으면 훨씬 단순해진다.
5 표준 JOIN과 구식 JOIN 비교
표준 JOIN
FROM A INNER JOIN B ON A.id = B.id처럼 연결 조건이 ON에 분리된다. 실무와 학습 모두 이 방식을 우선 사용한다.
구식 JOIN
FROM A, B WHERE A.id = B.id처럼 WHERE에 연결 조건을 섞는다. 동작은 가능하지만 실수 위험이 크다.
6 컬럼명을 명확히 적어야 하는 이유
두 테이블에 같은 이름의 컬럼이 있으면 MySQL은 어떤 컬럼을 뜻하는지 모호하다고 판단할 수 있다. 그래서 조인 쿼리에서는 buytbl.userID, usertbl.name처럼 테이블명 또는 별칭을 붙이는 습관이 중요하다.
SELECT B.userID, U.name, B.prodName FROM buytbl B INNER JOIN usertbl U ON B.userID = U.userID;
7 ON과 WHERE를 섞지 않는 습관
조인 조건은 ON에, 검색 조건은 WHERE에 둔다. 이렇게 나누면 쿼리를 나중에 다시 읽을 때 "관계 정의"와 "결과 필터"가 분명하게 보인다.
| 절 |
역할 |
예시 |
| ON |
테이블 사이 연결 조건 |
B.userID = U.userID |
| WHERE |
연결된 결과 필터링 |
B.userID = 'JYP' |
| ORDER BY |
출력 순서 지정 |
ORDER BY B.num |
| SELECT |
보여줄 컬럼 지정 |
B.prodName, U.addr |
8 실무 체크 순서
1
각 테이블 단독 조회조인 전에 buytbl과 usertbl의 주요 컬럼을 먼저 확인한다.
2
연결 키 확인두 테이블에서 같은 의미를 갖는 컬럼이 무엇인지 확인한다.
3
ON 조건 작성관계 조건은 ON에 먼저 고정한다.
4
WHERE 필터 추가JYP처럼 특정 조건은 조인이 된 뒤 마지막에 붙인다.
9 자주 하는 실수
| 실수 |
증상 |
해결 |
| ON 조건 누락 |
행 수가 폭발적으로 늘어난다 |
두 테이블의 연결 키를 ON에 반드시 적는다 |
| 같은 컬럼명을 그냥 씀 |
ambiguous column 오류가 난다 |
테이블명 또는 별칭을 붙인다 |
| WHERE에 조인 조건과 필터를 모두 섞음 |
쿼리 의도가 흐려진다 |
조인 조건은 ON, 필터는 WHERE로 분리한다 |
| SELECT * 남용 |
중복 컬럼이 많아지고 결과가 무거워진다 |
필요한 컬럼만 명시한다 |
| ORDER BY 생략 |
출력 순서가 기대와 달라진다 |
정렬 기준 컬럼을 명시한다 |
10 JOIN 선택 기준
두 테이블에 모두 존재하는 데이터만 보고 싶다면 INNER JOIN이 적합하다. 한쪽 테이블의 모든 행을 유지하면서 매칭되는 정보만 붙이고 싶다면 다음 단계에서 LEFT OUTER JOIN을 검토한다.
이번 폴더의 예제는 INNER JOIN과 EXISTS 중심이다. 공통점은 "관계가 있는 행만 결과로 남긴다"는 점이고, 차이는 JOIN은 행을 펼치고 EXISTS는 존재 여부만 검사한다는 점이다.
11 디버깅 체크 — 결과 행 수가 이상할 때
JOIN에서 가장 먼저 확인할 것은 결과 행 수다. 예상보다 행이 너무 많으면 조인 조건이 빠졌거나 중복 키가 있는 경우가 많고, 예상보다 너무 적으면 INNER JOIN이 매칭되지 않는 행을 제외했을 가능성이 높다.
| 증상 |
가능한 원인 |
확인 쿼리 |
| 행 수가 갑자기 많다 |
ON 조건 누락 또는 연결 키 중복 |
COUNT(*)와 GROUP BY 연결키 확인 |
| 행 수가 너무 적다 |
INNER JOIN 때문에 매칭 없는 행 제외 |
LEFT JOIN으로 바꿔 누락 행 확인 |
| 특정 회원이 보이지 않는다 |
구매 기록 또는 회원 정보 중 한쪽이 없음 |
각 테이블을 userID로 단독 조회 |
| 컬럼 값이 헷갈린다 |
같은 이름의 컬럼을 별칭 없이 조회 |
테이블 별칭과 출력 alias 명시 |
처음에는 SELECT *로 구조를 보고, 쿼리가 안정되면 SELECT 목록을 필요한 컬럼으로 줄이는 순서가 좋다.
12 작은 테스트 데이터로 검증하기
조인이 헷갈릴 때는 실제 운영 데이터보다 2~3행짜리 작은 데이터로 먼저 검증하는 편이 빠르다. 한 회원은 구매 있음, 한 회원은 구매 없음, 한 구매는 회원 없음처럼 경계 사례를 만들어 보면 INNER JOIN의 제외 규칙이 분명해진다.
-- 테스트 관점 -- 1. usertbl에는 있고 buytbl에는 없는 userID -- 2. buytbl에는 있고 usertbl에는 없는 userID -- 3. 양쪽 모두에 있는 userID -- 위 세 경우를 만든 뒤 INNER JOIN 결과에 3번만 남는지 확인한다.
1
기준 테이블 행 수 확인조인 전 각 테이블의 행 수와 연결 키 분포를 확인한다.
2
조인 조건만 먼저 실행WHERE 필터를 붙이기 전에 ON 조건만으로 결과를 확인한다.
3
필터를 하나씩 추가조건을 한 번에 많이 붙이지 말고 WHERE, ORDER BY 순서로 더한다.
4
최종 컬럼 정리중복 컬럼과 불필요한 컬럼을 제거하고 alias를 붙인다.
13 실무에서 읽기 좋은 JOIN 작성 규칙
JOIN 쿼리는 길어지기 쉽기 때문에 줄바꿈 규칙을 정해 두면 유지보수가 쉬워진다. SELECT 컬럼은 한 줄에 너무 많이 몰아넣지 않고, JOIN과 ON은 한 단계 들여쓰기로 붙여 읽는다.
SELECT B.userID, U.name, B.prodName, U.addr FROM buytbl B INNER JOIN usertbl U ON B.userID = U.userID WHERE B.userID = 'JYP' ORDER BY B.num;
테이블 별칭을 붙인 뒤 원래 테이블명과 별칭을 섞어 쓰면 쿼리가 지저분해진다. 한 쿼리 안에서는 별칭 사용 방식을 일관되게 유지한다.
14 최종 점검 메모
JOIN 쿼리를 완성한 뒤에는 결과가 "나온다"에서 멈추지 말고, 행 수와 합계가 원본 의도와 맞는지 확인해야 한다. 특히 INNER JOIN은 매칭되지 않는 행을 조용히 제외하므로 누락 여부 확인이 중요하다.
-- 조인 전후 행 수 비교 SELECT COUNT(*) FROM buytbl; SELECT COUNT(*) FROM usertbl; -- 조인 후 결과 행 수 확인 SELECT COUNT(*) FROM buytbl B INNER JOIN usertbl U ON B.userID = U.userID;
행 수가 예상과 다르면 쿼리를 더 복잡하게 만들기 전에 ON 조건과 기준 테이블부터 다시 확인한다.
핵심 요약
JOIN두 테이블을 관계 조건으로 연결해 하나의 결과처럼 조회한다.
ON테이블 사이의 연결 조건을 적는 자리다.
WHERE조인된 결과 중 필요한 행만 걸러내는 자리다.
별칭긴 테이블명을 줄이고 컬럼 소속을 명확하게 만든다.
EXISTS연결된 행이 존재하는지만 검사해 바깥 행을 남길지 결정한다.
Tags
#MySQL #JOIN #INNER JOIN #ON #WHERE #GROUP BY #SQL #데이터베이스 #DB #테이블조인 #쿼리 #웹개발 #티스토리
티스토리 태그 입력란 복사용
MySQL, JOIN, INNER JOIN, ON, WHERE, GROUP BY, SQL, 데이터베이스, DB, 테이블조인, 쿼리, 웹개발, 티스토리
댓글