
짧고도 길었던 SQL 학습이 끝났다. 참 속시원하다. 한 편으로는 '아직 한참 배워야 하는데 벌써 끝났다고?' 이런 생각이 계속해서 떠오른다.
그래도 다행인 점은 코드카타를 진행하면서 SQL에 대한 감각을 떨어트리지 않고 유지할 수 있다는 점이다. 앞으로 파이썬을 집중해서 학습할 텐데, 배운 내용을 잘 갈무리해서 SQL과 비슷한 수준까지는 올려야겠다.
오늘 학습 키워드
파이썬 - 함수 구조 및 변수 작성에 적응하기
SQL - join문 마지막 문제 풀기
오늘 학습한 내용을 나만의 언어로 정리하기
파이썬에서는 함수를 작성해 코드를 실행시키는 시도를 해보았다. 몇 가지 간단한 예시를 작성해서 각 함수가 어떻게 작동하는지 확인해보는 과정이었다.
SQL에서는 어제 풀던 문제를 마저 풀어냈다. 하루 전에는 문제가 도저히 풀릴 각이 보이지 않았었다. 그런데 시간이 흐르고 오늘 다시 보니 어찌저찌 쿼리가 작성되는 것이 아닌가? - 이런 경험은 언제 겪어도 참 신기하다. - 어찌 되었든, 문제를 풀어낼 수 있었다는 사실에 감격스러웠다.
학습 내용
파이썬은 함수에 대한 이론 학습과 더불어 오늘 배운 함수들을 실제로 적용시켜서 간단한 코드를 작성해보는 실습을 진행했다.
먼저 함수의 기본형을 확인해보았다.
def 함수이름():
실행문 # -> 자주 쓰이는 코드
# 호출(call)
함수이름()
파이썬 코드의 기본형은 def를 써서 함수를 정의하는 것부터 시작한다. 그 후 내가 실행하고자 하는 내용을 변수 선언과 다양한 함수들을 활용해 출력해내는 방식이다.
다음은 기본형 함수를 사용한 코드이다.
def say_hello():
print("Hello")
say_hello()

실로 간단하지만, 역사적인 첫 걸음이라고 볼 수 있다.
이 외에도 매개변수와 인자, 반환값, 변수의 스코프, 가변 매개변수와 키워드 매개변수 등을 학습하고 이를 실제로 적용해보는 실습을 진행했다.
다음은 오늘 진행한 파이썬 코드 작성 실습문과 그 실행 결과이다.

