해당 글은 프로그래머스 SQL 고득점 Kit를 바탕으로 작성됐음을 밝힙니다.
코딩테스트 연습 | 프로그래머스 (programmers.co.kr) - https://programmers.co.kr/learn/challenges

SQL 고득점 Kit에는 SELECT, AGGREGATE(SUM, MAX, MIN), GROUP BY, IS NULL, JOIN, String, Date와 관련된 문제들로 구성되어 있습니다. LEVEL 2 문제만 골라풀다가 GROUP BY와 JOIN 문제들을 풀어나가는 중입니다. 드디어 1개 남았습니다. Keep Going! 다 풀었습니다~!
SELECT
# 문제명: 모듬 레코드 조회하기 # 모든 레코드 조회 SELECT * FROM ANIMAL_INS ORDER BY ANIMAL_ID # 문제명: 역순 정렬하기 # 역순(내림차순) 정렬 SELECT NAME, DATETIME FROM ANIMAL_INS ORDER BY ANIMAL_ID DESC # 문제명: 아픈 동물 찾기 # 특정 문자열을 가지는 컬럼 찾기 SELECT ANIMAL_ID, NAME FROM ANIMAL_INS WHERE INTAKE_CONDITION = "Sick" ORDER BY ANIMAL_ID # 문제명: 어린 동물 찾기 # 특정 문자열을 가지지 않는 컬럼 찾기 SELECT ANIMAL_ID, NAME FROM ANIMAL_INS WHERE INTAKE_CONDITION <> "Aged" # 문제명: 동물의 아이디와 이름 # 오름차순 정렬 SELECT ANIMAL_ID, NAME FROM ANIMAL_INS ORDER BY ANIMAL_ID # 문제명: 여러 기준으로 정렬하기 # 다중 정렬 조건 SELECT ANIMAL_ID, NAME, DATETIME FROM ANIMAL_INS ORDER BY NAME ASC, DATETIME DESC # 문제명: 상위 n개 레코드 # 정렬 후 순서에 따른 레코드 읽어오기 SELECT NAME FROM ANIMAL_INS ORDER BY DATETIME LIMIT 1
SUM, MAX, MIN
# 문제명: 최댓값 구하기 # MAX를 사용한 최댓값 SELECT MAX(DATETIME) AS '시간' FROM ANIMAL_INS # LIMIT, ORDER BY 사용한 최댓값 SELECT DATETIME AS 시간 FROM ANIMAL_INS ORDER BY DATETIME DESC LIMIT 1 # 문제명: 최솟값 구하기 # MIN을 사용한 최솟값 SELECT MIN(DATETIME) As 시간 FROM ANIMAL_INS # LIMIT, ORDER BY 사용한 최솟값 SELECT DATETIME As 시간 FROM ANIMAL_INS ORDER BY DATETIME LIMIT 1 # 문제명: 동물 수 구하기 SELECT COUNT(*) AS count FROM ANIMAL_INS # 문제명: 중복 제거하기 SELECT COUNT(DISTINCT NAME) AS 'count' # DISTINCT에 WHERE IS NOT NULL 기능 포함 FROM ANIMAL_INS SELECT COUNT(DISTINCT NAME) AS 'count' FROM ANIMAL_INS WHERE NAME IS NOT NULL
프로그래머스에는 자주 쓰이는 SUM, MAX, MIN과 관련된 문제만 나왔지만, SQL 집계함수(aggregate function)에는 COUNT, SUM, MAX, MIN, AVG, ROUND, STDDEV, VARIANT 등 더 많은 집계 함수가 존재합니다.
IS NULL
# 문제명: 이름이 없는 동물의 아이디 # NULL 값 찾기 SELECT ANIMAL_ID FROM ANIMAL_INS WHERE NAME IS NULL ORDER BY ANIMAL_ID ASC # 문제명: 이름이 있는 동물의 아이디 # NULL 아닌 값 찾기 SELECT ANIMAL_ID FROM ANIMAL_INS WHERE NAME IS NOT NULL ORDER BY ANIMAL_ID ASC # 문제명: NULL 처리하기 # NULL을 다른 값으로 대체하기 SELECT ANIMAL_TYPE, IFNULL(NAME, "No name"), SEX_UPON_INTAKE FROM ANIMAL_INS ORDER BY ANIMAL_ID
String, Date
# 문제명: 루시와 엘라 찾기 # 다중 문자열 찾기 SELECT ANIMAL_ID, NAME, SEX_UPON_INTAKE FROM ANIMAL_INS WHERE NAME = "Lucy" OR NAME = "Ella" OR NAME = "Pickle" OR NAME = "Rogan" OR NAME = "Sabrina" OR NAME = "Mitty" # IN을 사용한 다중 문자열 찾기 SELECT ANIMAL_ID, NAME, SEX_UPON_INTAKE FROM ANIMAL_INS WHERE NAME IN ("Lucy", "Ella", "Pickle", "Rogan", "Sabrina", "Mitty") # 문제명: 이름에 el이 들어가는 동물 찾기 # 특정 문자열을 포함하는 컬럼 찾기 SELECT ANIMAL_ID, NAME FROM ANIMAL_INS WHERE NAME LIKE '%el%' AND ANIMAL_TYPE = "Dog" ORDER BY NAME # 문제명: 중성화 여부 파악하기 # 조건에 따라 특정 문자열을 치환(대체)하기 SELECT ANIMAL_ID, NAME, IF(SEX_UPON_INTAKE REGEXP 'Neutered|Spayed', 'O', 'X') AS '중성화' FROM ANIMAL_INS ORDER BY ANIMAL_ID # 문제명: 오랜 기간 보호한 동물(2) # 두개의 테이블을 JOIN 후, 특정값을 기준으로 ORDER BY하기 SELECT ANIMAL_OUTS.ANIMAL_ID, ANIMAL_OUTS.NAME #, ANIMAL_OUTS.DATETIME FROM ANIMAL_OUTS LEFT JOIN ANIMAL_INS ON ANIMAL_OUTS.ANIMAL_ID = ANIMAL_INS.ANIMAL_ID ORDER BY ANIMAL_OUTS.DATETIME - ANIMAL_INS.DATETIME DESC LIMIT 2 # 문제명: DATETIME에서 DATE로 형 변환 SELECT ANIMAL_ID, NAME, DATE_FORMAT(DATETIME, '%Y-%m-%d') As '날짜' FROM ANIMAL_INS ORDER BY ANIMAL_ID
GROUP BY
집계할 값이 없다면 GROUP BY와 같은 집계함수에서는 해당 결과물은 NULL로 아예 집계되지 않습니다. 따라서 집계될 요소가 없는 경우도 집계 결과를 0으로 반영하기 위해선 WITH RECURSIVE와 같은 구문과 JOIN을 활용해 추가적인 그룹을 활용해야합니다.
RIGHT JOIN 사용한 경우 - https://wwwnghks.tistory.com/148
GROUP BY 0인 경우까지 포함하는 방법 - http://www.gurubee.net/article/66199
# 문제명: 고양이와 개는 몇 마리 있을까 # 특정 컬럼에 대한 집계함수 적용하기 SELECT ANIMAL_TYPE, COUNT(ANIMAL_ID) AS count FROM ANIMAL_INS GROUP BY ANIMAL_TYPE ORDER BY ANIMAL_TYPE # 문제명: 동명 동물 수 찾기 # HAVING과 GROUP BY 사용 SELECT NAME, COUNT(NAME) As COUNT FROM ANIMAL_INS GROUP BY NAME HAVING COUNT(NAME) > 1 ORDER BY NAME # 문제명: 입양 시각 구하기(1) # WHERE과 GROUP BY 사용 SELECT HOUR(DATETIME) As HOUR, COUNT(DATETIME) As COUNT FROM ANIMAL_OUTS WHERE HOUR(DATETIME) >= 9 AND HOUR(DATETIME) < 20 GROUP BY HOUR(DATETIME) ORDER BY HOUR(DATETIME) # 문제명: 입양 시각 구하기(2) # GROUP BY가 0인 경우에 대해 COUNT하기 위해 WITH RECURSIVE 구문 사용 # 참고 코드 - https://programmers.co.kr/questions/19379 WITH RECURSIVE HOUR_RANGE(HOUR) AS ( SELECT 0 UNION SELECT HOUR_RANGE.HOUR+1 FROM HOUR_RANGE WHERE HOUR_RANGE.HOUR<23 ) SELECT HOUR_RANGE.HOUR AS HOUR, IFNULL(ANIMAL_GROUP.COUNT, 0) AS COUNT FROM HOUR_RANGE LEFT OUTER JOIN( SELECT HOUR(DATETIME) AS HOUR, COUNT(HOUR(DATETIME)) AS COUNT FROM ANIMAL_OUTS GROUP BY HOUR ) AS ANIMAL_GROUP ON HOUR_RANGE.HOUR = ANIMAL_GROUP.HOUR ORDER BY HOUR
JOIN
헷갈리는 문법 정리 - https://vg-rlo.tistory.com/229
MySQL IN/EXISTS 정리 - https://doorbw.tistory.com/222
INNER JOIN 같은 경우 MySQL에서는 WHERE절로도 표현할 수 있습니다. 따라서 문제 중에 INNER JOIN을 해야하는 경우라면 JOIN 대신 WHERE절을 사용해서도 풀 수 있습니다.
# 문제명: 없어진 기록 찾기 # RIGHT JOIN 사용 SELECT ANIMAL_OUTS.ANIMAL_ID, ANIMAL_OUTS.NAME FROM ANIMAL_INS RIGHT JOIN ANIMAL_OUTS ON ANIMAL_INS.ANIMAL_ID = ANIMAL_OUTS.ANIMAL_ID WHERE ANIMAL_INS.ANIMAL_ID IS NULL ORDER BY ANIMAL_OUTS.ANIMAL_ID # 문제명: 있었는데요 없었습니다 # JOIN 대신 WHERE절 사용 SELECT ANIMAL_OUTS.ANIMAL_ID, ANIMAL_OUTS.NAME FROM ANIMAL_OUTS, ANIMAL_INS WHERE ANIMAL_OUTS.DATETIME < ANIMAL_INS.DATETIME AND ANIMAL_OUTS.ANIMAL_ID = ANIMAL_INS.ANIMAL_ID ORDER BY ANIMAL_INS.DATETIME ASC # 문제명: 오랜 기간 보호한 동물(1) # JOIN 대신 NOT IN 사용 SELECT ANIMAL_INS.NAME, ANIMAL_INS.DATETIME FROM ANIMAL_INS WHERE ANIMAL_INS.ANIMAL_ID NOT IN (SELECT ANIMAL_ID FROM ANIMAL_OUTS) ORDER BY ANIMAL_INS.DATETIME ASC LIMIT 3 # 문제명: 보호소에서 중성화한 동물 # JOIN 대신 WHERE절 사용 SELECT ANIMAL_OUTS.ANIMAL_ID, ANIMAL_OUTS.ANIMAL_TYPE, ANIMAL_OUTS.NAME FROM ANIMAL_OUTS, ANIMAL_INS WHERE ANIMAL_OUTS.ANIMAL_ID = ANIMAL_INS.ANIMAL_ID AND ANIMAL_OUTS.SEX_UPON_OUTCOME != ANIMAL_INS.SEX_UPON_INTAKE AND ANIMAL_INS.SEX_UPON_INTAKE LIKE "Intact%"