╱╱╭╮╱╱╱╱╱╱╭━━━╮╱╱╱╭╮╱╭╮╱╱╱╱╱╱ ╱╱┃┃╱╱╱╱╱╱┃╭━╮┃╱╱╱┃┃╱┃┃╱╱╱╱╱╱ ╱╱┃┣━━┳━━╮┃┃╱┃┣━╮╱┃╰━╯┣━━┳━╮╱ ╭╮┃┃╭╮┃┃━┫┃╰━╯┃╭╮╮┃╭━╮┃╭╮┃╭╮╮ ┃╰╯┃╭╮┃┃━┫┃╭━╮┃┃┃┃┃┃╱┃┃╭╮┃┃┃┃ ╰━━┻╯╰┻━━╯╰╯╱╰┻╯╰╯╰╯╱╰┻╯╰┻╯╰╯

Data Base/Data Base 기초

[DB] Join 문 (Inner Join, Cross Join, Using)

재안안 2022. 7. 12. 10:56

이번에는 SQL의 join문에 대해서 알아보겠다.

 

Join이란?

두 개 이상의 테이블에서 데이터를 조회하는 것이다.

Join을 사용하면 테이블을 합쳐야하는 부분을 FROM절에 사용해 가독성이 높아진다.

 

Select 문의 사용법은 다음과 같다.

 

키워드 입력값

SELECT columns FROM table1 JOIN table2 ON join_condition;

SELECT columns FROM table1 JOIN table2 ON join_condition WHERE tuple_condition;

 

바로 예제를 통해 사용해 볼것인데

이때 저번에 Subquery를 알아보기위해 사용했던 테이블들을 그대로 사용할 것이다.

 

사용 할 테이블

 

user_mst

 

user_dtl

 

authorities

 

우선 해당 테이블을 통해 Join을 간단하게 사용해 보겠다.

 

JOIN 예제 1

 

Join 예제 1

 

우선 간단하게 user_mst 테이블과 user_dtl 테이블을 join 시켜서 서로 같은 user_id를 갖고 있는 튜플들만을 반환시켰다.

아래의 ODER BY는 um.user_id를 기준으로 튜플들을 정렬시켜준다. 보기 쉬우라고 기입한 것이다. 

 

FROM절에서 Join을 사용하였다.

 

user_mst um JOIN user_dtl ud ON um.user_id = ud.user_id

 

user_mst 테이블을 um이라고 임시로 지칭

user_dtl 테이블을 ud라고 임시로 지칭

 

um과 ud를 합치는데 이때 um.user_id와 ud.user_id가 같은 값을 갖고있는 튜플들만 (join conditon을 만족하는 튜플들만)

반환되는 것이다.

 

아래는 위의 쿼리의 실행 결과이다. 너무 길어서 화면으로도 다 보여지지가 않는다.

 

Join 예제 1 실행결과

 

결과를 보면 15개의 columns를 갖는 5개의 튜플이 반환되었다.

그래도 결과는 정확하게 확인해야하니 JSON으로 변환해서 가져왔다.

 

아래의 JSON에서는 각각의 튜플마다 user_id의 pair가 같다는 것을 확인 할 수 있다.

{
	"table": "알 수 없는 테이블",
	"rows":
	[
		{
			"user_id": 1, // user_mst
			"username": "plz",
			"email": "jaean1@naver.com",
			"password": "1234",
			"name": "pleaseWork",
			"provider": null,
			"enrol_date": "2022-07-09 03:40:14",
			"last_access": "2022-07-09 03:40:14",
			"user_id": 1, // user_dtl
			"user_profile_img": null,
			"user_address": null,
			"user_phone": "010-0000-1111",
			"user_gender": null,
			"enrol_date": "2022-07-09 03:40:14",
			"last_access": "2022-07-09 03:40:14"
		},
        
 			. . . (생략)
		
        {
			"user_id": 5, // user_mst
			"username": "jaeano",
			"email": "jaean1999@gmail.com",
			"password": "1234",
			"name": "jaeanEmailTest",
			"provider": null,
			"enrol_date": "2022-07-11 03:46:21",
			"last_access": "2022-07-11 03:46:21",
			"user_id": 5, // user_dtl
			"user_profile_img": null,
			"user_address": null,
			"user_phone": "010-2222-1111",
			"user_gender": null,
			"enrol_date": "2022-07-11 03:46:21",
			"last_access": "2022-07-11 03:46:21"
		}
	]
}

 

 

