본문 바로가기

coding/Others

[SQL] 프로그래머스 문제 풀이 모음집 (Oracle)

 

프로그래머스에는 일반 알고리즘 문제도 있지만 sql만 따로 연습해볼 수 있는 SQL 고득점 kit도 준비되어 있다.

https://programmers.co.kr/learn/challenges?tab=sql_practice_kit

 

코딩테스트 연습

기초부터 차근차근, 직접 코드를 작성해 보세요.

programmers.co.kr

 

지금까지는 총 27문제가 있는데, 아무래도 숙련되기엔 좀 적다. 필자는 sql 코테를 준비할 때 리트코드(LeetCode)에 있는 문제들을 추가로 풀어보기도 했다. 리트코드에서 푼 문제들은 나중에 따로 정리할 것이다.

 

 

풀이 언어는 오라클이다. 각 파트별로 나뉘어져 있다.

 

SELECT

SELECT문은 기본 중 기본이다. 그래서 다 레벨 1 정도의 쉬운 문제밖에 없다.

Level 1. 모든 레코드 조회하기

SELECT *
FROM animal_ins
ORDER BY animal_id;

 

Level 1. 역순 정렬하기

SELECT name, datetime
FROM animal_ins
ORDER BY animal_id DESC;

 

Level 1. 아픈 동물 찾기

SELECT animal_id, name
FROM animal_ins
WHERE intake_condition='Sick'
ORDER BY animal_id;

 

Level 1. 어린 동물 찾기

SELECT animal_id, name
FROM animal_ins
WHERE intake_condition<>'Aged'
ORDER BY animal_id;

 

Level 1. 동물의 아이디와 이름

SELECT animal_id, name
FROM animal_ins
ORDER BY animal_id;

 

Level 1. 여러 기준으로 정렬하기

SELECT animal_id, name, datetime
FROM animal_ins
ORDER BY name, datetime DESC;

 

Level 1. 상위 n개 레코드

SELECT name
FROM (
    SELECT *
    FROM animal_ins
    ORDER BY datetime
    )
WHERE ROWNUM=1;

 

 

IS NULL

sql 문제푸는 과제들을 하다보니 IS문으로 null인지 아닌지 판단하는 것보다는 NVL() 함수를 통해서 null 값을 0으로 바꿔주는 것이(혹은 다른 디폴트 값으로 바꿔주는 것이) 더 자주 쓰였다.

Level 1. 이름이 없는 동물의 아이디

SELECT animal_id
FROM animal_ins
WHERE name IS NULL
ORDER BY animal_id;

 

Level 1. 이름이 있는 동물의 아이디

SELECT animal_id
FROM animal_ins
WHERE name IS NOT NULL
ORDER BY animal_id;

 

Level 2. NULL 처리하기

SELECT animal_type, NVL(name, 'No name'), sex_upon_intake
FROM animal_ins
ORDER BY animal_id;

 

 

SUM, MAX, MIN

Level 1. 최댓값 구하기

SELECT MAX(datetime)
FROM animal_ins
ORDER BY datetime DESC;

 

Level 2. 최솟값 구하기

SELECT MIN(datetime)
FROM animal_ins
ORDER BY datetime DESC;

 

Level 2. 동물 수 구하기

SELECT COUNT(*)
FROM animal_ins;

 

Level 2. 중복 제거하기

SELECT COUNT(DISTINCT name)
FROM animal_ins;

 

 

JOIN

sql의 꽃은 서브쿼리와 조인이라고 생각한다. 그래서 프로그래머스의 sql 문제들 중 난이도 높은 것들은 다 조인에 몰려있는 듯...

Level 3. 없어진 기록 찾기

SELECT animal_id, name
FROM animal_outs
MINUS
SELECT animal_id, name
FROM animal_ins
ORDER BY animal_id;

 

Level 3. 있었는데요 없었습니다

SELECT A.animal_id, A.name
FROM animal_outs A, animal_ins B
WHERE A.animal_id=B.animal_id AND A.datetime<B.datetime
ORDER BY B.datetime;

 

Level 3. 오랜 기간 보호한 동물(1)

WITH NOT_YET AS (SELECT *
                 FROM animal_ins
                 WHERE animal_id NOT IN (
                                        SELECT animal_id
                                        FROM animal_outs
                                        )
                 )
SELECT name, datetime
FROM NOT_YET
ORDER BY datetime 
FETCH first 3 ROWS ONLY;

 ** 필자가 생각하는 소소한 팁: WITH문 사용하는 방법을 익혀두면 좋다. 서브쿼리가 많아지면 많아질수록 가독성이 떨어진다(테이블 안에 테이블 안에 테이블 안에.....) WITH문으로 새로운 테이블을 정의하고 FROM문에다 넣어주면 여러개의 테이블을 참조하더라도 어디서 뭘 가공했는지 빠르게 파악할 수 있다.

 

