어제 진행한 모 기업 코딩테스트에서 SQL 문제를 풀지 못한 충격에 바로 공부에 들어갔다. 틀릴만 했던 것이 맨날 JPA 같은 추상화 된 기술을 쓰면서 SQL
을 잘 다룬다고 착각했고 따로 SQL
공부를 잘 하지 않았다.. 반성 ..
아주 쉬운 문제만 빼고 모든 풀이를 기록하려 한다.
정답쿼리? 를 가져온 것이 아니고 프로그래머스 채점 기준이기 때문에 비효율적인 쿼리가 있을 수 있다.
날짜(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
을 사용한다.
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);
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
인 경우 다른 값으로 대체할 수 있는가를 묻는 문제이다.
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;
없어진 기록 찾기 문제와 거의 동일한 문제이다.
관계를 맺은 테이블에서 한 쪽에만 있는 데이터를 찾아야 한다.
한 쪽에만 => 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 join
과 like
를 쓸 줄 아는 지 물어보는 문제이다.
특정 칼럼을 기준으로 조건을 걸고 조건에 부합하는 경우 특정 문자를 부합하지 않는 경우 특정 문자를 출력해야 하는 문제이다.
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;
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;