오늘은 정말 아픈 날이다. 파이썬 세션은 그냥 멍하니 들었고, QCC는 핵심 문제라 할 수 있는 2번을 틀렸다. 듣기로 했던 강의도 다 듣지 못한 것까지 포함해, 부족함이 많았던 하루였다.
뭔가 다짐을 한 것에 비해, 이룬 것이 없어 보인 우울했던 하루. 우선 다시 5일로 돌아가, 내가 한 일들을 알아보자.
코드카타는 이제 큰 어려움이 없이 풀고 있다. 아직 고난도 문제로 들어가지 않았기 때문도 있지만, 그래도 문제에 접근하는 것에서 망설임이 없다는 건 좋은 거라고 생각한다. 조금 무대뽀로 문제를 푸는 점이 안타까울 따름이다.
RODUCT 테이블에서 만원 단위의 가격대 별로 상품 개수를 출력하는 SQL 문을 작성해주세요. 이때 컬럼명은 각각 컬럼명은 PRICE_GROUP, PRODUCTS로 지정해주시고 가격대 정보는 각 구간의 최소금액(10,000원 이상 ~ 20,000 미만인 구간인 경우 10,000)으로 표시해주세요. 결과는 가격대를 기준으로 오름차순 정렬해주세요.
SELECT CASE WHEN price between 10000 and 19999 THEN 10000
WHEN price between 20000 and 29999 THEN 20000
WHEN price between 30000 and 39999 THEN 30000
WHEN price between 40000 and 49999 THEN 40000
WHEN price between 50000 and 59999 THEN 50000
WHEN price between 60000 and 69999 THEN 60000
WHEN price between 70000 and 79999 THEN 70000
WHEN price between 80000 and 89999 THEN 80000 END PRICE_GROUP
, COUNT(1) PRODUCTS
FROM PRODUCT
GROUP BY 1
ORDER BY 1
진짜 무식해보이는 쿼리지만, 이것 말고 푸는 방법이 있을진 모르겠다. 나도 뭔가 이쁘고 멋져보이는 쿼리를 쓰고 싶지만, CASE WHEN으로 무식하게 때려박는 게 가장 좋은 방법이라고 판단했다.
MEMBER_PROFILE 테이블에서 생일이 3월인 여성 회원의 ID, 이름, 성별, 생년월일을 조회하는 SQL문을 작성해주세요. 이때 전화번호가 NULL인 경우는 출력대상에서 제외시켜 주시고, 결과는 회원ID를 기준으로 오름차순 정렬해주세요.
SELECT MEMBER_ID, MEMBER_NAME, GENDER, DATE_FORMAT(DATE_OF_BIRTH,('%Y-%m-%d')) as DATE_OF_BIRTH
FROM
(
SELECT *
FROM MEMBER_PROFILE
WHERE MONTH(DATE_OF_BIRTH) = '3' and GENDER = 'W'
) as a
WHERE TLNO is not NULL
ORDER BY MEMBER_ID ASC
개인적으로 문제를 풀 때 조건의 중요도 단계를 나눠서 쿼리를 작성하는 편인 것 같다. 이번 문제의 경우,
중요 조건 :
- 생일이 3월
- 여성 회원
부차적인 조건- 전화번호가 NULL인 경우 출력대상에서 제외
- 회원ID 기준 오름차순 정렬
이런 식이다. 그리고 중요 조건은 모두 인라인뷰 서브쿼리로 넣어버리면 필요한 조건들만 담긴 임시 테이블이 완성이 되니, 부차조건을 바깥 쿼리에서 걸어주면 깔끔하게 문제가 풀린다. 다들 서브쿼리가 복잡하고 번거롭다고 얘기를 하지만, 개인적으로는 생각하기가 더 편해서 좋다.
CAR_RENTAL_COMPANY_CAR 테이블과 CAR_RENTAL_COMPANY_RENTAL_HISTORY 테이블에서 자동차 종류가 '세단'인 자동차들 중 10월에 대여를 시작한 기록이 있는 자동차 ID 리스트를 출력하는 SQL문을 작성해주세요. 자동차 ID 리스트는 중복이 없어야 하며, 자동차 ID를 기준으로 내림차순 정렬해주세요.
SELECT a.CAR_ID
FROM
(
SELECT CAR_ID, CAR_TYPE
FROM CAR_RENTAL_COMPANY_CAR
WHERE CAR_TYPE = '세단'
) as a
JOIN
(
SELECT CAR_ID, START_DATE
FROM CAR_RENTAL_COMPANY_RENTAL_HISTORY
WHERE MONTH(START_DATE) = '10'
) as b
on a.CAR_ID=b.CAR_ID
group by CAR_ID
ORDER BY CAR_ID DESC
이 문제에서 중요하게 생각한 것은 종류가 세단인 것, 그리고 10월 대여를 시작한 기록이라는 두 조건을 언제 어떻게 걸어줄지였다. JOIN문으로 묶을 때 각각에 걸어주면 동등한 조건 적용 테이블이기 때문에 결합이 된다고 배운 점을 활용하여 조인을 적용했다. 그 후엔 부차적인 조건들만 걸어주면 되는 문제였다.
다만 JOIN, LEFT JOIN, INNER JOIN, OUTER JOIN의 차이를 설명하라고 하면 난 여전히 명확하게 설명이 안된다. 이 점은 복습을 하거나, 추가적인 개념 탐구가 필요하다.
쿼리 테스트가 오늘부터 시작됐다. 두 문제로, 결과는 하나는 맞고 하나는 틀렸다. 맞은 건 난이도가 낮았고, 틀린 건 난이도가 높았다. 명백히 '패배'다.
select count(EMAIL_PROMOTION) customer_count
from
(
SELECT person_type, email_promotion
FROM person
WHERE person_type = 'IN'
) as a
WHERE EMAIL_PROMOTION >0
1번 문제는 조건을 빼먹는 게 틀릴 가장 큰 이유라고 생각한다. 조건이 많았기 때문에 각 조건 적용을 잊어버리거나, 심지어는 조건에 알맞는 괄호 사용 등이 햇깔린다면 어떻게든 돌파구를 찾아내야 했다.
예를 들어 email_promotion의 경우는 상태값이 0부터 5인가 하는 숫자 단위였다. 따라서 0이 아니어야 하는 조건을 <>0, !=0과 같은 방식이 아닌, 내가 쓴 부등호 사용도 동일하게 적용이 된다. 하지만 만약 상태값이 문자형이었다면, 난 얄짤없이 틀렸을 것이다. 이 점을 반성한다.
select e.customer_id, f.first_name, f.last_name, e.total_quantity
FROM
(
select customer_id, sum(order_qty) total_quantity, order_date
FROM
(
select sales_order_id, customer_id, status, order_date
from sales_order_header
) as a
LEFT outer join
(
select sales_order_id, order_qty
from sales_order_detail
) as b
on a.sales_order_id=b.sales_order_id
where a.status < 6 and date_format(a.order_date,('%Y-%m'))='2011-10'
GROUP BY CUSTOMER_ID
having total_quantity >=70
) as e
inner join
(
select c.customer_id, d.first_name, d.last_name
from
(
select customer_id, person_id
from sales_customer
) as c
left outer join
(
select business_entity_id, first_name, last_name
from person
) as d
on c.person_id=d.business_entity_id
) as f
on e.customer_id=f.customer_id
order by customer_id ASC
총 4개의 테이블이 제공되었고, 각각 조인을 할 수 있는 PK, 즉 키 컬럼이 있었다. 따라서 결합을 한다는 접근, 그리고 공통 컬럼을 잡은 것에는 문제가 없어 보인다. 아마 이건 잔실수 때문에 틀린 것이 분명하다. 다만 아직도 왜 틀렸는지 정확히는 알 수 없으니, 좀 더 코드를 면밀히 들여다보고, 튜터님께 질문을 드려야겠다.
오늘 잡은 개념은 자료형이었다. 파이썬에서 사용하는 자료형은 대표적으로 리스트, 튜플, 딕셔너리가 있다. 각각은 서로 다른 형태와 성격, 조건을 지니고 있으니 하나씩 살펴보도록 하자.
리스트는 서로 다른 자료들이 순서를 갖고 모여있는 자료형이다. 기호는 대괄호 '[]'으로 표기된다. 예를 들면
a = [1, 5, 2]
fromis9 = ['지선', '서연', '지원', '하영']
이런 식이다. 순서에 따른 자릿값이 존재하기 때문에, 만약 자료를 꺼내고 싶을 경우,
print(fromis9[1])
-> '서연'
같은 식으로 인덱싱을 진행하면 된다. 리스트는 자유로운 자료의 추가와 삭제, 변경이 가능하다는 점이 특징이다. 또한 자료들끼리의 중복도 허용이 된다. 이같은 특징 때문에 순서가 중요한 데이터를 다룰 때 주로 사용되며, 대표적으로 쇼핑 리스트나 할 일 목록 처럼 항목의 변경이 유연해야하는 경우 유용하다.
튜플은 리스트와는 정 반대의 개념이다. 기호로는 소괄호 '()'으로 표기한다. 튜플의 정체성은 불변성(immutable)이다. 튜플에 포함된 자료는 변경하거나 추가, 삭제가 불가능하다. 이는 데이터를 보호해야 하는 상황에서 유용하기 때문에, 고객 정보나 주소, 계좌번호와 같이 보안이 필요한 자료에 주로 사용한다.
예를 들면
fromis9 = ('서연', '지선', '지원', '하영')
print(fromis9[2]) # 세 번째 요소 출력 → 지원
numbers[2] = '규리' # ❌ 오류 발생 (튜플은 값을 변경할 수 없음ㅠㅠ)
다음과 같은 식으로 사용한다. 다만 다음과 같은 것은 가능하다.
> a_fromis = [('서연','00'),('지선','98'),('하영','97')]
이는 곧, 리스트의 하위 집합으로 튜플이 포함될 수 있음을 의미한다. 튜플에 담긴 개인 정보들은 보안이 중요하지만, 고객 정보를 모아놓는 집합에는 신규 회원 정보의 추가, 혹은 기존 회원이 탈퇴 등의 이유로 정보의 인앤아웃이 필요하기 때문이다. 따라서 리스트로 모집단을 만들어 소집합 튜플의 이동은 가능하되, 튜플에 담긴 자료 자체는 수정을 할 수 없게 만드는 것이다.
리스트와 튜플이 서로 대조적인 특성을 가지면서도 융합이 가능하다면, 딕셔너리는 둘과는 다른 성격을 가진 자료형이다. 딕셔너리는 key, 키와 value, 값을 한 쌍으로 저장하는 자료형이다. 표기는 중괄호, {}로 한다.
키와 값은 서로 1:1 대응하기 때문에, value는 중복이 가능하지만, key는 자료의 객체를 결정하는 고유의 값이므로 중복이 불가능하다. 예시를 함께 보자.
코드를 입력하세요
QUEEN = {"보컬" : "프레디 머큐리",
"베이스" : "존 디콘",
"기타" : "브라이언 메이",
"드럼" : '로저 테일러"}
이렇게 되어있을 경우,
print(queen{"보컬"}) => "프레디 머큐리"
print{QUEEN} => "보컬" : "프레디 머큐리", "베이스" : "존 디콘","기타" : "브라이언 메이","드럼" : '로저 테일러"
이런 식으로 작동한다. 상대적으로 가벼운 키를 입력하면 그에 대응하는, 다양한 정보가 담긴 Value 값을 주면서 정보의 추가와 삭제, 변경이 용이하다는 점에서 회원 정보 관리나 제품 정보 등 정보 제공이 필요한 분야에서 많이 사용된다.
자료형에 담긴 자료들을 수정하거나 인덱싱하는 다양한 명령어인 메서드도 존재하지만, 그걸 여기서 다 다루면 글이 너무 길어지므로, 생략하도록 하자.
한건 별로 없고, 그것도 성에 차지는 않지만, 그럼에도 못난 나를 사랑한다. 그건 아주 작은 부분이라도 이해하지 않고는 못 배기는 내 천성이 나로하여금 모르는 것 없이 꼭꼭 십어먹고 넘어가게 하기 때문이다. 조금의 복습만 한다면 어떻게든 코드를 써내고마는 끊기가 나에겐 있다.
고로 다음주부터 다시 나를 사랑하며 신나게 달려보려고 한다. 대준아 힘내자~