문제: https://datalemur.com/questions/international-call-percentage
나의 풀이:
SELECT *
FROM phone_calls c
JOIN (SELECT caller_id
, country_id AS caller_country
FROM phone_info) i1 ON c.caller_id = i1.caller_id
JOIN (SELECT caller_id AS receiver_id
, country_id AS receiver_country
FROM phone_info) i2 ON c.receiver_id = i2.receiver_id
WHERE caller_country <> receiver_country;
발신자와 수신자의 국가를 조인하고
발신자와 수신자의 국가가 다른 조건을 걸었는데
밑에서 두번째는 왜 걸러지지 않는지 모르겠다 ㅜㅜ
발신자와 수신자가 같은 조건을 걸면 여기에도 caller_id 7은 걸리지 않는다
쟤 하나만 빼고 나머지는 잘 작동하는 것 같은데 사이트 오류 아닐까 ㅎ?
일단 무시하고 계속 해봤다
WITH tb AS (
SELECT *
FROM phone_calls c
JOIN (SELECT caller_id
, country_id AS caller_country
FROM phone_info) i1 ON c.caller_id = i1.caller_id
JOIN (SELECT caller_id AS receiver_id
, country_id AS receiver_country
FROM phone_info) i2 ON c.receiver_id = i2.receiver_id
WHERE caller_country != receiver_country)
SELECT ROUND(COUNT(*) :: decimal / (SELECT COUNT(*) FROM phone_calls c2) :: decimal * 100, 1) AS international_calls_pct
FROM tb;
근데 됐음 ㅋㅋ! (?)
왜인지는 모르겠지만 정답처리는 된...
근데 쿼리가 가독성이 너무 떨어진다
그리고 타입캐스팅도.. 번거로움 ㅠ
솔루션을 확인해보자
WITH international_calls AS (
SELECT
caller.caller_id,
caller.country_id,
receiver.caller_id,
receiver.country_id
FROM phone_calls AS calls
LEFT JOIN phone_info AS caller
ON calls.caller_id = caller.caller_id
LEFT JOIN phone_info AS receiver
ON calls.receiver_id = receiver.caller_id
WHERE caller.country_id <> receiver.country_id
)
SELECT
ROUND(
100.0 * COUNT(*)
/ (SELECT COUNT(*) FROM phone_calls),1) AS international_call_pct
FROM international_calls;
내 풀이랑 결은 비슷한데 위드문이 더 깔끔하다
이걸 보니까 나도 이렇게 할 껄 싶다 ㅋㅋ
굳이 조인에서 select하지말고 조인만 한 다음에 전체 select에서 필요한 컬럼만 호출하니 훨씬 깔끔!
그리고 더 대박인거!!
바로 100.0 을 앞에서 곱하면 :: decimal 이라고 캐스팅을 안 해줘도 된다
뒤에도 안 되고 꼭 앞에 붙여야 함!
100.0 을 앞에서 곱하면 뒷 부분도 같은 타입으로 인식이 되나..?
100을 곱하면 int로 인식되는 것 같고 100.0은 decimal로 인식되는 것 같다
또다른 풀이 방법으로는..
1. 필터 쓰기
SELECT
ROUND(
100.0 * COUNT(*) FILTER (
WHERE caller.country_id <> receiver.country_id)
/ COUNT(*), 1) AS international_calls_pct
FROM phone_calls AS calls
LEFT JOIN phone_info AS caller
ON calls.caller_id = caller.caller_id
LEFT JOIN phone_info AS receiver
ON calls.receiver_id = receiver.caller_id;
더 깔끔해짐
필터.. 아주 좋은 기능이였구나 손에 안 익어서 쓸 생각이 안 났다
좋은데..?
2. 케이스 쓰기
SELECT
ROUND(
100.0 * SUM(CASE
WHEN caller.country_id <> receiver.country_id THEN 1 ELSE NULL END)
/COUNT(*) ,1) AS international_call_pct
FROM phone_calls AS calls
LEFT JOIN phone_info AS caller
ON calls.caller_id = caller.caller_id
LEFT JOIN phone_info AS receiver
ON calls.receiver_id = receiver.caller_id;
흠흠,, 마이에스큐엘에익숙해서 필터를 생각 못 했다는 변명이 무색하게 케이스로도 풀 수 있음 (당연)
아주.. 쉽고... 깔끔하군... !!
다음엔 생각이 날거야~~