Sql server 2012 在SQL Server中用基于集合的方法替换while循环

Sql server 2012 在SQL Server中用基于集合的方法替换while循环,sql-server-2012,Sql Server 2012,我有一个表格,显示每天的提供商和活动客户端总数 DailyClient人口表: 结果应该是这样的。这意味着我们需要显示每个月上半年和下半年的平均客户数量 TEMPBIWEEKLYCENSUS表: 我正在使用while循环显示结果,并更新开始日期和当前结束日期值。例如:11月上半月,开始日期=11/01,当前结束日期=11/15 结束日期=月底 代码如下: DECLARE @STARTDATE DATETIME DECLARE @ENDDATE DATETIME DECLARE @CURRENTE

我有一个表格,显示每天的提供商和活动客户端总数

DailyClient人口表:

结果应该是这样的。这意味着我们需要显示每个月上半年和下半年的平均客户数量

TEMPBIWEEKLYCENSUS表:

我正在使用while循环显示结果,并更新开始日期和当前结束日期值。例如:11月上半月,开始日期=11/01,当前结束日期=11/15

结束日期=月底

代码如下:

DECLARE @STARTDATE DATETIME
DECLARE @ENDDATE DATETIME
DECLARE @CURRENTENDDATE DATETIME
DECLARE @MONTHLASTDATE DATETIME
DECLARE @DAYSTOADD INT
DECLARE @TEMPSTARTDATE DATETIME

SET @STARTDATE= CONVERT(DATE, DATEADD(DAY, -@NoOfCharts*15,GETDATE()))
--PRINT @STARTDATE
SET @STARTDATE  = DATEADD(MONTH,DATEDIFF(MONTH, 0, @STARTDATE),0 )
--PRINT @STARTDATE
SET @ENDDATE = CONVERT(DATE,DATEADD(MONTH,1,GETDATE()))
SET @ENDDATE = DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, @ENDDATE),0))
--PRINT @ENDDATE

DECLARE @TEMPBIWEEKLYCENSUS table (ProviderName NVARCHAR(500), ActiveClients  INT, BiWeeklyRange NVARCHAR(50)  );
-- SET @MONTHLASTDATE =DATEADD(DAY,-1, DATEADD(MONTH,1,@STARTDATE))

WHILE (@STARTDATE <= @ENDDATE)
BEGIN
    SET @MONTHLASTDATE = DATEADD(DAY, -1, DATEADD(MONTH, 1, DATEADD(MONTH, DATEDIFF(MONTH, 0, @STARTDATE), 0)))
    -- PRINT DATEDIFF(DAY,@STARTDATE, @MONTHLASTDATE)

    IF DATEDIFF(DAY, @STARTDATE, @MONTHLASTDATE) > 15
    BEGIN 
        IF DATEDIFF(DAY, @STARTDATE, @MONTHLASTDATE) / 2 = 15
        BEGIN
            SET @DAYSTOADD = 15
        END
        ELSE 
        BEGIN 
            SET @DAYSTOADD = 14
        END 
    END
    ELSE IF DATEDIFF(DAY, @STARTDATE, @MONTHLASTDATE) < 15
    BEGIN  
        SET @DAYSTOADD = DATEDIFF(DAY,@STARTDATE, @MONTHLASTDATE)
    END  

    SET @CURRENTENDDATE = CONVERT(DATE,DATEADD(DAY,@DAYSTOADD,@STARTDATE))
    --PRINT '**************************************'
    --PRINT 'STARTDATE'
    --PRINT @STARTDATE
    --PRINT 'CURRENTENDDATE'
    --PRINT @CURRENTENDDATE 
    --PRINT '**************************************'

    INSERT INTO @TEMPBIWEEKLYCENSUS 
        SELECT 
            [ProviderName], 
            AVG(ActiveClients),  
            CONVERT(VARCHAR(10), DATEPART(MONTH, @STARTDATE)) + '/' + CONVERT(VARCHAR(10), DATEPART(DAY, @STARTDATE)) + '-' +  CONVERT(VARCHAR(10), DATEPART(MONTH, @CURRENTENDDATE)) + '/' + CONVERT(VARCHAR(10), DATEPART(DAY, @CURRENTENDDATE))   
        FROM 
            [dbo].[DailyClientPopulation] 
        WHERE 
            CONVERT(DATE, DateAdded) >= @STARTDATE 
            AND CONVERT(DATE, DateAdded) <= @CURRENTENDDATE
        GROUP BY 
            ProviderName

    SET @STARTDATE = CONVERT(DATE,DATEADD(DAY,1,@CURRENTENDDATE))
END

SELECT 
    ProviderName, ActiveClients, BiWeeklyRange  
FROM 
    @TEMPBIWEEKLYCENSUS

