Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/date/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Tsql 如何计算同时考虑日期和时间的运行总数?_Tsql_Date_Sum_Sql Server 2014 Express_Totals - Fatal编程技术网

Tsql 如何计算同时考虑日期和时间的运行总数?

Tsql 如何计算同时考虑日期和时间的运行总数?,tsql,date,sum,sql-server-2014-express,totals,Tsql,Date,Sum,Sql Server 2014 Express,Totals,我被要求创建一个存储过程,以显示可变日期范围内的租赁项目数量。我有一个具有以下架构的表: --Note that this is condensed, and in reality has proper constraints --and more columns. Many dates from this table are tied to a single --ContractDetail (separate table) by Con

我被要求创建一个存储过程,以显示可变日期范围内的租赁项目数量。我有一个具有以下架构的表:

         --Note that this is condensed, and in reality has proper    constraints
        --and more columns. Many dates from this table are tied to a single
        --ContractDetail (separate table) by ContractDetailId.
        CREATE TABLE RentalContractDates
        (
            RentalDateId               INT IDENTITY(1,1)    NOT NULL, --PK
            ContractDetailId           INT                  NOT NULL, --FK
            RentalDate                 DATETIME             NOT NULL,
            Quantity                   DECIMAL(20,8)        NULL
        );

        INSERT INTO RentalContractDates (ContractDetailId, RentalDate, Quantity)
        VALUES (1, '04/01/2016 3:00 PM', 10), 
               (1, '04/10/2016 1:00 PM', 2), 
               (1, '04/15/2016 11:00 AM', -5),
               (1, '04/15/2016 11:30 AM', -2), 
               (1, '04/27/2016 2:00 PM', -5);       
用户将输入一个要搜索的日期范围,该过程应查找该范围内的所有日期,然后还应考虑客户将在另一天收取租金的截止时间

示例场景:全局截止时间设置为12:00PM。我在2016年1月4日下午3点租了10个小部件。这基本上意味着我实际上是在2016年2月4日租的,因为它已经超过了4月1日的截止时间。我在2016年10月4日下午1:00再租了两间,所以基本上是2016年11月4日。我在2016年4月15日上午11:00返回了5个小部件,在11:30返回了2个。我想在2016年4月27日归还所有小部件,但我到达的时间超过了下午12:00的截止时间,因此我不是要收取4/02-4/27的费用,而是要收取4/02-4/28的费用

重要提示:如果我在01年4月之前(即报告范围的开始)有租赁数量,我需要在报告中包括这些数量。例如,如果我在3月31日、4月1日和3月31日有12次租房,那么他们的总数将增加12次。换句话说,任何以前的数量都需要计算到与输入的报告@BeginDate和@EndDate参数一起拉入的总和中。 所以04/01应该是12,04/02应该是22,等等

正如您所看到的,我不需要用户每天输入租金,我只是让他们使用数量设置租金的开始日期和时间,下一次他们输入日期/时间组合时,将重新求和

当前代码:我想用整个月的日历日期列表加入此查询,并相应地设置其数量

        DECLARE @BeginDate DATETIME = '04/01/2016',
                @EndDate DATETIME = '04/28/2016';

        DECLARE
                @CutoffTime TIME = '12:00 PM';

        SET @BeginDate = @BeginDate + @CutoffTime;
        SET @EndDate = @EndDate + @CutoffTime;

        SELECT  gbd.ContractDetailId,
                gbd.RentalDate,
                gbd.Cutoff,
                gbd.Quantity,
                'Running Total' = SUM(Quantity) OVER (PARTITION BY ContractDetailId, RentalDate, Cutoff ORDER BY RentalDate)
        FROM    (

    SELECT  
            r.ContractDetailId,
            'RentalDate' = CONVERT(Date, RentalDate),
            r2.Cutoff,
            r.Quantity
    FROM    RentalContractDates r
    INNER JOIN
        (
            SELECT 
                rcd.ContractDetailId,
                'Cutoff' = CASE WHEN CONVERT(TIME, RentalDate) >= @CutoffTime THEN 'AFTER CUTOFF' ELSE 'BEFORE CUTOFF' END
            FROM 
                RentalContractDates rcd
         ) r2
        ON r2.ContractDetailId = r.ContractDetailId
    WHERE  
            r.RentalDate Between @BeginDate and @EndDate
    GROUP BY r.ContractDetailId, CONVERT(DATE, RentalDate), r2.Cutoff, Quantity
        ) gbd
        ORDER BY RentalDate, Cutoff DESC