각각을 뜯어 보면 굉장히 간단한 내용을 담고 있다. 이마저도 내가 직접 생각한 코드는 아니다. 아직 파이썬에 익숙하지 않아 이것조차도 굉장히 버벅대면서 간신히 흘겨보면서 작성할 수 있었다. 갈 길이 구만리인 것을 다시 한 번 느낄 수 있었다.
SQL은 드디어 마지막 문제를 풀어냈다.
마지막 6번 문제는 다음과 같다.
아래 조건을 만족하여, 서버별 마지막 접속일자-첫 접속일자 평균을 구해주세요.
조건1) user 테이블에서 게임계정, 첫 접속일자, 서버번호를 추출해주세요.
단, ip 주소가 70으로 시작하는 경우만 필터링해주세요.
그리고, 계정별로 첫 접속일자가 여러개인 경우는 알맞은 서브쿼리를 사용하여 계정당 가장 작은 날짜 1개만 반환해주세요.조건2) 그 다음, payment 테이블에서 게임계정별 가장 마지막 결제일자를 찾고 그 컬럼이름을
date2로 지정해주세요. 그 다음 inner join을 진행해주세요.
다만, 첫 접속일자보다 마지막 결제일자가 큰 경우만 추출해주세요.조건3) 조인 결과를 바탕으로 마지막 결제일자-첫 접속일자 를 구해주세요. 그리고 컬럼이름을 diffdate로 설정해주세요. 두 날짜의 형식은 같아야 합니다.
조건4) 인라인 뷰 subquery 를 이용하여 서버별 평균 diffdate를 구해주시고, 컬럼이름을avgdiffdate로 설정해주세요. 해당컬럼은 정수 형태로 출력되어야 합니다.
조건5) 조건절에 diffdate 값이 10일 이상인 경우를 필터링해주세요. 그리고 서버번호를 기준으로 내림차순 정렬해주세요.
힌트) 소수점을 반올림해주는 round 함수를 활용해주세요!
굉장히 길다. 일단 지문의 길이에서 압도되어 버렸다. 오늘 진행되었던 라이브 세션에서 튜터님께서 말씀하시길, 이 문제는 틀리라고 낸 문제란다. 과연 머리가 아플만 했다.
위 문제에 대한 쿼리는 다음과 같이 작성되었다.
SELECT
serverno,
round(avg(diffdate)) AS avgdiffdate
FROM
(
SELECT
a.game_account_id AS game_account_id_A,
a.first_login_date,
a.serverno,
b.game_account_id AS game_account_id_B,
b.date2,
datediff(date2, first_login_date) AS diffdate
FROM
(
SELECT
t.game_account_id,
t.first_login_date,
t.serverno
FROM
(
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY game_account_id ORDER BY first_login_date ASC) AS rown
FROM
basic.users
) AS t
WHERE
(ip_addr LIKE '70%')
AND (rown = 1)
) AS a
INNER JOIN
(
SELECT
game_account_id,
date_format(max(DISTINCT approved_at), '%Y-%m-%d') AS date2
FROM
basic.payment
GROUP BY
game_account_id
) AS b
ON
a.game_account_id = b.game_account_id
WHERE
first_login_date<date2
) AS c
WHERE
c.diffdate >= 10
GROUP BY
1
ORDER BY
1 DESC
;
쿼리의 길이가 굉장히 길다는 것을 알 수 있다. 이번 문제의 쿼리를 작성하는 데 발생한 과정을 상세히 서술하고 싶다만, 나도 이걸 어떻게 풀었는지 잘 생각이 나질 않는다. 뭔가 하다 보니 얼떨결에 풀린 느낌이다.
무엇보다, 상당히 많은 양의 텍스트를 적어야 하기 때문에 선뜻 적기가 두렵기도 하다.
그럼에도 한 가지 가장 어려웠던 점을 꼽자면 조건 5번을 맞추는 과정이었다.
모든 조건을 클리어하고 마지막 조건을 만족하기 위해 마지막으로 컬럼을 조회한 결과, "Duplicate column name"이라는 오류를 만나게 된다.
처음에는 컬럼명을 잘못 적었나 싶어서 계속해서 컬럼명을 수정하며 오류의 원인을 찾으려고 시도했다. 하지만 모든 시도가 실패로 돌아가게 되었고, 구글에 해당 문장을 검색하고 문제가 해결될 수 있었다.
"Duplicate column name"은 조회하고자 하는 테이블에 동일한 컬럼명이 복수 존재한다는 뜻이었다. 이를 해결하는 방법은 간단했다. 중복되는 컬럼명을 바꿔주기만 하면 됐다.
이 해결방법을 적용해서 "a.game_account_id AS game_account_id_A, b.game_account_id AS game_account_id_B"으로 쿼리를 수정했고, 아래의 결과를 최종적으로 얻게 되었다.

참으로 어렵고 힘들었다... 하지만 어떻게든 풀어내는 데에 성공했다!!! 뿌듯하기도 하고, 앞으로 어떤 장애물이 와도 넘어설 수 있을 듯한 자기효능감도 얻게 되었다.
마치며 : 시간이 오래 걸려도 포기하지만 않으면 결국에는 이뤄낼 수 있다는 것을 몸소 실현해낸 날이다. 정말 기쁘고, 마찬가지로 피곤하기도 하다. 오늘은 정말 일찍 잠자리에 들어서 한 숨 푹 자둬야겠다.