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

Data Base/Data Base 기초

[DB] SQL Select 문 (Subquery)

재안안 2022. 7. 11. 17:43

이전 포스트에서 Select의 기초에 대해 알아보았다.

이번에는 Subquery에 대해 알아보겠다.

 

Subquery란?

간단하게 말하자면 쿼리 내부에 쿼리가 있는 것이다.

내부 쿼리의 반환 값을 이용하여 외부에서 Select를 한번 더 사용한다고 생각하면 좋겠다.

조건을 두번이나 걸었으니 해당 쿼리는 더 유동적일 수도 있고 더 세세한 값을 조회할 수도 있겠다.

 

물론 단순히 튜플에대해 조건을 여러번 걸려면 WHERE 절에서 AND를 사용해서 조건을 두개 걸어주면 된다.

SELECT * FROM user WHERE name like "jaean" AND usercode = 14;

이름이 jaean이며 usercode가 14라는 조건을 만족하는 튜플만 반환된다.

 

Subquery는 쿼리 어디든 들어갈 수 있다.

반환값을 잘 사용하면 된다.

 

바로 사용해보며 알아보겠다.

 

이번 예제에서 사용할 테이블을 총 3개이다.

 

  • user_mst
  • user_dtl
  • authrorities

 

모두 user에 대한 정보들을 세분화해서 나눠 가지고 있는 것이다.

 

user_mst

 

user_mst는 user_id, 로그인 아이디, 이메일, 비밀번호, 이름, 제공자, 가입시기, 마지막 로그인시기를 저장한다.

 

user_dtl

 

user_mst는 user_id, 프로필 이미지 링크, 주소, 연락처, 성별, 가입시기, 마지막 수정 시간을 저장한다.

 

authorities

 

authorities는 로그인 아이디, 권한, 생성시간, 수정시간을 저장한다.

 

단순히 user 테이블 하나에

모두 다 담을 순 있지만 그렇게 하면 user 테이블이 너무 커지기 때문에 세분화해서 담고있다.

 

어차피 user_mst에 trigger를 걸어놔서 user_mst가 생성될 때 user_dtl과 authroities에도 자동적으로

새로 생성된 user_mst에 대한 튜플이 생성되어 하나의 테이블처럼 작동한다. 

 

이제 이 3개의 테이블을 통해 Subquery를 사용해 보겠다.

 

사용자가 로그인 할 때 사용자가 서비스를 이용할 때 필요할 모든 정보들을 넘겨줄 것이다.

이때 tt라는 사용자가 있다고 가정한다.

 

FROM

 

Subquery 1 예제

우선 user_mst에서 tt라는 사용자 이름을 가지고있는 튜플을 반환 받고 그 반환된 튜플에 um이라는 이름을 붙인다.

 

그런데 정확하게 말하자면 (select . . . like "tt")는 하나의 튜플이 아니라 1행짜리 테이블이 반환되는 것이다.

반환된 테이블에 um이라는 이름을 붙이고 해당 테이블의 컬럼값들을 사용하는 것이다.

 

여기서 중요한 것은 Subquery는 내부쿼리를 지칭하는데

내부쿼리의 반환 값을 사용해서 새로운 쿼리를 실행한다는 개념을 이해하는 것이 중요하다.

 

해당 예제에서 Subquery는 FROM절 안에 들어있다.

 

Subquery 1 실행결과

 

테스트를 한다고 *를 사용했는데 너무 많은 컬럼들이 반환되었다.

너무 길어서 캡쳐조차 할 수 없었다.

 

아래는 반환된 컬럼의 데이터를 json형태로 변환시켜서 복사한 것이다.

 

{
"table": "알 수 없는 테이블",
"rows":
[
{
"user_id": 2,
"username": "tt",
"email": "jaean1999@naver.com",
"password": "1234",
"name": "test2",
"provider": null,
"enrol_date": "2022-07-09 05:39:56",
"last_access": "2022-07-09 05:39:56",
"user_id": 2,
"user_profile_img": null,
"user_address": null,
"user_phone": null,
"user_gender": null,
"enrol_date": "2022-07-09 05:39:56",
"last_access": "2022-07-09 05:39:56",
"username": "tt",
"authority": "ROLE_USER",
"create_date": "2022-07-09 05:39:56",
"update_date": "2022-07-09 05:39:56"
}
]
}

 