FETCH문은 상위 n개의 레코드를 가져올 때 쓰기 좋다. 오라클 12버전에서는 rownum으로 썼었는데 이후 버전부터는 FETCH문을 사용할 수 있게 되었다. 자세한 사용 방법은 이쪽을 참고했다.

 

Level 4. 보호소에서 중성화한 동물

WITH BEFORE AS (SELECT *
                FROM animal_ins
                WHERE sex_upon_intake LIKE 'Intact%'),
AFTER AS (SELECT *
          FROM animal_outs
          WHERE sex_upon_outcome NOT LIKE 'Intact%')

SELECT AFTER.animal_id, AFTER.animal_type, AFTER.name
FROM BEFORE, AFTER
WHERE BEFORE.animal_id=AFTER.animal_id;

 

 

GROUP BY

개인적으로 GROUP BY 문제들에서는 [Level 4. 입양 시각 구하기(2)]가 킬러 문제였다. 시간 없으신 분들은 이 문제만이라도 연습해보시길...

Level 2. 고양이와 개는 몇 마리 있을까

SELECT animal_type, COUNT(ROWNUM)
FROM animal_ins
WHERE animal_type IN ('Cat', 'Dog')
GROUP BY animal_type
ORDER BY animal_type;

 

Level 2. 동명 동물 수 찾기

SELECT name, COUNT(name)
FROM animal_ins
GROUP BY name
HAVING COUNT(name)>1
ORDER BY name;

 

Level 2. 입양 시각 구하기(1)

SELECT TO_NUMBER(TO_CHAR(datetime, 'HH24')) HOUR , COUNT(TO_NUMBER(TO_CHAR(datetime, 'HH24'))) COUNT
FROM animal_outs
GROUP BY TO_NUMBER(TO_CHAR(datetime, 'HH24'))
HAVING TO_NUMBER(TO_CHAR(datetime, 'HH24')) BETWEEN 9 AND 19
ORDER BY TO_NUMBER(TO_CHAR(datetime, 'HH24'));

뭔가 이거 말고 더 스마트하게 푸는 방법이 있을텐데... 문자로 바꿨다가 다시 숫자 형식으로 바꾸는 과정을 거쳐야 한다는게 번거롭다.

 

Level 4. 입양 시각 구하기(2) 

WITH table_A AS (
    SELECT TO_NUMBER(TO_CHAR(datetime, 'HH24')) HOUR, COUNT(TO_NUMBER(TO_CHAR(datetime, 'HH24'))) COUNT
    FROM animal_outs
    GROUP BY TO_NUMBER(TO_CHAR(datetime, 'HH24'))
    ORDER BY HOUR
    ),
table_B AS (
    SELECT LEVEL-1 DATES
    FROM DUAL
    CONNECT BY LEVEL<=24)

SELECT table_B.DATES, NVL(table_A.COUNT, 0)
FROM table_A, table_B
WHERE table_A.HOUR(+)=table_B.DATES
ORDER BY table_B.DATES;

자체 제공되는 DUAL 테이블을 잘 활용할 줄 알아야 한다. 문제에서 주어진 테이블로만 풀면 예시 답안처럼 나오지 않기 때문에 DUAL 테이블을 갖고 와서 값이 없는 날짜를 채워주고, null로 반환되는 곳은 NVL() 함수를 이용해 0으로 바꿔준다. 마지막으로 외부 조인(outer join)을 통해 table_A에는 없는 날짜들을 table_B에서 가져온다!

 

 

String, Date

Level 2. 루시와 엘라 찾기

SELECT animal_id, name, sex_upon_intake
FROM animal_ins
WHERE name IN ('Lucy', 'Ella', 'Pickle', 'Rogan', 'Sabrina', 'Mitty')
ORDER BY animal_id;

 

Level 2. 이름에 el이 들어가는 동물 찾기

SELECT animal_id, name
FROM animal_ins
WHERE UPPER(name) LIKE '%EL%' AND animal_type='Dog'
ORDER BY name;

 

Level 2. 중성화 여부 파악하기

SELECT animal_id, name,
    CASE
        WHEN sex_upon_intake LIKE '%Neutered%' THEN 'O'
        WHEN sex_upon_intake LIKE '%Spayed%' THEN 'O'
        ELSE 'X'
    END
FROM animal_ins
ORDER BY animal_id;

 

Level 3. 오랜 기간 보호한 동물(2)

SELECT *
FROM
    (SELECT A.animal_id, A.name
    FROM animal_ins A,
        (
        SELECT *
        FROM animal_outs
        ) B
    WHERE A.animal_id=B.animal_id
    ORDER BY B.datetime-A.datetime DESC)
WHERE ROWNUM<=2;

왜 자꾸 rownum을 쓰는거냐... FETCH를 써라...

 

Level 2. DATETIME에서 DATE로 형 변환 

SELECT animal_id, name, TO_CHAR(datetime, 'YYYY-MM-DD')
FROM animal_ins
ORDER BY animal_id;

 

 

-----------------------------------끝-----------------------------------------

 

프로그래머스가 sql 문제 더 추가해줬으면 좋겠다~~~ (쩌렁쩌렁)