我想将数据与此CTE合并,并为每个日期设置数量:

        ;WITH T([Date]) AS
    ( 
        SELECT @StartDate
        UNION ALL
        SELECT DATEADD(DAY,1,T.[Date]) FROM T WHERE T.[Date] < @EndDate
    )
    SELECT * FROM T
我已经完成了最终输出所需的数据透视代码和动态sql(如果需要,我可以发布),但我不知道如何通过预截止/后截止正确地分组这些数据,并相应地更改日期。我该如何处理这种情况?谢谢你的建议/帮助

编辑1:修复了不正确的样本数据

  --Inputs for your function
    DECLARE @BeginDate DATE = '04/01/2016',
            @EndDate DATE = '04/28/2016',
            @ContractDetailID INT = 1;

    --Defined in the function
    DECLARE @CutoffTime TIME = '12:00 PM';

    DECLARE @PriorSum DECIMAL(20,8) = 0;

    DECLARE @RowCount INT = DATEDIFF(dd,@BeginDate,@Enddate) +1;

    --Get Any quantities before Begin Date
    SELECT @PriorSum=COALESCE(SUM(rcd.Quantity),0)
    from RentalContractDates rcd
    WHERE CAST(CASE when CAST(rcd.RentalDate as TIME) > @CutoffTime THEN DATEADD(dd,1,rcd.RentalDate) ELSE rcd.RentalDate end as date) < @BeginDate
        AND @ContractDetailID = rcd.ContractDetailId

    --Create the Days for the report
    ;WITH RecursiveRowGenerator (Row#, Iteration) AS (
           SELECT 1, 1
            UNION ALL
           SELECT Row# + Iteration, Iteration * 2
             FROM RecursiveRowGenerator
            WHERE Iteration * 2 < CEILING(SQRT(@RowCount+1))
            UNION ALL
           SELECT Row# + (Iteration * 2), Iteration * 2
             FROM RecursiveRowGenerator
            WHERE Iteration * 2 < CEILING(SQRT(@RowCount+1))
         )
         , SqrtNRows AS (
           SELECT *
             FROM RecursiveRowGenerator
            UNION ALL
           SELECT 0, 0
         )
         , Rowtbl as (  
           SELECT top (@RowCount+1) A.Row# * POWER(2,CEILING(LOG(SQRT(@RowCount+1))/LOG(2))) + B.Row# as RowNum
           FROM SqrtNRows A, SqrtNRows B
           ORDER BY A.Row#, B.Row#
         )  
      ,
    DateTable as (

      select top (@RowCount)  DATEADD(dd,RowNum,@BeginDate) AS ReportDate
      from Rowtbl
      where RowNum <= @RowCount  

      )
    ,
    --Merge the days for the report with the actual rental data
    GBD AS 
    ( SELECT  
               @ContractDetailID as ContractDetailID,
                DT.ReportDate AS 'RentalDate',
                CASE when CAST(rcd.RentalDate as TIME) > @CutoffTime THEN 'AFTER CUTOFF' ELSE 'BEFORE CUTOFF' END AS 'Cutoff',
                COALESCE(rcd.Quantity,0) AS Quantity
        FROM    DateTable DT 
        LEFT JOIN RentalContractDates rcd on 
                DT.ReportDate = CAST( CASE when CAST(rcd.RentalDate as TIME) > @CutoffTime THEN DATEADD(dd,1,rcd.RentalDate) ELSE rcd.RentalDate END as DATE) 
                AND @ContractDetailID = rcd.ContractDetailId
        WHERE DT.ReportDate Between @BeginDate and @EndDate 


    )
--Final Select    
SELECT  gbd1.ContractDetailId,
                gbd1.RentalDate,
                (select SUM(gbd2.Quantity) from GBD GBD2 where GBD1.rentaldate >= GBD2.RentalDate) + @PriorSum AS RunningTotal               
        FROM   GBD gbd1     
        GROUP BY gbd1.ContractDetailId,gbd1.RentalDate
        ORDER BY gbd1.RentalDate asc   

它只将rentaldate转换为一个时间,与截止时间相比,如果过去,则增加一天,然后转换为仅日期。

