【发布时间】:2016-04-26 20:47:37
【问题描述】:
我被要求创建一个存储过程来显示可变日期范围内的租赁项目数量。我有一个具有以下架构的表:
--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:00。我在 2016 年 4 月 1 日下午 3:00 租了 10 个小部件。这基本上我的意思是我实际上是在 2016 年 4 月 2 日租了它们,因为它已经过了 04/01 的截止时间。我在 2016 年 4 月 10 日下午 1:00 再租了 2 个,所以基本上是 2016 年 4 月 11 日。我在 2016 年 4 月 15 日上午 11:00 返还 5 个小部件,在上午 11:30 返还 2 个。我想在 2016 年 4 月 27 日归还所有小部件,但我在截止时间 12:00 PM 之后到达,因此我实际上将在 2016 年 4 月 2 日至 27 日收取费用,而不是收取费用-4/28。
重要提示:如果我之前在 04/01 之前租过的数量是报告范围的开始,我需要将这些数量包含在报告中。例如,如果我在 3 月 31 日、4 月 1 日及以后有 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
预期的最终输出: 完成后,报告最终会看起来像这样,尽管它会被旋转并在名称中包含星期几:
ContractDetailId RentalDate Quantity
----------------------------------------------------------------
1 04/01/2016 0 -- 0, because rentals were input after cutoff.
1 04/02/2016 10
1 04/03/2016 10 -- Continues until 4/10
1 04/10/2016 10
1 04/11/2016 12 -- Continues until 4/15
1 04/15/2016 5 -- I returned 5 and then 2, so this should sum since both were before the cutoff time.
-- Continues until 4/27.
1 04/27/2016 5 -- 5, because -5 was entered past cutoff on 4/27.
1 04/28/2016 0
我已经完成了旋转代码以及最终输出所需的动态 sql(如果需要,我可以发布此内容),但我不知道如何通过预截止/截止后正确分组这些数据并更改相应的一天。我应该如何处理这种情况?感谢您的任何建议/帮助!
编辑 1: 修正了不正确的样本数据。
【问题讨论】:
-
您的示例数据似乎显示了 12 个租用的小部件和 14 个返回的小部件。虽然这可能是一种有利可图的商业模式,但似乎有点偏离。
-
要处理截止时间,只需从每个原始
datetime中减去12*60=720分钟,然后忽略时间部分:CAST(DATEADD(minute, -720, RentalDate) AS date)。 -
@HABO 你是对的,不知道我是怎么错过的。 04/15 应该是 5。我编辑了它。
标签: tsql sum sql-server-2014-express