본문 바로가기

SQL/DataLemar

[PostgreSQL] (Medium) International Call Percentage [Verizon SQL Interview Question]

문제: 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;

흠흠,, 마이에스큐엘에익숙해서 필터를 생각 못 했다는 변명이 무색하게 케이스로도 풀 수 있음 (당연)

아주.. 쉽고... 깔끔하군... !!

다음엔 생각이 날거야~~