역시 코테는 문제를 풀어봐야 배우는 점이 많다.
너무 쉬운 문제나, 직전 문제의 아이디어를 알면 바로 해결할 수 있는 문제는 작성하지 않았다.
적절한 접근인지 의문이 드는 문제들(특히 대장균 시리즈)은 Perplexity(GPT-4o)에게 물어본 쿼리도 함께 공부해서 확인해보았다.
(이전 문제들은 올리신 분들이 많아서 추후 추가 예정)
테이블에는 각 자동차의 ID, 종류, 대여 요금이 주어진다.
자동차 종류가 'SUV'인 자동차들의 평균 일일 대여 요금을 구하여라.
이때, 일일 대여 요금은 소수 첫번째 자리에서 반올림한다.
select round(avg(DAILY_FEE), 0) as AVERAGE_FEE
from CAR_RENTAL_COMPANY_CAR
where CAR_TYPE = 'SUV'
group by CAR_TYPE
avg() 함수를 사용하면 되고, 반올림을 할 때는 round(반올림할 수, 나타낼 소수점 자릿수)를 사용하면 된다.테이블에는 의사의 이름, ID, 고용일자, 진료과코드 등이 주어진다.
진료과가 흉부외과(CS)거나 일반외과(GS)인 의사의 이름, ID, 진료과, 고용일자를 구하여라.
결과는 고용일자의 내림차순, 같다면 이름의 오름차순으로 정렬하여 나타낸다.
select DR_NAME, DR_ID, MCDP_CD, date_format(HIRE_YMD, "%Y-%m-%d") as HIRE_YMD
from DOCTOR
where MCDP_CD = 'CS' or MCDP_CD = 'GS'
order by HIRE_YMD desc, DR_NAME asc
date_format()함수를 사용하여 DATE또는 DATETIME 자료형의 출력 형태를 변화시킬 수 있다.date()함수를 사용해보기도 했지만, 신기하게도 DATETIME으로 나타난다.... 그냥 date_format()을 사용해야겠다.REST_INFO 테이블은 식당 ID, 이름, 음식 종류, 즐겨찾기수, 주소 등을 나타낸다.
REST_REVIEW 테이블은 리뷰 ID, 식당 ID, 리뷰 점수 등을 나타낸다.
서울에 위치한 식당들의 식당 ID, 식당 이름, 음식 종류, 즐겨찾기수, 주소, 리뷰 평균 점수를 얻어라.
이때 리뷰 평균 점수는 소수점 세번째 자리에서 반올림한다.
결과는 평균 점수의 내림차순, 같다면 즐겨찾기수의 내림차순으로 정렬한다.
select REST_ID, REST_NAME, FOOD_TYPE, FAVORITES, ADDRESS, round(avg(REVIEW_SCORE), 2) as SCORE
from REST_INFO join REST_REVIEW using(REST_ID)
where ADDRESS like "서울%"
group by REST_ID
order by SCORE desc, FAVORITES desc
like에 와일드카드인 %를 사용해서 서울에 위치한 식당을 얻고, 식당 ID로 그룹화하여 리뷰 평균 점수를 얻었다.테이블은 환자 번호, 이름, 성별코드, 나이, 전화번호가 주어진다.
12세 이하인 여자환자의 환자 이름, 번호, 성별코드, 나이, 전화번호를 나타내라.
이때 전화번호가 없는 경우에는 'NONE'으로 출력한다.
결과는 나이의 내림차순, 같다면 환자이름의 오른차순으로 정렬한다.
select PT_NAME, PT_NO, GEND_CD, AGE, ifnull(TLNO, 'NONE') as TLNO
from PATIENT
where AGE <= 12 and GEND_CD = 'W'
order by AGE desc, PT_NAME asc
ifnull(열, 출력할 값)을 사용하면 해당 열의 값이 null일 때 특정 값으로 나타낼 수 있다.테이블에는 판매 ID, 회원 ID, 상품 ID, 판매량, 판매일이 주어진다.
동일한 회원이 동일한 상품을 재구매한 적이 있는 경우에 대해 회원 ID와 상품 ID를 구하여라.
결과는 회원 ID의 오름차순, 같다면 상품 ID의 내림차순으로 정렬한다.
select USER_ID, PRODUCT_ID
from ONLINE_SALE
group by USER_ID, PRODUCT_ID
having count(*) > 1
order by USER_ID asc, PRODUCT_ID desc
테이블에는 동물 ID, 이름, 보호 시작일이 주어진다.
동물 보호소에 가장 먼저 들어온 동물의 이름을 얻어라.
select NAME
from ANIMAL_INS
order by DATETIME
limit 1
limit을 이용해서 상위 n개의 레코드만 보는 설정이 가능하다.테이블에는 각 ID의 부모 ID가 주어진다.
이때, 각 ID의 자식 개수를 구하여라.
출력은 ID의 오름차순으로 나타낸다.
나의 풀이
select ID, IFNULL(CHILD_COUNT, 0) as CHILD_COUNT
from ECOLI_DATA natural left outer join (
select PARENT_ID as ID, count(PARENT_ID) as CHILD_COUNT
from ECOLI_DATA
group by PARENT_ID
having PARENT_ID is not null
) as A
order by ID
left outer join을 수행해 전체 ID에 자식 개수를 연결해주고, 자식이 0인 경우에는 정보가 없어서 null로 되어 있을 테니 ifnull을 활용해 0으로 설정해준다.GPT-4o
SELECT ID, COUNT(CHILDREN.PARENT_ID) AS CHILD_COUNT
FROM ECOLI_DATA
LEFT JOIN ECOLI_DATA AS CHILDREN ON ECOLI_DATA.ID = CHILDREN.PARENT_ID
GROUP BY ECOLI_DATA.ID
ORDER BY ECOLI_DATA.ID ASC;
left join을 수행하는 접근법은 동일했다. (left join과 left outer join은 동일하다)테이블에는 ID와 개체의 크기가 주어진다.
이때 개체의 크기가 100 이하인 경우 'LOW', 100 초과 1000 이하인 경우 'MEDIUM', 1000 초과인 경우 'HIGH'로 변경하여 ID와 개체의 크기를 출력하라.
결과는 ID의 오름차순으로 정렬하여 나타낸다.
나의 풀이
(select ID, 'LOW' as SIZE
from ECOLI_DATA
where SIZE_OF_COLONY <= 100)
union
(select ID, 'MEDIUM' as SIZE
from ECOLI_DATA
where SIZE_OF_COLONY > 100 and SIZE_OF_COLONY <= 1000)
union
(select ID, 'HIGH' as SIZE
from ECOLI_DATA
where SIZE_OF_COLONY > 1000)
order by ID
GPT-4o
SELECT ID,
CASE
WHEN SIZE_OF_COLONY <= 100 THEN 'LOW'
WHEN SIZE_OF_COLONY > 100 AND SIZE_OF_COLONY <= 1000 THEN 'MEDIUM'
ELSE 'HIGH'
END AS SIZE
FROM ECOLI_DATA
ORDER BY ID ASC;
CASE 문을 활용해 나타내었다.테이블에는 각 개체의 ID와 형질이 주어진다.
2번 형질을 보유하지 않으면서 1번이나 3번 형질을 보유하는(1번과 3번 모두 보유한 경우도 포함) 개체 수를 구하여라.
다만 각 개체의 형질은 2진수의 형태로 주어진다.
나의 풀이
select count(ID) as COUNT
from ECOLI_DATA
where GENOTYPE & 2 = 0 and (GENOTYPE & 1 = 1 or GENOTYPE & 4 = 4)
&(and)를 사용했다.GENOTYPE & 4 = 4 이 부분에서 실수했는데, 비트 단위 연산이기는 하지만 각 자리 숫자에 모두 연산한 결과를 얻기 떄문에 1이 아니라 4와 같다고 설정해야 한다.GPT-4o
SELECT COUNT(*) AS COUNT
FROM ECOLI_DATA
WHERE (GENOTYPE & 2) = 0 -- 2번 형질을 보유하지 않음
AND ((GENOTYPE & 1) != 0 OR (GENOTYPE & 4) != 0); -- 1번 또는 3번 형질을 보유
COUNT(*)를 사용하는 것이 더 적절한 것 같긴 하다.AS COUNT는 작성해주지 않아서 직접 추가했다.테이블에는 개체의 ID와 형질, 그리고 부모 개체의 ID가 주어진다.
부모의 형질을 모두 보유한 개체의 ID와 형질, 그리고 부모 개체의 형질을 구하여라.
결과는 ID의 오름차순으로 정렬하여 나타내라.
다만 각 개체의 형질은 2진수의 형태로 주어진다.
나의 풀이
select A.ID, A.GENOTYPE GENOTYPE, B.GENOTYPE PARENT_GENOTYPE
from ECOLI_DATA A left join ECOLI_DATA B on(A.PARENT_ID = B.ID)
where A.GENOTYPE & B.GENOTYPE = B.GENOTYPE
order by A.ID
left join을 수행해 각 개체에 부모 정보를 결합했다.&를 사용했다. 부모의 형질을 모두 갖고 있다면 &를 했을 때 부모 형질이 그대로 나타나야 한다.GPT-4o
SELECT CHILD.ID, CHILD.GENOTYPE, PARENT.GENOTYPE AS PARENT_GENOTYPE
FROM ECOLI_DATA AS CHILD
JOIN ECOLI_DATA AS PARENT ON CHILD.PARENT_ID = PARENT.ID
WHERE (CHILD.GENOTYPE | PARENT.GENOTYPE) = CHILD.GENOTYPE
ORDER BY CHILD.ID ASC;
left join을 수행해 자식 정보와 부모 정보를 결합한다는 아이디어는 동일하다.|(or)을 사용했다는 차이가 있다. 이러한 방식도 가능한 이유는, 자식이 부모 형질을 가지고 있지 않으면 or을 수행했을 때 자식 형질이 그대로 얻어지지 않기 때문이다.테이블에는 개체의 ID와 크기가 주어진다.
개체의 크기를 내림차순으로 정렬했을 때 상위 25%까지를 'CRITICAL', 50%까지를 'HIGH', 75%까지를 'MEDIUM', 나머지를 'LOW'라고 분류할 수 있다.
이런 상황에서 개체의 ID와 분류된 이름을 얻어라. 결과는 개체의 ID에 대해 오름차순으로 정렬하여 나타낸다.
이때 총 데이터의 수는 4의 배수이며, 같은 크기 개체가 다른 이름으로 분류되는 경우는 없다.
나의 풀이
select A.ID,
case
when A.SIZE_RANK <= A.COUNT * 0.25 then 'CRITICAL'
when A.SIZE_RANK > A.COUNT * 0.25 and A.SIZE_RANK <= A.COUNT * 0.50 then 'HIGH'
when A.SIZE_RANK > A.COUNT * 0.50 and A.SIZE_RANK <= A.COUNT * 0.75 then 'MEDIUM'
else 'LOW'
end as COLONY_NAME
from (select ID, row_number() over(order by SIZE_OF_COLONY desc) as SIZE_RANK, count(*) over() as COUNT
from ECOLI_DATA) as A
order by A.ID
row_number()라는 함수가 있었다. 뒤의 over 내에 정렬 방법을 설정해서 그에 대응하는 순위를 얻었다.group by없이 집계 함수 count(*)를 다른 열과 함께 선택하면 에러가 발생한다는 것이다. 뒤에 over()을 꼭 붙여주어야 에러가 발생하지 않는다.percent_rank()라는 함수가 있었으며, 적용 방식이 row_number()와 유사하다. 이를 활용한 예시는 아래와 같다.select A.ID,
case
when A.PERCENT <= 0.25 then 'CRITICAL'
when A.PERCENT > 0.25 and A.PERCENT <= 0.50 then 'HIGH'
when A.PERCENT > 0.50 and A.PERCENT <= 0.75 then 'MEDIUM'
else 'LOW'
end as COLONY_NAME
from (select ID, percent_rank() over(order by SIZE_OF_COLONY desc) as PERCENT, count(*) over() as COUNT
from ECOLI_DATA) as A
order by A.ID
case 문에서 위아래 구간을 모두 표현했지만, 사실 순서대로 실행되기 때문에 이전 조건에서 처리될 조건은 적지 않아도 생각한 대로 동작하기는 한다.GPT-4o
WITH RankedColony AS (
SELECT ID, SIZE_OF_COLONY,
NTILE(4) OVER (ORDER BY SIZE_OF_COLONY DESC) AS Quartile
FROM ECOLI_DATA
)
SELECT ID,
CASE Quartile
WHEN 1 THEN 'CRITICAL'
WHEN 2 THEN 'HIGH'
WHEN 3 THEN 'MEDIUM'
WHEN 4 THEN 'LOW'
END AS COLONY_NAME
FROM RankedColony
ORDER BY ID ASC;
NTILE() 함수를 사용한 접근법이다. 전체적인 아이디어와 함수 활용 방법은 유사하다.테이블에는 개체의 ID, 부모의 ID가 주어진다.
3세대 개체의 ID를 얻어라.
결과는 ID의 오름차순으로 정렬해서 나타내어라.
이때 최초 개체의 부모 ID는 NULL이다.
나의 풀이
select A.ID
from (ECOLI_DATA A join ECOLI_DATA B on(A.PARENT_ID = B.ID))
join ECOLI_DATA C on(B.PARENT_ID = C.ID)
where C.PARENT_ID is null
order by A.ID
outer join 대신 그냥 join을 사용해서 이로 인한 문제를 회피했다.GPT-4o
SELECT ID
FROM ECOLI_DATA
WHERE PARENT_ID IN (
SELECT ID FROM ECOLI_DATA WHERE PARENT_ID IN (
SELECT ID FROM ECOLI_DATA WHERE PARENT_ID IS NULL
)
)
ORDER BY ID ASC;
join 대신 in을 사용하기 때문에 나의 접근보다 더 효율적일 것 같다.테이블에는 개체의 ID, 부모 ID가 주어진다.
각 세대에 대해 그 세대 번호와 세대 별 자식이 없는 개체의 수를 구하여라.
결과는 세대에 대해 오름차순으로 나타낸다.
이때, 모든 세대에는 자식이 없는 개체가 적어도 1개체 존재한다.
나의 풀이
with recursive RECURSION as(
select ID, PARENT_ID, 1 as GENERATION
from ECOLI_DATA
where PARENT_ID is null
union all
select A.ID, A.PARENT_ID, REC.GENERATION+1 as GENERATION
from ECOLI_DATA A join RECURSION REC on(A.PARENT_ID = REC.ID)
where A.PARENT_ID = REC.ID
)
select count(*) as COUNT, GENERATION
from RECURSION
where ID not in (select PARENT_ID from ECOLI_DATA where PARENT_ID is not null)
group by GENERATION
order by GENERATION
with recursion을 사용하면 되는 것이었다.union해서 결과를 얻는 것이다.PARENT_ID is not null이라는 조건이 필요하다는 것이었다. 이는 null과 not in을 수행하면 전체가 부정이 되어버리는 상황이 발생하기 때문이었다.GPT-4o
WITH RECURSIVE RecursiveGenerations AS (
SELECT ID, PARENT_ID, 1 AS GENERATION
FROM ECOLI_DATA
WHERE PARENT_ID IS NULL
UNION ALL
SELECT e.ID, e.PARENT_ID, rg.GENERATION + 1
FROM ECOLI_DATA e
JOIN RecursiveGenerations rg ON e.PARENT_ID = rg.ID
),
LeafNodes AS (
SELECT ID
FROM ECOLI_DATA
WHERE ID NOT IN (SELECT DISTINCT PARENT_ID FROM ECOLI_DATA WHERE PARENT_ID IS NOT NULL)
)
SELECT COUNT(rg.ID) AS COUNT, rg.GENERATION
FROM RecursiveGenerations rg
JOIN LeafNodes ln ON rg.ID = ln.ID
GROUP BY rg.GENERATION
ORDER BY rg.GENERATION ASC;
LeafNodes라는 이름의 CTE(Common Table Expression)로 따로 얻었다는 차이점이 있다.with recursive가 아니라 그냥 with로 적혀있어서 에러가 발생했는데, 해당 부분을 수정해서 작동하도록 했다.