이제부터는 컬럼을 조금만 선택하겠다.

 

위의 예제는 단순히 join만 사용한 에제이고 이제는 WHERE 절에 조건을 걸어보겠다.

 

JOIN 예제 2

 

Join 2 예제

 

위와 같은 테이블에서 WHERE 절에 username = 'jaean'을 주었다.

 

실행 결과는 아래와 같다.

 

Join 2 예제 실행결과


아직까지 특별한 것은 없다.

 

그런데 이때 USING을 사용하면 쿼리가 훨씬 깔끔해 보일 것이다.

 

Join 3 예제

 

Join 3 예제

 

만약에 튜플이 같은 컬럼명을 갖고있다면, 그리고 그 컬럼의 값이 같다면 join시키는 것이다.

um.user_id = ud.user_id를 USING 하나로 대체한 것이다.

주의사항으로 USING 옆에 괄호를 붙여야한다.

 

*을 안쓸거라고 했는데 써버렸다.

 

그 이유는 USING을 사용할 때 꼭 알고 있어야하는 점 때문이다. 

아래의 실행결과를 통해 설명하겠다.

 

Join 3 예제 실행결과

 

실행결과를 보면 반환된 튜플의 컬럼이 14 개이다.

그냥 um.user_id = ud.user_id를 사용했을 때는 반환된 튜플의 컬럼이 분명 15개였다.

 

JSON 데이터로 확인하면 ud.user_id 컬럼이 없어진 것을 알 수 있다.

 

{
	"table": "알 수 없는 테이블",
	"rows":
	[
		{
			"user_id": 3,
			"username": "jaean",
			"email": "jaean3@naver.com",
			"password": "1234",
			"name": "jaean",
			"provider": null,
			"enrol_date": "2022-07-10 21:08:33",
			"last_access": "2022-07-10 21:08:33",
			"user_profile_img": null,
			"user_address": null,
			"user_phone": "010-1111-1111",
			"user_gender": null,
			"enrol_date": "2022-07-10 21:08:33",
			"last_access": "2022-07-10 21:08:33"
		}
	]
}

 

USING을 사용하면 테이블이 합쳐지기도 하지만

USINGconditon으로 사용되었던 컬럼은 합쳐져서 맨 앞으로 오게된다.

 

예제를 통해 보여주겠다.

 

Join 4 예제

 

Join 4 예제

 

um과 ud를 합친 테이블을 또 auth와 합친것이다.

 

Join 4 실행결과

 

분명 user_id의 컬럼이 맨 앞이였는데 결과로는 username의 컬럼이 맨 앞으로 온 것을 알 수 있다.

 

JSON으로 확인하면 아래와 같이 나온다.

 

{
	"table": "알 수 없는 테이블",
	"rows":
	[
		{
			"username": "jaean",
			"user_id": 3,
			"email": "jaean3@naver.com",
			"password": "1234",
			"name": "jaean",
			"provider": null,
			"enrol_date": "2022-07-10 21:08:33",
			"last_access": "2022-07-10 21:08:33",
			"user_profile_img": null,
			"user_address": null,
			"user_phone": "010-1111-1111",
			"user_gender": null,
			"enrol_date": "2022-07-10 21:08:33",
			"last_access": "2022-07-10 21:08:33",
			"authority": "ROLE_USER",
			"create_date": "2022-07-10 21:08:33",
			"update_date": "2022-07-10 21:08:33"
		}
	]
}

 

 

지금까지 기본적인 Join의 사용법에 대해 알아보았다.

 