반환된 데이터를 보여주기 위해 변환 시킨 것이다.

 

알 수 없는 테이블은 최종 쿼리에서 반환 테이블의 이름을 지정해주지 않아서 그런다.

 

너무 김으로 이번 예제에선 가독성을 위해 몇개의 컬럼들만 선택하겠다.

 

Subquery 2 예제

 

이제 가독성을 위해 이처럼 3개의 컬럼만 선택하겠다. 1 테이블당 1개의 컬럼만 가져온다.

 

Subquery 2 실행결과

 

 

WHERE

 

Subquery 3 예제

 

만들면서 느꼇는데 테이블 구조를 잘못 짠거 같다. (다른 문법을 사용하면 이렇게 어렵진 않다)

Subquery를 사용하기에 최적의 구조는 아닌거 같다.

 

왜냐하면 서브쿼리를 사용할 때 주의해야할 것이 있기 때문이다.

 

nested query는 내부의 Select문 개수만큼 서버에 select 요청해서

쿼리에서 select의 개수는 최소한으로 만들어야 한다고 알고 있다.

 

쿼리를 변수에 담아서 활용할 수도 있지만 지금은 크게 의미가 없는거 같다.

 

실행 결과는 아래와 같다.

 

Subquery 3 실행결과

 

해당 예제에서 보여주고 싶었던 것은 WHERE 절에서도 Subquery를 사용할 수 있는 것이고

WHERE 절에서 Subquery 사용시 함께 사용할 수 있는 키워드들이 있다는 것이였다..

 

함께 사용하면 좋은 키워드는 아래와 같다.

  • IN
  • ANY
  • ALL

 

쉽게 말하자면 IN은 왼쪽 값이 오른쪽 배열에 존재하면 true이다.

 

이때 왼쪽 값은 target 비교값이고 오른쪽은 컬럼들이다. 

 

IN

 

Subquery 4 예제

 

7월10일 이후로 가입한 사용자들의 username과 enrol_date를 반환하는 쿼리이다.

 

enrol_date > '2022-07-10'

 

SELECT user_id FROM user_dtl WHERE enrole_date > '2022-07-10'은

enrol_date가 2022-07-10보다 큰 값을 갖고있는 사용자들의 user_id로 이루어진 컬럼들을 반환한다.

 

Subquery 4 실행결과

 

jaean, test, jaeano가 2022년 7월 10일 이후의 가입자들이다.

이때 IN이 아닌 NOT IN을 사용하면 이전 가입자들이 나오긴한다.

 

ANY

 

ANY는 오른쪽의 배열 요소중 비교값이 하나라도 같다면 true이다. (OR)

주의사항으로 ANY를 사용할 때는 연산자와 같이 사용해야한는 것이다.

 

Subquery 5 예제
Subquery 5 실행결과

 

이렇게만 보면 IN과 차이는 없지만 연산자를 <, >, <=. >=, <> 등으로 바꾸어 더 세세한 조건을 줄 수 있다.

 

 

ALL

 

ALL은 오른쪽의 모든 배열요소에 비교값이 모두 만족해야 true이다. (AND)

 

Subquery 6 예제
Subquery 6 실행결과

 

Subquery를 통해 naver.com이 들어가는 사용자의 user_id를 찾았는데 all을 여기서 not in처럼 사용했다.

좋은 코드라고 보긴 힘들다.

 

그래도 결과적으로는 naver.com이 아닌 메일을 갖고있는 사용자의 username과 email이 반환되었다.

 

사실 본인은 Subquery를 그닥 선호하지 않는 편이다.

요청 관련된 이슈도 있다고 알고있고 (여러번 요청하는 것)

 

서브쿼리를 사용하면 쿼리가 자꾸 길어진다.

 

다음에는 join에 대해 알아볼 것인데

 

join을 사용하면 왠만한 모든 subquery를 대체할 수 있다.

그리고 심지어 select도 한번만 날리기 때문에 성능상 이점도 있다.

 

그래도 Subquery를 사용할 수는 있어야한다고 생각한다.