获取30天内未购课用户的SQL查询问题求助
解决近30天未购买课程的用户查询问题
我来帮你搞定这个SQL查询的问题!你的需求是获取近30天内未购买任何课程的用户列表,包含两类用户:从来没有过购课记录的,以及曾经购课但最近一次购课已超过30天的。你当前的查询没有得到预期结果,核心原因是逻辑上的几个小问题,咱们一步步拆解修正:
原查询的问题分析
你的原查询语句:
SELECT U.Id, U.Name, U.Email FROM [User] U LEFT JOIN [Transaction] T ON U.Id=T.UserId WHERE T.Description LIKE '%Buy Course%' GROUP BY U.Id, U.Name, U.Email HAVING MAX(T.DateAdded) <= (GETDATE()-30)
存在两个关键问题:
- 过滤掉了无购课记录的用户:LEFT JOIN后,没有任何购课交易的用户(比如user1、user5)对应的
T.Description是NULL,NULL LIKE '%Buy Course%'会返回假,导致这类用户被WHERE子句直接排除。 - 逻辑覆盖不全:HAVING子句只筛选了「最近一次购课在30天前」的用户,但完全漏掉了「从来没有购课记录」的用户。
正确的查询写法
这里提供两种简洁且符合需求的写法:
方法1:使用NOT EXISTS(推荐,逻辑直观)
SELECT U.Id, U.Name, U.Email FROM [User] U WHERE NOT EXISTS ( -- 检查该用户近30天内是否有购课交易 SELECT 1 FROM [Transaction] T WHERE T.UserId = U.Id AND T.Description LIKE '%Buy Course%' AND T.DateAdded >= DATEADD(DAY, -30, GETDATE()) )
逻辑说明:
NOT EXISTS会逐个检查每个用户,只要该用户在近30天内没有任何「Buy Course」类型的交易,就会被选中。不管用户有没有其他类型的交易(比如Add-Cash),也不管是否有历史购课记录,完全匹配你的需求。
方法2:使用LEFT JOIN + 子查询
如果你更习惯用JOIN的写法,可以先统计每个用户的最近购课日期,再关联筛选:
SELECT U.Id, U.Name, U.Email FROM [User] U LEFT JOIN ( -- 子查询:获取每个用户的最近一次购课日期 SELECT UserId, MAX(DateAdded) AS LastCoursePurchaseDate FROM [Transaction] WHERE Description LIKE '%Buy Course%' GROUP BY UserId ) T ON U.Id = T.UserId -- 筛选条件:要么无购课记录,要么最近一次购课已超过30天 WHERE T.LastCoursePurchaseDate IS NULL OR T.LastCoursePurchaseDate <= DATEADD(DAY, -30, GETDATE())
测试验证
根据你提供的测试数据:
- user1:只有Add-Cash交易,无购课记录 → 符合条件
- user2:最近购课日期是2020-04-01,距当前日期(假设为2020-05-20)已超过30天 → 符合条件
- user3:最近购课日期是2020-05-15,在30天内 → 不符合
- user4:最近购课日期是2020-05-01,在30天内 → 不符合
- user5:无任何交易记录 → 符合条件
最终返回结果正好是你预期的user1、user2、user5。
小提示
推荐使用DATEADD(DAY, -30, GETDATE())替代GETDATE()-30,虽然SQL Server支持直接对datetime类型减数字,但DATEADD的可读性更强,也能避免潜在的类型转换问题。
内容的提问来源于stack exchange,提问作者Roshan
相关产品推荐
相关产品推荐