이제 지금까지 사용했던 Join에 대해 조금 더 깊게 알아보겠다.

 

Join에도 여러가지 종류가 있다.

  • Inner Join
  • Outer Join
  • Natural Join
  • Cross Join

 

지금까지 사용했던 Join은 사실 Inner Join이다.

 

왜냐하면 Join에서 INNER는 생략 할 수 있기 때문이다. 지금까지 사용했던 쿼리는 모두 INNER가 생략 되어있는 것이다.

 

Inner Join

 

Join 5 예제

 

선택한 컬럼이 달라서 직접적인 반환값은 다르지만 INNER 키워드를 직접 기입하고 실행해도

FROM절의 테이블은 전혀 달라지지 않는다.

 

Join을 사용할 때 특정 종류를 기입하지 않으면 해당 Join을 Inner Join으로 분류된다.

 

Join 5 실행결과

 

처음에 Join을 정의 할 때,

Join은 두 개 이상의 테이블에서 데이터를 조회할 때 사용하는 것이라고 했다.

 

Inner Join은 두 개 이상의 테이블을 통해 만들어진 수많은 튜플중 (Cross Join)

join conditon의 결과가 인 튜플만 반환한다.

 

쉽게말하자면 user_mst와 user_dtl가 갖고있는 각각의 모든 튜플에 대하여 pair가 생기는 것이다.

 

데이터베이스에는 현재 5명의 사용자가 가입되어있다.

 

사용자의 정보는 user_mst와 user_dtl에 나누어서 저장된다.

 

그말은 즉슨 현재 user_mst와 user_dtl은 각각 5개의 튜플을 갖고있다는 말이다.

 

이때 Cross Join이 무엇인지는 아직 모르지만

Join을 사용할 때 만들어지는 "수많은 튜플"이 뭔지 알아보기 위해 사용하겠다.

 

Cross Join

 

Cross Join
Cross Join 실행결과

실행결과를 통해 알 수 있는 것은 25개의 튜플이 반환되었다는 것이다.

이는 user_mst 튜플 개수 * user_dtl 튜플 개수 (5 * 5)이다.

 

Cross Join은 두 테이블의 튜플들로 만들 수 있는 모든 조합을 반환한다. (Cartesian Product)

 

이것 또한 예제로 보는 것이 더 빠르다.

 

더 쉽게 알아보기 위해 쿼리를 조금 바꿔서 실행해 보겠다.

 

Cross Join 2

 

Cross Join 2 예제

 

실행결과는 아래와 같다.

 

Cross Join 2 예제

 

총 5개의 튜플이 반환되었고 실행 결과가 이상한 거 같지만 정상적으로 잘 실행된 것이 맞다.

왜냐하면 WHERE 절에서 조건을 주었기 때문이다. 

 

데이터가 길긴 한데 이는 JSON으로 봐야지 바로 이해할 수 있다.

 

