订阅更新

顾客的可信联系人数量:按邮箱判断

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

LeetCode 1364 · 多次 LEFT JOIN、条件计数、看清定义

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

面试怎么回答

我会先在联系人表上按顾客汇总:联系人 LEFT JOIN 顾客表(按邮箱),COUNT(*) 是联系人数,COUNT(匹配到的顾客 ID) 是可信联系人数;再用发票表关联顾客姓名和这份汇总,IFNULL 补 0。这题的坑在定义:可信是按邮箱判断,不能按姓名。

先把这段讲顺;面试官追问时,再展开下面的原理和细节。

详细讲解

题意

顾客表(姓名、邮箱)、联系人表(顾客的联系人姓名和邮箱)、发票表(发票属于哪个顾客、金额)。对每张发票输出:顾客姓名、金额、该顾客的联系人数,以及可信联系人数——可信联系人是指邮箱出现在顾客表里的联系人。按发票号排序。

表结构

  • Customers:customer_id(主键)、customer_name、email
  • Contacts:user_id、contact_name、contact_email;(user_id, contact_email) 为主键
  • Invoices:invoice_id(主键)、price、user_id

思路

以发票为主表,关联顾客拿姓名;再关联「每个顾客的联系人统计」:联系人数直接计数,可信联系人数是联系人邮箱能在顾客表里找到的个数。用一次 LEFT JOIN 顾客表(按邮箱)加条件计数,就能同时算出两个数。注意题目是按「邮箱」定义可信,原解法用了姓名匹配,同名不同人时会算错。

参考解法

select invoice_id ,customer_name,price,ifnull(cnt,0) contacts_cnt,ifnull(bc,0) trusted_contacts_cnt 
from Invoices i
left join (
select user_id ,count(*) cnt
from Contacts
group by user_id
) t1
on i.user_id=t1.user_id
left join (
select  user_id ,count(*) bc
from Contacts
    where contact_name in
    (
        select customer_name
        from Customers
    )
group by user_id 
)t2
on i.user_id = t2.user_id
left join Customers c
on i.user_id= c.customer_id
order by invoice_id

另一种写法:按邮箱判断可信联系人,一次关联算两个计数

SELECT i.invoice_id, c.customer_name, i.price,
       IFNULL(t.contacts_cnt, 0) AS contacts_cnt,
       IFNULL(t.trusted_cnt, 0) AS trusted_contacts_cnt
FROM Invoices i
JOIN Customers c ON i.user_id = c.customer_id
LEFT JOIN (
  SELECT ct.user_id, COUNT(*) AS contacts_cnt, COUNT(cu.customer_id) AS trusted_cnt
  FROM Contacts ct
  LEFT JOIN Customers cu ON ct.contact_email = cu.email
  GROUP BY ct.user_id
) t ON i.user_id = t.user_id
ORDER BY i.invoice_id

易错点

  • 按姓名匹配可信联系人是错的,定义里用的是邮箱。
  • 没有联系人的顾客,两个计数都要显示 0。
刷题进度保存在当前浏览器