将日期范围转换为个别日期

时间:2013-08-09 16:47:29

标签: sql sql-server tsql date

名为VolumeRequest的表按日期范围的帐户存储卷请求。

AccountId   StartDate                       EndDate                   DailyVolume
670         2013-07-01 00:00:00.000         2013-07-31 00:00:00.000   10
670         2013-07-01 00:00:00.000         2013-07-31 00:00:00.000   1050
670         2013-07-10 00:00:00.000         2013-07-10 00:00:00.000   -350
670         2013-07-24 00:00:00.000         2013-07-26 00:00:00.000   -350
673         2013-06-01 00:00:00.000         2013-07-31 00:00:00.000   233

我需要每天显示请求,其中每天按照帐户的总帐量汇总给定的日期范围,例如报告如下所示的7月份。对于给定的报告日期,需要修剪卷请求的日期开始日期和结束日期

AccountId   Date                         Volume
670         2013-07-01 00:00:00.000      1060
670         2013-07-02 00:00:00.000      1060
.
.
670         2013-07-10 00:00:00.000      710
.
.
670         2013-07-24 00:00:00.000      710
670         2013-07-25 00:00:00.000      710
670         2013-07-26 00:00:00.000      710
.
.
670         2013-07-31 00:00:00.000      1060
673         2013-07-01 00:00:00.000      233
.
.
673         2013-07-31 00:00:00.000      233

现在我正在使用表变量和循环来实现它,我知道这不是一个好的编码方式。

DECLARE @sDate DATETIME, @eDate DATETIME , @volume DECIMAL (10, 4),  rstartdate DATETIME, @renddate   DATETIME , @loopcount   INT
SET @sdate = '4/1/2013'
SET @edate = '4/30/2013'

DECLARE @VolumeRequest TABLE 
  ( 
     ID        INT IDENTITY (1, 1) PRIMARY KEY, 
     Aid       INT, 
     Startdate DATETIME, 
     Enddate   DATETIME, 
     volume    DECIMAL (14, 4) 
  ) 
DECLARE @DailyRequest TABLE 
  ( 
     ID        INT IDENTITY (1, 1) PRIMARY KEY, 
     Accountid INT, 
     ReadDate  DATETIME, 
     Volume    DECIMAL (14, 4) 
  ) 

    INSERT INTO @VolumeRequest 
      SELECT Accountid, 
             ( CASE 
                 WHEN @sdate > startdate THEN @sdate 
                 ELSE startdate 
               END ), 
             ( CASE 
                 WHEN @edate < enddate THEN @edate 
                 ELSE enddate 
               END ), 
             dailyvolume 
      FROM   VolumeRequest 
      WHERE  Startdate <= @edate 
             AND Enddate >= @sdate 
             AND isnull (deprecated, 0) != 1 

      --loop to breakdown the volume requests into daily requests 
      SET @loopcount = 1

      WHILE @loopcount <= (SELECT MAX(ID) 
                                FROM   @VolumeRequest) 
        BEGIN 
            SELECT @volume = volume, 
                   @rstartdate = Startdate, 
                   @renddate = Enddate 
            FROM   @VolumeRequest 
            WHERE  ID = @loopcount 

            WHILE @rstartdate <= @renddate 
              BEGIN 
                  INSERT INTO @DailyRequest 
                  SELECT @currentaid, 
                         @rstartdate, 
                         @volume 

                  SET @rstartdate = DATEADD(day, 1, @rstartdate) 
              END 

            SET @LoopCount = @LoopCount + 1 
        END

我正在寻找不涉及循环或游标的方法。我找到了Similar Question。那里的答案对我没有帮助。

2 个答案:

答案 0 :(得分:2)

我喜欢使用日期表,例如

CREATE TABLE #Dates(
    DateId INT,
    CalendarDate DATETIME)

填写您需要的任何范围的日期。我使用此表连接到诸如VolumeRequest之类的表来检索您请求的输出。

SELECT
    v.AccountId,
    d.CalendarDate,
    SUM(v.DailyVolume)
FROM
    #Dates d INNER JOIN
    VolumeRequest v ON
        d.CalendarDate >= v.StartDate AND
        d.CalendarDate <= v.EndDate
group by
    d.CalendarDate,
    v.AccountId

填写#Dates表,我使用这样的东西:

declare @startdate datetime = '6/1/13', @enddate datetime = '7/31/13'

create table #Dates(CalendarDate datetime)

insert into #Dates(CalendarDate)
select
    dateadd(dd, rid-1, @startdate) as calendardate
from (
    select
        ROW_NUMBER() over(order by o.object_id) as rid
    From
        sys.objects o cross apply
        sys.objects o2
) dates
where
    dateadd(dd, rid-1, @startdate) >= @startdate and dateadd(dd, rid-1, @startdate) <= @enddate

修改以满足您的日期范围需求。

答案 1 :(得分:1)

<强> SQLFiddle demo

使用WITH子句和递归,我们生成Days表,其中包含MIN和MAX日期之间的所有日期。 然后生成具有不同Accounts的表AccountID。 最后,加入所有这些表并将所有表分组为SUM。

WITH MINMAX as 
( SELECT MIN(StartDate) as MinDate,
         MAX(EndDate) as MaxDate
  from T
),
DAYS as
( SELECT MinDate as D from MINMAX
  UNION ALL
  SELECT D+1 as D FROM DAYS WHERE D+1<=
    (
      SELECT MaxDate FROM MINMAX  
     )
),
Accounts as 
(
  select distinct AccountID from T
) 

select A.AccountId,Days.D,sum(T.DailyVolume) from Days
CROSS JOIN Accounts A 
JOIN T on A.AccountID=T.AccountID
          AND
          Days.D between T.StartDate and T.EndDate
GROUP BY A.AccountId,Days.D
ORDER BY A.AccountId,Days.D
OPTION (MAXRECURSION 10000)
相关问题