SQL Server查询优化:排除已预约新时段的久未到店活跃客户
解决方案
你的原查询存在两个核心问题:
- 使用
LEFT JOIN后在WHERE子句中过滤APPOINTMENT.APP_DATE < 6个月前,会把从未有过预约的活跃客户排除(这类客户的APP_DATE为NULL,无法满足<条件)。 - 未添加过滤有
BOOKED状态预约客户的逻辑。
以下分两种场景给出适配的查询语句:
场景1:排除所有有过BOOKED状态预约的活跃客户
无论预约时间,只要客户曾经有过状态为BOOKED的预约,就从结果中排除:
SELECT CONCAT(c.CLIENT_FNAME, ' ', c.CLIENT_LNAME) AS Name, MAX(a.APP_DATE) AS 'Last Appointment' FROM CLIENT c LEFT JOIN APPOINTMENT a ON c.CLIENT_ID = a.CLIENT_ID WHERE c.CLIENT_STATUS = 'ACTIVE' GROUP BY c.CLIENT_ID, c.CLIENT_FNAME, c.CLIENT_LNAME HAVING (MAX(a.APP_DATE) < DATEADD(MONTH, -6, GETDATE()) OR MAX(a.APP_DATE) IS NULL) AND NOT EXISTS ( SELECT 1 FROM APPOINTMENT a2 WHERE a2.CLIENT_ID = c.CLIENT_ID AND a2.APP_STATUS = 'BOOKED' )
场景2:仅排除最近6个月内有BOOKED状态预约的活跃客户
这类客户本来就不属于“6个月内未预约”的范围,只需确保查询包含从未预约的活跃客户即可:
SELECT CONCAT(c.CLIENT_FNAME, ' ', c.CLIENT_LNAME) AS Name, MAX(a.APP_DATE) AS 'Last Appointment' FROM CLIENT c LEFT JOIN APPOINTMENT a ON c.CLIENT_ID = a.CLIENT_ID WHERE c.CLIENT_STATUS = 'ACTIVE' GROUP BY c.CLIENT_ID, c.CLIENT_FNAME, c.CLIENT_LNAME HAVING MAX(a.APP_DATE) < DATEADD(MONTH, -6, GETDATE()) OR MAX(a.APP_DATE) IS NULL
另一种更简洁的写法(用NOT EXISTS)
如果想避免分组带来的复杂度,也可以用NOT EXISTS实现,逻辑更直观:
SELECT CONCAT(CLIENT_FNAME, ' ', CLIENT_LNAME) AS Name, (SELECT MAX(APP_DATE) FROM APPOINTMENT a WHERE a.CLIENT_ID = c.CLIENT_ID) AS 'Last Appointment' FROM CLIENT c WHERE c.CLIENT_STATUS = 'ACTIVE' -- 筛选最近6个月无任何预约的客户 AND NOT EXISTS ( SELECT 1 FROM APPOINTMENT a WHERE a.CLIENT_ID = c.CLIENT_ID AND a.APP_DATE >= DATEADD(MONTH, -6, GETDATE()) ) -- 如需排除所有有过BOOKED预约的客户,保留此段;否则删除 AND NOT EXISTS ( SELECT 1 FROM APPOINTMENT a WHERE a.CLIENT_ID = c.CLIENT_ID AND a.APP_STATUS = 'BOOKED' )
关键说明
- 不需要使用
DELETE语句,仅通过查询条件就能过滤掉不符合要求的客户,不会修改任何数据。 MAX(a.APP_DATE)用于获取每个客户的最后一次预约时间,NULL代表该客户从未有过预约。NOT EXISTS是高效的筛选方式,避免了JOIN可能带来的重复数据问题。
内容的提问来源于stack exchange,提问作者locostwilly
相关产品推荐
相关产品推荐

