from Karte,

[프로그래머스 코딩테스트 연습 SQL] #3. 차집합 본문

study log/SQL

[프로그래머스 코딩테스트 연습 SQL] #3. 차집합

karte 2020. 10. 1. 23:44

* SQL 아마추어가 작성한 내용이라 오류가 있을 수 있습니다. 이외에도 오타, 저작권 문제 등 문제가 있을 시 댓글로 알려주세요.

 바로 수정하겠습니다. 

* 유/무료 서비스 이름이 등장합니다만, 광고 또는 홍보 목적이 아니며 금전적 지원을 받은 사실이 없음을 밝힙니다.

* 깊은 개념적 이해보다 문제의 예시 답안 코드를 이해하기 위한 수준의 내용을 다루는 점 유의 바랍니다. 

* 본 포스팅 작성 시 참고한 소스는 페이지 가장 하단의 References 에 하이퍼링크를 이용하여 명시했습니다. 

 

 

또 다시 돌아온 프로그래머스 SQL 코딩테스트 문제를 풀이! 2020 빅콘 제출이 끝나고 기념으로 몇 문제 풀었더니 어느새 남은 문제가 거의 없어져 있더라는...그래서 앞으로 프로그래머스 SQL 코테 연습문제는 2~3개 정도 더 올라오고 leetcode 사이트를 참고할까 생각 중이다. 

 

각설하고, 오늘의 문제는 < 없어진 기록 찾기 > 이다. 

 


세 번째 정리 차집합

문제: 코딩테스트 연습 - 모든 문제 - MySQL 필터링 적용 - 없어진 기록 찾기

본문 링크 : programmers.co.kr/learn/courses/30/lessons/59042#qna

 

아래와 같이 보호소에 들어온 동물의 정보를 담은 ANIMAL_INS 테이블과 보호소에서 입양 보낸 동물의 정보를 담은  ANIMAL_OUTS 테이블이 존재한다. 

 

  • ANIMAL_INS
NAME TYPE NULLABLE
ANIMAL_ID VARCHAR (N) FALSE
ANIMAL_TYPE VARCHAR (N) FALSE
DATETIME DATETIME FALSE
INTAKE_CONDITION VARCHAR (N) FALSE
NAME VARCHAR (N) TRUE
SEX_UPON_INTAKE VARCHAR (N) FALSE

 

* ANIMAL_ID : 동물의 아이디

* ANIMAL_TYPE : 생물 종 ( e.g. Dog, Cat, etc )

* DATETIME : 보호 시작일 ( 년-월-일-시-분-초 단위까지 기록 )

* INTAKE_CONDITION : 보호 시작 시  상태 ( e.g. Aged, Sick, Normal, etc )

* NAME : 이름

* SEX_UPON_INTAKE : 보호 시작 시 성별 및 중성화 여부 ( e.g. Neutered Male, etc )

 

  • ANIMAL_OUTS
NAME TYPE NULLABLE
ANIMAL_ID VARCHAR (N) FALSE
ANIMAL_TYPE VARCHAR (N) FALSE
DATETIME DATETIME FALSE
NAME VARCHAR (N) TRUE
SEX_UPON_OUTCOME VARCHAR (N) FALSE

 

* ANIMAL_ID : ANIMAL_INS 의 ANIMAL_ID 필드를 외래 키로 사용

* DATETIME : 입양 일시 ( 년-월-일-시-분-초 단위까지 기록 )

* SEX_UPON_OUTCOME : 입양 시 성별 및 중성화 여부

* 다른 컬럼은 ANIMAL_INS 와 설명 동일

 

이 때, 천재지변이 발생하여 데이터 일부가 유실되었다. 입양을 간 기록은 있지만 보호소에 들어온 기록이 없는 동물의 ID 와 이름은 ID 순으로 조회할 수 있도록 하는 SQL 문을 작성해야 한다. 

 

즉, 나는 이 문제를 풀 때 두 테이블을 두 개의 집합으로, 각 테이블의 레코드를 각 집합에 속하는 원소로 생각하고 ANIMAL_OUTS 집합을 기준으로 해당 집합에 있는 원소들 중 ANIMAL_INS 집합에는 없는 것들만을 결과로 반환할 수 있도록 했다. 

 


문제는 그렇게 어렵지 않지만 이 문제에 대한 풀이를 포스팅 하기로 결심한 이유는 2가지 방식으로 접근이 가능하기 때문이다!

 

첫 번째 방식. 서브쿼리 와 WHERE 이용

 

먼저 코드를 공유하자면 다음과 같다. 

 

