平均通话时长高于全球平均的国家
LeetCode 1501 · 双向展开、字符串截取关联、和全局平均比较
面试怎么回答
把通话按主叫和被叫 UNION ALL 展开成(人, 时长),关联人员表取电话前三位作为国家码,再关联国家名,按国家 AVG(duration),HAVING 大于全表的 AVG(duration)。一次通话属于两个参与者,展开后每一端各算一次,和题目定义一致。
先把这段讲顺;面试官追问时,再展开下面的原理和细节。
详细讲解
题意
人员表记录每人的电话号码(前 3 位是国家码),国家表记录国家码和国家名,通话表记录主叫、被叫和时长。一个国家的平均通话时长 = 该国的人参与的所有通话(主叫或被叫)的平均时长。找出平均时长严格大于全球平均时长的国家。
表结构
- Person:id(主键)、name、phone_number(格式 xxx-yyyyyyy)
- Country:country_code(主键)、name
- Calls:caller_id、callee_id、duration;没有主键
思路
一次通话涉及两个人,可能属于两个国家,每个国家都要算上这次通话。所以先把通话展开成「主叫 + 时长」和「被叫 + 时长」两份(UNION ALL),关联人员拿到国家码(电话号码前三位),再按国家求平均,和全局平均比较。全局平均直接对通话表求即可(展开后每通电话恰好出现两次,平均值不变)。
参考解法
写法 1:用 OR 条件关联主叫或被叫
select c2.name as country
from Calls c1,Person p,Country c2
where (p.id=c1.caller_id or p.id=c1.callee_id) and c2.country_code=left(p.phone_number,3)
group by c2.name
having avg(duration)>(select avg(duration) from Calls)
写法 2:先展开主叫、被叫两列,思路更清晰
with people_country as
(
select id, c.name country
from Person p left join Country c
on left(p.phone_number,3) = c.country_code
)
select country
from
(
select country, avg(duration) avgtime
from
(
select caller_id id, duration
from Calls
union all
select callee_id, duration
from Calls
) t left join people_country
using(id)
group by country
) temp
where avgtime >
(
select avg(duration) avgtime
from
(
select caller_id, duration
from Calls
union all
select callee_id, duration
from Calls
) t
)
易错点
- 只按主叫算国家,被叫方所在的国家就漏掉了。
- 同一国家的两人互相打电话,这次通话要为该国算两次,这正是题目的定义。