{
	"table": "알 수 없는 테이블",
	"rows":
	[
		{
			"user_id": 1, // user_mst
			"username": "plz",
			"email": "jaean1@naver.com",
			"password": "1234",
			"name": "pleaseWork",
			"provider": null,
			"enrol_date": "2022-07-09 03:40:14",
			"last_access": "2022-07-09 03:40:14",
			"user_id": 1, // user_dtl
			"user_profile_img": null,
			"user_address": null,
			"user_phone": "010-0000-1111",
			"user_gender": null,
			"enrol_date": "2022-07-09 03:40:14",
			"last_access": "2022-07-09 03:40:14"
		},
		{
			"user_id": 1, // user_mst
			"username": "plz",
			"email": "jaean1@naver.com",
			"password": "1234",
			"name": "pleaseWork",
			"provider": null,
			"enrol_date": "2022-07-09 03:40:14",
			"last_access": "2022-07-09 03:40:14",
			"user_id": 2, // user_dtl
			"user_profile_img": null,
			"user_address": null,
			"user_phone": "010-1111-0000",
			"user_gender": null,
			"enrol_date": "2022-07-09 05:39:56",
			"last_access": "2022-07-09 05:39:56"
		},
		{
			"user_id": 1,// user_mst
			"username": "plz",
			"email": "jaean1@naver.com",
			"password": "1234",
			"name": "pleaseWork",
			"provider": null,
			"enrol_date": "2022-07-09 03:40:14",
			"last_access": "2022-07-09 03:40:14",
			"user_id": 3, // user_dtl
			"user_profile_img": null,
			"user_address": null,
			"user_phone": "010-1111-1111",
			"user_gender": null,
			"enrol_date": "2022-07-10 21:08:33",
			"last_access": "2022-07-10 21:08:33"
		},
		{
			"user_id": 1, // user_mst
			"username": "plz",
			"email": "jaean1@naver.com",
			"password": "1234",
			"name": "pleaseWork",
			"provider": null,
			"enrol_date": "2022-07-09 03:40:14",
			"last_access": "2022-07-09 03:40:14",
			"user_id": 4, // user_dtl
			"user_profile_img": null,
			"user_address": null,
			"user_phone": "010-2222-0000",
			"user_gender": null,
			"enrol_date": "2022-07-10 21:36:56",
			"last_access": "2022-07-10 21:36:56"
		},
		{
			"user_id": 1, // user_mst
			"username": "plz",
			"email": "jaean1@naver.com",
			"password": "1234",
			"name": "pleaseWork",
			"provider": null,
			"enrol_date": "2022-07-09 03:40:14",
			"last_access": "2022-07-09 03:40:14",
			"user_id": 5, // user_dtl
			"user_profile_img": null,
			"user_address": null,
			"user_phone": "010-2222-1111",
			"user_gender": null,
			"enrol_date": "2022-07-11 03:46:21",
			"last_access": "2022-07-11 03:46:21"
		}
	]
}

 

Cross Join을 사용하면 모든 pair의 튜플이 반환된다.

이제는 두 테이블의 모든 튜플로 만들 수 있는 pair가 무슨 말인지 이해할 수 있을 것이다.

 

Join 예제 6

 

이때 Inner Join을 사용하면 위의 튜플 중 join condition을 만족하는 튜플들만 가져오는 것이다.

 

예를 들면 위의 JSON에서 um.user_id = ud.user_id를 만족하는 튜플은 하나 밖에 없다.

 

그래서 select * from user_mst um inner join user-dtl ud using(user_id) where um.user_id=1; 을 실행시키면

하나의 튜플만 반환되는 것이다.

 

Join 6 예제
Join 6 실행 결과

 

Equi Join

 

추가적으로 진짜 간단한 개념 하나만 더 알고 정리하겠다.

join conditon에서 = (equality comparison)을 사용하는 join을 Equi Join이라고 한다. (join conditoin에서도 >, < 등 사용 할 있다.)

 

정리

 

이제 아래와 같이 정리할 수 있다.

 

두 테이블로 만들 수 있는 모든 튜플에 대한 pair에서 (Cross Join)

join conditon의 결과로 true이 나오는 튜플들만 반환하는 것이 Inner Join이다.

 

이때 condition의 결과로 true가 나온다는 것이 중요하다.

왜냐하면 보통 조건식의 결과는 true or false로 명확하게 나뉘는 것이 보통인데

SQL에서는 조건식의 결과로 true, false, or unknown이 나오기 때문이다.

 

select * from A join B on A.column = B.column;

 

A 테이블의 column 값과 B 테이블의 column 값이 같아야한다.

 

그런데 이때 만약 어떠한 튜플에서 (A 테이블이든 B 테이블이든)

column의 값이 만약 null 이라면 join conditon의 결과는 false가 아닌 unknown이 나와버린다.

1 == null이 false가 아닌 것이 놀랍지만 unknown이라는 것을 알고있어야 한다. 그냥 값이 unknown이다.

 

어쨋든 true를 반환하는게 아니라서 Inner Join에는 포함되지 못한다.

 

그런데 나중에 알아볼 Outer Join에서 unknown이 사용될 것이다.