请您建议如何删除此while循环并将代码转换为基于集合的方法。

您可以使用DateAdded date值对dbo.DailyClientPopulation交叉应用一些子查询,以生成每两周的开始和结束日期,然后在where子句中包含BEVER逻辑。我在临时表中添加了几个月的示例数据,以显示日期函数如何将月份划分为两周一次的范围:

-- insert sample data
if object_id('tempdb..#DailyClientPopulation') is not null
    drop table #DailyClientPopulation
go
create table #DailyClientPopulation
    (
    Provider char(2),
    Activeclients int,
    DateAdded datetime
    )
insert into #DailyClientPopulation
    values
        ('p1',10,'2016-11-01'),
        ('p1',15,'2016-11-02'),
        ('p2',14,'2016-11-01'),
        ('p1',70,'2016-11-30'),
        ('p2',50,'2016-11-30'),
        ('p1',10,'2016-12-01'),
        ('p1',15,'2016-12-02'),
        ('p2',14,'2016-12-01'),
        ('p1',70,'2016-12-30'),
        ('p2',50,'2016-12-30'),
        ('p1',10,'2017-01-01'),
        ('p1',15,'2017-01-02'),
        ('p2',14,'2017-01-01'),
        ('p1',70,'2017-01-30'),
        ('p2',50,'2017-01-30'),
        ('p1',10,'2017-02-01'),
        ('p1',15,'2017-02-02'),
        ('p2',14,'2017-02-01'),
        ('p1',70,'2017-02-28'),
        ('p2',50,'2017-02-28')

-- return AvgActiveClients per BiWeeklyRange
select
    dcp.Provider,
    avg(dcp.ActiveClients) as AvgActiveClients,
    convert(varchar(10),bed.begin_date,101) + ' - ' + convert(varchar(10),bed.end_date,101) as BiWeeklyRange
from #DailyClientPopulation dcp
    cross apply (values(dateadd(mm,datediff(mm,0,dcp.DateAdded),0))) bom(bom_date) -- begin of month
    cross apply (values(dateadd(dd,-1,dateadd(mm,1,bom.bom_date)))) eom(eom_date) -- end of month
    cross apply (values(dateadd(dd,day(eom.eom_date)/2,bom.bom_date))) bosh(bosh_date) -- begin of second half
    cross apply (values(dateadd(dd,-1,bosh.bosh_date))) eofh(eofh_date) -- end of first half
    cross apply (values(bom.bom_date,eofh.eofh_date),
                       (bosh.bosh_date,eom.eom_date)) bed(begin_date,end_date) -- begin / end dates
where dcp.DateAdded between bed.begin_date and bed.end_date
group by
    dcp.Provider,
    bed.begin_date,
    bed.end_date
order by
    bed.begin_date,
    dcp.Provider

我假设您使用的是SQL Server,但是哪个版本?较新的版本具有增强的日期/时间功能,这可能会有所帮助。另外,15号是否始终是截止日期?或者它是根据一个月中的天数移动的,例如2月是14日吗?干杯。根据要求,可以使用基于开始和结束参数的理货表生成日期表。我使用的是SQL server 2012,而不是硬编码值,我们不能对月数使用任何logiv吗?我已更新了建议的解决方案。该方法使用交叉应用和一些复杂的日期函数逻辑将添加日期的月份划分为两周一次的日期范围。
DECLARE @STARTDATE DATETIME
DECLARE @ENDDATE DATETIME
DECLARE @CURRENTENDDATE DATETIME
DECLARE @MONTHLASTDATE DATETIME
DECLARE @DAYSTOADD INT
DECLARE @TEMPSTARTDATE DATETIME

SET @STARTDATE= CONVERT(DATE, DATEADD(DAY, -@NoOfCharts*15,GETDATE()))
--PRINT @STARTDATE
SET @STARTDATE  = DATEADD(MONTH,DATEDIFF(MONTH, 0, @STARTDATE),0 )
--PRINT @STARTDATE
SET @ENDDATE = CONVERT(DATE,DATEADD(MONTH,1,GETDATE()))
SET @ENDDATE = DATEADD(DAY, -1, DATEADD(MONTH, DATEDIFF(MONTH, 0, @ENDDATE),0))
--PRINT @ENDDATE

DECLARE @TEMPBIWEEKLYCENSUS table (ProviderName NVARCHAR(500), ActiveClients  INT, BiWeeklyRange NVARCHAR(50)  );
-- SET @MONTHLASTDATE =DATEADD(DAY,-1, DATEADD(MONTH,1,@STARTDATE))

