[프로그래머스] SQL 고득점 kit 풀이

Kim Dae Hyun·2022년 2월 6일
0


어제 진행한 모 기업 코딩테스트에서 SQL 문제를 풀지 못한 충격에 바로 공부에 들어갔다. 틀릴만 했던 것이 맨날 JPA 같은 추상화 된 기술을 쓰면서 SQL을 잘 다룬다고 착각했고 따로 SQL 공부를 잘 하지 않았다.. 반성 ..


아주 쉬운 문제만 빼고 모든 풀이를 기록하려 한다.
정답쿼리? 를 가져온 것이 아니고 프로그래머스 채점 기준이기 때문에 비효율적인 쿼리가 있을 수 있다.


✅ 상위 N개 레코드

날짜(datetime)을 기준으로 상위 N개 레코드를 select 하는 문제이다.
날짜 기준 내림차순 정렬 후 limit를 이용해서 N개 레코드를 가져온다.

SELECT name
from animal_ins
order by datetime asc
limit 1;

✅ 중복 제거하기

동물의 이름(name)을 기준으로 중복을 제거한 row 수를 조회하는 문제이다.
중복제거는 distinct를 row 수는 count를 사용한다.

select count(distinct name) as 'count'
from animal_ins;

✅ 동명 동물 수 찾기

2번 이상 사용된 동물의 이름(name)을 조회하는 문제이다.

이름 별로 카운팅 후 카운팅 된 결과가 2 이상인 행을 출력한다.

SELECT name, count(name) as 'count'
from animal_ins
group by name
having count(name) > 1
order by name;

집계된 결과에 조건을 걸어야 하므로 where가 아닌 having을 사용한다.


✅ 입양 시각 구하기 (1)

datetime 타입의 필드에서 시간 범위 안에 있는 건 수를 조회하는 문제이다.

datetime에서 시간에 대한 정보만 뽑아낼 수 있는 HOUR 함수를 사용한다.

시간으로 그룹핑을 수행하고 그 건수를 집계하는 방식으로 풀이한다.

SELECT HOUR(datetime), count(HOUR(datetime)) as 'count'
from animal_outs
where HOUR(datetime) between 9 and 19
group by HOUR(datetime)
order by HOUR(datetime);

✅ 입양 시각 구하기 (2)

sql 공부를 게을리 한 나에게 가장 어려웠던 문제이다.

입양 시각 구하기 (1)와 거의 동일한 문제인데 테이블에 없는 시각까지 조회해줘야 하기 때문에 그룹핑을 사용할 수 없었다.

테이블에 없는 시각이 있기 때문에 시각에 대한 정보는 SET 을 사용해서 시각을 위한 변수를 선언하고 매 row마다 1씩 증가되도록 했다.

SET은 mysql에서 변수를 사용할 수 있는 키워드이다.

SET @변수이름 = 초기값;

SET을 사용할 때를 제외하고 변수에 값을 바꾸려 할 때는 =이 아니고 :=를 사용해야 한다.
SET을 제외한 모든 곳에서 =는 동등성 검사로 사용되기 때문이다.

SET @HOUR = -1;
SELECT (@HOUR := @HOUR +1) as 'HOUR', 
    (
    SELECT count(HOUR(datetime))
        FROM animal_outs
        WHERE HOUR(datetime) = @HOUR
    ) as 'COUNT'
FROM animal_outs
WHERE @HOUR < 23;

매 row마다 증가되는 @HOUR와 같은 시각(HOUR(datetime))을 갖는 row를 카운팅하는 방식이다.

매 row마다 @HOUR가 증가되므로 종료조건이 필요하다.

WHERE @HOUR < 23

✅ NULL 처리하기

칼럼의 값이 null 인 경우 다른 값으로 대체할 수 있는가를 묻는 문제이다.
ifnull(값, '대체값') 을 사용한다.

SELECT animal_type, ifnull(name, 'No name'), sex_upon_intake
from animal_ins
order by animal_id

name 칼럼이 null인 경우 No name을 대체값으로 사용한다.


✅ 없어진 기록 찾기

이 문제는 관계를 맺은 두 테이블이 있을 때 한 쪽 테이블에만 있는 데이터를 찾을 수 있는가를 묻는다.

보호소에 들어온 동물에 대한 테이블 animal_ins
입양 간 동물에 대한 테이블 animal_outs가 있을 때 입양은 보냈지만 보호소에 들어온 기록이 없는 동물을 찾아야 한다.

조인칼럼은 animal_id이다.

데이터가 확실히 있는 쪽을 기준으로 left join을 수행한다.
문제에서는 animal_outs가 될 것이다.

데이터가 확실히 있는 쪽을 기준으로 left join하게 되면 기준이 되는 테이블의 모든 데이터는 모두 들고오고 join되는 테이블에 데이터가 없는 경우 null로 채워서 들고 온다.

left join animal_ins ai on ao.animal_id = ai.animal_id

join되는 테이블에서 null인 것만 들고오면 조인 기준 테이블에만 있는 데이터를 찾게 된다.

where ai.animal_id is null;

select ao.animal_id, ao.name
from animal_outs ao
left join animal_ins ai on ao.animal_id = ai.animal_id
where ai.animal_id is null;

✅ 오랜 기간 보호한 동물(1)

없어진 기록 찾기 문제와 거의 동일한 문제이다.
관계를 맺은 테이블에서 한 쪽에만 있는 데이터를 찾아야 한다.

한 쪽에만 => left join & is null

SELECT ai.name, ai.datetime
from animal_ins ai
left join animal_outs ao on ai.animal_id = ao.animal_id
where ao.animal_id is null
order by ai.datetime
limit 3;

보호소에 들어왔지마 아직 입양가지 않는 동물 중 보호소에 들어온 날짜가 오래된 3마리 동물을 조회한다.

데이터가 확실히 있는 쪽을 기준으로 하고 (from 절에 두고) left join을 수행한다. 데이터가 없을 수 있는 join되는 테이블을 기준으로 null 체크를 수행한다.


✅ 보호소에서 중성화한 동물

SELECT ai.animal_id, ai.animal_type, ai.name
from animal_ins ai
join animal_outs ao on ai.animal_id = ao.animal_id
where ai.sex_upon_intake like 'Intact%' and (ao.sex_upon_outcome like 'Spayed%' or ao.sex_upon_outcome like 'Neutered%')
order by ai.animal_id;

기본적인 inner joinlike를 쓸 줄 아는 지 물어보는 문제이다.


✅ 중성화 여부 파악하기

특정 칼럼을 기준으로 조건을 걸고 조건에 부합하는 경우 특정 문자를 부합하지 않는 경우 특정 문자를 출력해야 하는 문제이다.

select절에서 if문을 사용해서 풀이한다.

select if(조건, 'O', 'X');

ex)
select if(answer=True, 'O', 'X');
SELECT animal_id, name, 
if(sex_upon_intake like 'Neutered%' or sex_upon_intake like 'Spayed%', 
   'O', 'X') as '중성화'
from animal_ins
order by animal_id;

✅ 오랜 기간 보호한 동물(2)

SELECT ai.animal_id, ai.name
from animal_ins ai
join animal_outs ao on ai.animal_id = ao.animal_id
order by ao.datetime - ai.datetime desc
limit 2;
profile
좀 더 천천히 까먹기 위해 기록합니다. 🧐

0개의 댓글