我有两个要尝试JOIN
的表。
dbo.Users
看起来像这样:
UserID
------
24525
5425
7676
dbo.TelemarketingCallAudits
看起来像这样(日期格式dd / mm / yyyy):
UserID Date CampaignID
------ ---------- ----------
24525 21/01/2018 1
24525 26/08/2018 1
24525 17/02/2018 1
24525 12/01/2017 2
5425 22/01/2018 1
7676 16/11/2017 2
我想返回一个表,其中包含仅在至少30天前(如果CampaignID
= 1)和至少70天前(如果CampaignID
= 2)调用的用户。
最终结果应如下所示(今天是18/02/09):
UserID Date CampaignID
------ ---------- ----------
5425 22/01/2018 1
7676 16/11/2017 2
我尝试了这个简单的AND / OR条件,然后发现它仍然会返回我不希望看到的用户,因为他们确实有表明其他调用的行,而只是忽略了条件调用...显然错过了目标
如果用户在第二张表中的任何相关行都不满足该条件,我不知道如何调整用户的整体外观。
AND
(
internal_TelemarketingCallAudits.CallAuditID IS NULL --No telemarketing calls is fine
OR
(
internal_TelemarketingCallAudits.CampaignID = 1 --Campaign 1
AND
DATEADD(dd, 75, MAX(internal_TelemarketingCallAudits.Date)) < GETDATE() --Last call occured at least 10 days ago
)
OR
(
internal_TelemarketingCallAudits.CampaignID != 1 --Other campaigns
AND
DATEADD(dd, 10, MAX(internal_TelemarketingCallAudits.Date)) < GETDATE() --Last call occured at least 10 days ago
)
)
非常感谢您的帮助。
答案 0 :(得分:2)
尝试一下:SQL Fiddle
select *
from dbo.Users u
inner join ( --get the most recent call per user (taking into account different campaign timescales)
select tca.UserId
, tca.CampaignId
, tca.[Date]
, case when DateAdd(Day,c.DaysSinceLastCall, tca.[Date]) > getutcdate() then 1 else 0 end LastCalledInWindow
, row_number() over (partition by tca.UserId order by case when DateAdd(Day,c.DaysSinceLastCall, tca.[Date]) > getutcdate() then 1 else 0 end desc, tca.[Date] desc) r
from dbo.TelemarketingCallAudits tca
inner join (
values (1, 60)
, (2, 70)
) c (CampaignId, DaysSinceLastCall)
on tca.CampaignId = c.CampaignId
) mrc
on mrc.UserId = u.UserId
and mrc.r = 1 --only accept the most recent call
and mrc.LastCalledInWindow = 0 --only include if they haven't been contacted in the last x days
我不在这里比较所有行;而是看到您对最近的通话时间感兴趣;那么您只需关心是否在X天窗口中即可。由于X天因广告系列而异,因此还会有一些额外的复杂性。因此,它不是您最关心的最近呼叫,而是最可能属于该窗口的呼叫。为了解决这个问题,我将每个用户的呼叫按窗口中的呼叫顺序排序,然后再按非用户的呼叫顺序排序;然后按这两个组中的最新优先顺序进行排序。这给了我r
字段。
通过在r = 1
上为每个用户进行过滤,我们只会得到最近的通话(针对广告系列窗口进行了调整)。通过过滤LastCalledInWindow = 0
,我们排除了在广告系列窗口内被呼叫的人。
注意:我使用了内部查询(别名为c
)来保存广告系列ID及其对应的窗口。实际上,您可能想要一个包含相同信息的Campaigns表,而不是在查询内部进行编码。
希望其他所有事情都是不言自明的;但是如果您需要任何其他信息,请在评论中稍加提及。
刚刚意识到您还说过“没有电话就可以了” ...这是一个经过调整的版本,可用于未呼叫此人的情况。
select *
from dbo.Users u
left outer join ( --get the most recent call per user (taking into account different campaign timescales)
select tca.UserId
, tca.CampaignId
, tca.[Date]
, case when DateAdd(Day,c.DaysSinceLastCall, tca.[Date]) > getutcdate() then 1 else 0 end LastCalledInWindow
, row_number() over (partition by tca.UserId order by case when DateAdd(Day,c.DaysSinceLastCall, tca.[Date]) > getutcdate() then 1 else 0 end desc, tca.[Date] desc) r
from dbo.TelemarketingCallAudits tca
inner join (
values (1, 60)
, (2, 70)
) c (CampaignId, DaysSinceLastCall)
on tca.CampaignId = c.CampaignId
) mrc
on mrc.UserId = u.UserId
where
(
mrc.r = 1 --only accept the most recent call
and mrc.LastCalledInWindow = 0 --only include if they haven't been contacted in the last x days
)
or mrc.r is null --no calls at all
要包含默认值,您可以执行以下代码(SQL Fiddle Example)。在这里,我将每个广告系列的偏移值都放在了Campaigns
表中,但是创建了一个默认的广告系列,其中ID = -1
用于处理未定义偏移的任何内容。我在审核表和活动表之间使用left join
,以便我们从审核表中获取所有记录,而不管是否定义了活动,然后使用cross join
来获取默认活动。最后,我用coalesce
说“如果未定义广告系列,请使用默认广告系列”。
select *
from dbo.Users u
left outer join ( --get the most recent call per user (taking into account different campaign timescales)
select tca.UserId
, tca.CampaignId
, tca.[Date]
, case when DateAdd(Day,coalesce(c.DaysSinceLastCall,dflt.DaysSinceLastCall), tca.[Date]) > getutcdate() then 1 else 0 end LastCalledInWindow
, row_number() over (partition by tca.UserId order by case when DateAdd(Day,coalesce(c.DaysSinceLastCall,dflt.DaysSinceLastCall), tca.[Date]) > getutcdate() then 1 else 0 end desc, tca.[Date] desc) r
from dbo.TelemarketingCallAudits tca
left outer join Campaigns c
on tca.CampaignId = c.CampaignId
cross join Campaigns dflt
where dflt.CampaignId = -1
) mrc
on mrc.UserId = u.UserId
where
(
mrc.r = 1 --only accept the most recent call
and mrc.LastCalledInWindow = 0 --only include if they haven't been contacted in the last x days
)
or mrc.r is null --no calls at all
也就是说,我建议不要使用默认值,而是要确保每个广告系列都定义了偏移量。也就是说,假设您已经有一个Campaigns表;并且由于此偏移量值是针对每个广告系列定义的,因此您可以在该表中包含一个用于保存此偏移量的字段。您可以将其设置为默认值,而不是将其保留为null
来保存某些记录。因此简化了逻辑/避免了可能随后使用该值的其他地方的潜在问题。
您还询问了order by
子句。没有order by 1/0
;所以我认为这是一个错字。完整的语句是row_number() over (partition by tca.UserId order by case when DateAdd(Day,coalesce(c.DaysSinceLastCall,dflt.DaysSinceLastCall), tca.[Date]) > getutcdate() then 1 else 0 end desc, tca.[Date] desc) r
。
本文的目的是为每个用户找到“最重要的”呼叫。 “最重要的”我基本上是指最新的,因为这通常是我们所追求的。尽管有一个警告。如果某个用户属于2个广告系列的一部分,则一个偏移量为30天,一个偏移量为60天,则他们可能有2个呼叫,一个是32天前,另一个是38天前。尽管32天前的致电是较新的致电,但如果广告系列中有30天的偏移,则不在窗口内,而38天前的较早致电是在广告系列中,偏移为60天,这意味着窗口,因此会引起更多关注(即,已在广告系列窗口中调用了该用户)。
鉴于上述要求,以下是此代码满足的方式:
row_number()
为(子)查询结果中的每一行从1开始计算一个数字。每个partition
partition by tca.UserId
表示我们正在按用户ID进行分区;因此,对于每个用户,将有1行row_number()
返回1,然后对于该用户,每增加一行,就会返回一个连续的数字。order by
部分定义了每个用户行中的哪一行获得#1,然后数字如何递增;即按顺序排列的第一行的编号为1,下一个编号为2,依此类推。case when DateAdd(Day,coalesce(c.DaysSinceLastCall,dflt.DaysSinceLastCall), tca.[Date]) > getutcdate() then 1 else 0 end
对于其广告系列窗口内的呼叫返回1,对于窗口外的呼叫返回0。由于我们按此结果的升序排列,也就是说,应先返回其广告系列窗口中的所有记录,然后再返回其广告系列窗口之外的任何记录。tca.[Date] desc
进行订购;也就是说,较新的通话会在以后的通话之前返回。r
,并在r = 1
的外部查询过滤器中命名;这意味着对于每个用户,我们只需要一行,根据上面的订购标准,这是第一行;即,如果广告系列的窗口中有一行,我们将采用该行,然后以最近的那个呼叫为准(如果有,则在窗口中;如果没有,则在窗口外)。看看子查询的输出,以更好地了解它的工作原理:SQL Fiddle
我希望解释有意义/可以帮助您理解代码?可悲的是,我找不到一种比代码本身更简洁的解释方式。因此,如果没有任何意义,请尝试使用代码并查看其如何影响输出,以帮助您理解。