请写出一条 SQL 语句,从访问记录表中筛选出在 2024 年 7 月 1 日当天访问过 '/home' 页面,但未曾访问过 '/contact' 页面的用户的用户名,并且要求结果中不包含重复的用户名。
考察说明
考查对 SQL 查询中集合操作、子查询以及去重的掌握,特别是处理“存在”与“不存在”条件的逻辑。
回答思路
- 【回答框架 1】核心思路是利用集合差或反连接:先找到访问过 '/home' 的用户集合,再排除其中也访问过 '/contact' 的用户。推荐使用 NOT IN 或 NOT EXISTS 子查询实现,因为 NOT EXISTS 对 NULL 更安全。
- 【回答框架 2】基本写法:SELECT DISTINCT user_name FROM visits WHERE page = '/home' AND visit_date = '2024-07-01' AND user_name NOT IN (SELECT user_name FROM visits WHERE page = '/contact' AND visit_date = '2024-07-01'); 注意 NOT IN 子查询中若出现 NULL 会导致整个查询结果为空,因此需确保 user_name 非空或使用 NOT EXISTS。
- 【回答框架 3】使用 NOT EXISTS 的写法:SELECT DISTINCT user_name FROM visits v1 WHERE page = '/home' AND visit_date = '2024-07-01' AND NOT EXISTS (SELECT 1 FROM visits v2 WHERE v2.user_name = v1.user_name AND v2.page = '/contact' AND v2.visit_date = '2024-07-01'); 这种方法能正确处理 NULL,并且通常性能更好。
- 【回答框架 4】也可以使用 LEFT JOIN 加 IS NULL 实现:SELECT DISTINCT v1.user_name FROM visits v1 LEFT JOIN visits v2 ON v1.user_name = v2.user_name AND v2.page = '/contact' AND v2.visit_date = '2024-07-01' WHERE v1.page = '/home' AND v1.visit_date = '2024-07-01' AND v2.user_name IS NULL; 注意去重和连接条件。
- 【回答框架 5】日期过滤建议使用 DATE 类型或日期范围,避免时间戳干扰;若表有索引,应确保在 where 条件中直接使用列比较,避免函数包裹导致索引失效。
- 【关键点 1】使用 NOT EXISTS 或 LEFT JOIN ... IS NULL 比 NOT IN 更安全,避免 NULL 值问题。
- 【关键点 2】务必使用 DISTINCT 去除重复用户名,因同一用户可能多次访问同一页面。
- 【关键点 3】日期条件应精确匹配 2024-07-01,若字段含时间需使用日期函数或范围条件。
- 【易错点 1】直接使用 NOT IN 时子查询结果含有 NULL 会导致最终结果为空。
- 【易错点 2】未加 DISTINCT 或 GROUP BY 可能产生重复用户名。
- 【易错点 3】若日期字段带时间部分,直接用 '=' 比较可能匹配不到数据,需转为日期或使用 BETWEEN。