重构经历(一)

 

一、基本需求

在当前的项目中,很多用户都有订阅论坛中不同的类别,程序需要根据用户订阅的触发时间来向用户发送邮件。

在这里涉及到几个表。

WebSites包含论坛的URL, LogoURL, Status, Description等信息
Campaigns 和WebSite相关,一般情况下一个WebSite只对应一个Campaign,它也包含一个Status
Subscribers 订阅用户,包含Email, FirstName, LastName等订户信息
Subscriptions 订阅用户所订阅的类别,包含类别,类别所在的论坛,订阅时间,接收邮件的具体时间(时分秒),接收周期(WeekInterval varchar(7) 值可能是0123456这个字符串中的任意组合,如023表示用户将在周日,周三,周四收到邮件)。

实际上Subscribers和Subscriptions也都包含Status字段,有些信息是不能够通过邮件来发送的或者用户退订了。

 

二、实现过程

之前由于不太明确需求,我把业务逻辑的主次给颠倒了,我是根据当前时间去查询Subscriptions表,看有哪些订阅者,这些订阅者又分别属于哪个WebSite,这是一个从下往上找的过程。

Subscriptions –> Subscribers –> Campaigns / WebSites

但是后来醒悟过来,如果WebSite的Status为"D"(Disabled),那么这个站点下的所有用户将在一段时间内收不到邮件,知道Status转变为"A"(Active)。

现在转变过来了,则应当是:WebSites –> Campaigns –> Subscribers –> Subscriptions。

之前的WebService部分和存储过程在现在这种情况下都要改动。

 

三、重构过程

(1) 第一次重构

之前为了测试方便,把方法分的很开,可笑的对Campaign写了3个WebMethod,GetCampaign()、IsCampaignExists()、CreateCampaign(),甚至每个方法都会调用一个简短的StoredProcedure。其实我的目的仅仅是为了获取一个Campaign而已,至于IsCampaignExists()、CreateCampaign()这根本和功能无关紧要。于是我考虑三个存储过程合而为一,三个WebMethod也合而为一。改成GetCampaign()

于是,我改了方法,改了存储过程,自以为代码量减少了,业务逻辑也符合了,但是却忽略了Campaign和WebSite的Status

(2)第二次重构

由于加入了Status,所以至上而下的找,有缝缝补补,更为GetActiveCampaign()

(3)第三次重构

到现在我才真正明白这个需求,我其实是为了拿取当前时间下,状态处于Active,并且有订阅用户将会接收到邮件的WebSite,其他的Campaign并不是最顶层的。

而且第二次重构的结果导致我只能拿取一条数据,这样则会造成多次访问数据库,我还是索性的一次拿光吧。最终只有GetActiveWebSites()

四、SQL重构

(1)第一次重构

获取当前有订户需要接收邮件的WebSite

create proc [dbo].[SP_GetCurrentWebsites]
(
@weekday int
)
as
declare @now datetime
declare @now_time time

set @now = GETDATE()
set @now_time = @now
select distinct Subscribers.WebSiteID from Subscribers 
inner join Subscriptions
on Subscribers.ID = Subscriptions.SubscriberID
where  WeekDays = @weekday
and StartTime < @now_time

GO

获取处于激活状态的WebSite

create proc [dbo].[SP_GetActiveCampaigns]
as
select Campaigns.* from Campaigns 
inner join WebSites
on Campaigns.WebSiteID = WebSites.ID and WebSites.Status = 'A' and Campaigns.Status = 'A'
GO

然后获取最终的WebSite的方法我是放到程序里进行处理的,写好了,发现这让我感觉很别扭,总觉得多余,为了获取WebSite我竟然花了3个步骤。

(2)第二次重构

简化为一个存储过程,一个WebMethod调用。

--获取WebSite
create proc SP_GetActiveWebSites
(
@weekday varchar(1)
)
as
declare @now datetime
declare @now_time time
set @now = GETDATE()
set @now_time = @now
select w.ID from 
(
select distinct s1.WebSiteID from Subscribers s1
inner join Subscriptions s2 on s1.ID = s2.SubscriberID
where  CHARINDEX(@weekday,s2.WeekDays) > 0
and StartTime < @now_time
) t
inner join WebSites w on t.WebSiteID = w.ID
inner join Campaigns c on c.WebSiteID = w.ID
where w.Status = 'A' and c.Status = 'A'
go

--执行查询
declare @start datetime
set @start = GETDATE()
exec SP_GetActiveWebSites '1'
select DATEDIFF(MILLISECOND, @start, GETDATE()) as QueryTime

为了获取几条数据,这个查询花了4000多毫秒,由于Subscriptions和Subscribers表数据量较大,这几个表都inner join会花费很多时间。

(3)第三次重构

alter proc SP_GetActiveWebSites
(
@weekday varchar(1)
)
as
declare @now datetime
declare @now_time time

set @now = GETDATE()
set @now_time = @now
--临时表1
create table #Temp1(ID bigint, WebSiteID bigint)
insert into #Temp1
select Campaigns.ID, Campaigns.WebSiteID from (Campaigns 
inner join WebSites
on Campaigns.WebSiteID = WebSites.ID and WebSites.Status = 'A' and Campaigns.Status = 'A' )
--临时表2
create table #Temp2(WebSiteID bigint)
insert into #Temp2 
select distinct s1.WebSiteID from Subscribers s1
inner join Subscriptions s2
on s1.ID = s2.SubscriberID
where  WeekDays = @weekday
and StartTime < @now_time

--临时表1,表二联合查询
select t1.ID, t1.WebSiteID from #Temp1 t1
inner join #Temp2 t2 on t1.WebSiteID = t2.WebSiteID 
go

--执行查询
declare @start datetime
set @start = GETDATE()
exec SP_GetActiveWebSites '1'
select DATEDIFF(MS,@start,GETDATE())
最终这个查询只需要500毫秒左右,重构完成。
posted @ 2010-09-19 16:11  Sunny Peng  阅读(1754)  评论(1编辑  收藏  举报