SELECT ANIMAL_ID, NAME # 조회할 필드 2개 선택 ( ANIMAL_ID, NAME)
FROM ANIMAL_OUTS
WHERE ANIMAL_ID NOT IN (SELECT ANIMAL_ID # 서브쿼리: ANIMAL_INS 테이블에서 ANIMAL_ID 만 반환
                       FROM ANIMAL_INS);

 

ANIMAL_OUTS 테이블에서 레코드를 결과 창에 표시할 때 ANIMAL_ID 와 NAME 필드만 보이도록 할 건데 (SELECT ... FROM ... 까지), 이 때 ANIMAL_ID 가 ANIMAL_INS 테이블에 없는 경우에만 반환하라는 조건 (WHERE ... ; 까지) 을 걸어준 것이다. 

 

다시 정리를 위해 코드의 로직을 표현해 보면 다음의 벤 다이어그램과 같다.  빗금친 영역이 결과로서 반환되어야 할 영역이다. 

 

출처: 포스팅 작성자 ( Karte ) 본인

 

 

 

 

두 번째 방식. LEFT OUTER JOIN 연산자 이용

 

사실 이 방법으로 푸는 것이 출제자의 의도였던 것 같다. 먼저 잠시 LEFT OUTER JOIN 을 되새김 해보기로 한다. 

 

출처: https://www.w3schools.com/sql/sql_join_left.asp

SQL 을 비롯한 여러 프로그래밍 언어 학습 사이트로 유명한 노르웨이의 w3schools.com 에서 가져온 LEFT JOIN 실행 결과 이미지이다. 

 

이미지 내에 써 있듯이, 어떤 쿼리 언어에서는 LEFT JOIN 을 LEFT OUTER 조인으로 부른다고 한다. 결국 둘의 차이가 없다는 얘기인데, 혹시나 해서 한번 더 찾아보았더니 geeksforgeeks 라는 유명 사이트에서도 같은 말을 하고 있다. 그래서 나는 LEFT OUTER JOIN 이라고 하면 LEFT JOIN 보다 더 확실하게 실행 결과의 이미지 전달이 잘 되는 것으로 이해했다. 참고로 MySQL 에서는 두 연산자 모두 사용 가능하다. 

 

따라서 LEFT (OUTER) JOIN 을 활용해 쿼리문을 구성하면 이렇게 된다.

 

SELECT OUTS.ANIMAL_ID, OUTS.NAME
FROM ANIMAL_OUTS OUTS
LEFT JOIN ANIMAL_INS INS ON INS.ANIMAL_ID = OUTS.ANIMAL_ID
WHERE INS.ANIMAL_ID IS NULL;

 

위의 w3schools.com 의 벤 다이어그램에서 볼 수 있듯이 LEFT JOIN 을 하면 좌측 테이블 ( 여기선 ANIMAL_OUTS )의 모든 레코드가 반환되는데, 문제에서 원하는 것은 좌측 테이블에서 우측 테이블 ( = ANIMAL_INS ) 과의 교집합을 뺀 ANIMAL_OUTS - ANIMAL_INS 의 결과이므로 WHERE 문을 통해 조건을 걸어 ANIMAL_INS 에서 온 ANIMAL_ID 필드가 비어있는 레코드는 걸러주었다. 

 

 

마지막으로 첫 번째 방식. 서브쿼리 와 WHERE 이용 두 번째 방식. LEFT OUTER JOIN 연산자 이용 중 아무 것이나 마음에 드는 걸로 써도 될지, 두 방법은 어떤 차이가 있는지 간단하게 알아보고 포스팅을 마무리하려고 한다. 

 

어떤 작업을 수행하려고 하는지에 따라 차이가 있겠지만, 적어도 이 포스팅에 등장한 문제를 풀 때는 JOIN 을 사용하는 편이 낫다고 한다. 이유는 쿼리 수행 시 내부적으로 수행되는 쿼리 최적화 과정 때문인 듯 한데, 서브쿼리보다 JOIN 문을 이용할 때의 속도가 더 빠르다고 한다. 따라서 데이터 건수가 적으면 둘 중 하나를 택해도 실행 속도에 큰 차이는 없겠지만, 몇 십 ~ 몇 백만건의 데이터를 다뤄야 하는 상황에서는 가능하면 JOIN 으로 처리하기를 권장한다고 한다. ( 이 내용을 작성할 때 참고한 글의 주소는 아래 References 란에 기재했으니 참고 바랍니다. )

 

 

 

 

References

MySQL where in (서브쿼리) vs 조인 조회 성능 비교 (5.5 vs 5.6) : jojoldu.tistory.com/520

 

 

 

 

 

 

 

 

 

Comments