订阅更新

平均通话时长高于全球平均的国家

SQL 实战 123 题 · 第 117 题 中等 约 3 分钟读完

LeetCode 1501 · 双向展开、字符串截取关联、和全局平均比较

本文目录
  1. 面试怎么回答
  2. 题意
  3. 表结构
  4. 思路
  5. 参考解法
  6. 易错点

面试怎么回答

把通话按主叫和被叫 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
    )

易错点

  • 只按主叫算国家,被叫方所在的国家就漏掉了。
  • 同一国家的两人互相打电话,这次通话要为该国算两次,这正是题目的定义。
刷题进度保存在当前浏览器