WHILE (@STARTDATE <= @ENDDATE)
BEGIN
    SET @MONTHLASTDATE = DATEADD(DAY, -1, DATEADD(MONTH, 1, DATEADD(MONTH, DATEDIFF(MONTH, 0, @STARTDATE), 0)))
    -- PRINT DATEDIFF(DAY,@STARTDATE, @MONTHLASTDATE)

    IF DATEDIFF(DAY, @STARTDATE, @MONTHLASTDATE) > 15
    BEGIN 
        IF DATEDIFF(DAY, @STARTDATE, @MONTHLASTDATE) / 2 = 15
        BEGIN
            SET @DAYSTOADD = 15
        END
        ELSE 
        BEGIN 
            SET @DAYSTOADD = 14
        END 
    END
    ELSE IF DATEDIFF(DAY, @STARTDATE, @MONTHLASTDATE) < 15
    BEGIN  
        SET @DAYSTOADD = DATEDIFF(DAY,@STARTDATE, @MONTHLASTDATE)
    END  

    SET @CURRENTENDDATE = CONVERT(DATE,DATEADD(DAY,@DAYSTOADD,@STARTDATE))
    --PRINT '**************************************'
    --PRINT 'STARTDATE'
    --PRINT @STARTDATE
    --PRINT 'CURRENTENDDATE'
    --PRINT @CURRENTENDDATE 
    --PRINT '**************************************'

    INSERT INTO @TEMPBIWEEKLYCENSUS 
        SELECT 
            [ProviderName], 
            AVG(ActiveClients),  
            CONVERT(VARCHAR(10), DATEPART(MONTH, @STARTDATE)) + '/' + CONVERT(VARCHAR(10), DATEPART(DAY, @STARTDATE)) + '-' +  CONVERT(VARCHAR(10), DATEPART(MONTH, @CURRENTENDDATE)) + '/' + CONVERT(VARCHAR(10), DATEPART(DAY, @CURRENTENDDATE))   
        FROM 
            [dbo].[DailyClientPopulation] 
        WHERE 
            CONVERT(DATE, DateAdded) >= @STARTDATE 
            AND CONVERT(DATE, DateAdded) <= @CURRENTENDDATE
        GROUP BY 
            ProviderName

    SET @STARTDATE = CONVERT(DATE,DATEADD(DAY,1,@CURRENTENDDATE))
END

SELECT 
    ProviderName, ActiveClients, BiWeeklyRange  
FROM 
    @TEMPBIWEEKLYCENSUS
-- insert sample data
if object_id('tempdb..#DailyClientPopulation') is not null
    drop table #DailyClientPopulation
go
create table #DailyClientPopulation
    (
    Provider char(2),
    Activeclients int,
    DateAdded datetime
    )
insert into #DailyClientPopulation
    values
        ('p1',10,'2016-11-01'),
        ('p1',15,'2016-11-02'),
        ('p2',14,'2016-11-01'),
        ('p1',70,'2016-11-30'),
        ('p2',50,'2016-11-30'),
        ('p1',10,'2016-12-01'),
        ('p1',15,'2016-12-02'),
        ('p2',14,'2016-12-01'),
        ('p1',70,'2016-12-30'),
        ('p2',50,'2016-12-30'),
        ('p1',10,'2017-01-01'),
        ('p1',15,'2017-01-02'),
        ('p2',14,'2017-01-01'),
        ('p1',70,'2017-01-30'),
        ('p2',50,'2017-01-30'),
        ('p1',10,'2017-02-01'),
        ('p1',15,'2017-02-02'),
        ('p2',14,'2017-02-01'),
        ('p1',70,'2017-02-28'),
        ('p2',50,'2017-02-28')

-- return AvgActiveClients per BiWeeklyRange
select
    dcp.Provider,
    avg(dcp.ActiveClients) as AvgActiveClients,
    convert(varchar(10),bed.begin_date,101) + ' - ' + convert(varchar(10),bed.end_date,101) as BiWeeklyRange
from #DailyClientPopulation dcp
    cross apply (values(dateadd(mm,datediff(mm,0,dcp.DateAdded),0))) bom(bom_date) -- begin of month
    cross apply (values(dateadd(dd,-1,dateadd(mm,1,bom.bom_date)))) eom(eom_date) -- end of month
    cross apply (values(dateadd(dd,day(eom.eom_date)/2,bom.bom_date))) bosh(bosh_date) -- begin of second half
    cross apply (values(dateadd(dd,-1,bosh.bosh_date))) eofh(eofh_date) -- end of first half
    cross apply (values(bom.bom_date,eofh.eofh_date),
                       (bosh.bosh_date,eom.eom_date)) bed(begin_date,end_date) -- begin / end dates
where dcp.DateAdded between bed.begin_date and bed.end_date
group by
    dcp.Provider,
    bed.begin_date,
    bed.end_date
order by
    bed.begin_date,
    dcp.Provider