您的示例数据显示租用了12个小部件,返回了14个小部件。虽然这可能是一个有利可图的商业模式,但似乎有点不合适。要处理截止时间,只需从每个原始的
datetime
中减去
12*60=720分钟,然后忽略时间部分:
CAST(DATEADD(minute,-720,RentalDate)AS date)
@HABO你是对的,不知道我是怎么错过的。04/15应该是5。我编辑了它。我测试了你的代码,虽然它工作正常,但当将日期范围扩大到一年或更长时,速度会大大减慢。从2000年开始计算开始日期时,处理时间超过1分钟。问题是,我可以在运行报告的单个合同中有相当多的ContractDetailID(100+),每个都有许多日期。我正在修改它,看看是否可以使用GBD合并选择并将其移动到临时表以加快速度。我相信CTE和这个select的递归会减慢速度。我会在完成后更新我的原始帖子。谢谢完全同意。我应该更清楚的是,这个解决方案并不是要在更大的日期范围内执行的。我认为你的方法很好。
  --Inputs for your function
    DECLARE @BeginDate DATE = '04/01/2016',
            @EndDate DATE = '04/28/2016',
            @ContractDetailID INT = 1;

    --Defined in the function
    DECLARE @CutoffTime TIME = '12:00 PM';

    DECLARE @PriorSum DECIMAL(20,8) = 0;

    DECLARE @RowCount INT = DATEDIFF(dd,@BeginDate,@Enddate) +1;

    --Get Any quantities before Begin Date
    SELECT @PriorSum=COALESCE(SUM(rcd.Quantity),0)
    from RentalContractDates rcd
    WHERE CAST(CASE when CAST(rcd.RentalDate as TIME) > @CutoffTime THEN DATEADD(dd,1,rcd.RentalDate) ELSE rcd.RentalDate end as date) < @BeginDate
        AND @ContractDetailID = rcd.ContractDetailId

    --Create the Days for the report
    ;WITH RecursiveRowGenerator (Row#, Iteration) AS (
           SELECT 1, 1
            UNION ALL
           SELECT Row# + Iteration, Iteration * 2
             FROM RecursiveRowGenerator
            WHERE Iteration * 2 < CEILING(SQRT(@RowCount+1))
            UNION ALL
           SELECT Row# + (Iteration * 2), Iteration * 2
             FROM RecursiveRowGenerator
            WHERE Iteration * 2 < CEILING(SQRT(@RowCount+1))
         )
         , SqrtNRows AS (
           SELECT *
             FROM RecursiveRowGenerator
            UNION ALL
           SELECT 0, 0
         )
         , Rowtbl as (  
           SELECT top (@RowCount+1) A.Row# * POWER(2,CEILING(LOG(SQRT(@RowCount+1))/LOG(2))) + B.Row# as RowNum
           FROM SqrtNRows A, SqrtNRows B
           ORDER BY A.Row#, B.Row#
         )  
      ,
    DateTable as (

      select top (@RowCount)  DATEADD(dd,RowNum,@BeginDate) AS ReportDate
      from Rowtbl
      where RowNum <= @RowCount  

      )
    ,
    --Merge the days for the report with the actual rental data
    GBD AS 
    ( SELECT  
               @ContractDetailID as ContractDetailID,
                DT.ReportDate AS 'RentalDate',
                CASE when CAST(rcd.RentalDate as TIME) > @CutoffTime THEN 'AFTER CUTOFF' ELSE 'BEFORE CUTOFF' END AS 'Cutoff',
                COALESCE(rcd.Quantity,0) AS Quantity
        FROM    DateTable DT 
        LEFT JOIN RentalContractDates rcd on 
                DT.ReportDate = CAST( CASE when CAST(rcd.RentalDate as TIME) > @CutoffTime THEN DATEADD(dd,1,rcd.RentalDate) ELSE rcd.RentalDate END as DATE) 
                AND @ContractDetailID = rcd.ContractDetailId
        WHERE DT.ReportDate Between @BeginDate and @EndDate 


    )
--Final Select    
SELECT  gbd1.ContractDetailId,
                gbd1.RentalDate,
                (select SUM(gbd2.Quantity) from GBD GBD2 where GBD1.rentaldate >= GBD2.RentalDate) + @PriorSum AS RunningTotal               
        FROM   GBD gbd1     
        GROUP BY gbd1.ContractDetailId,gbd1.RentalDate
        ORDER BY gbd1.RentalDate asc   
gbd.RentalDate = CAST(CASE WHEN CAST(rcd.RentalDate AS TIME) > @CutoffTime THEN DATEADD(dd, 1, rcd.RentalDate)
            ELSE rcd.RentalDate END AS DATE)