Sql Server 2008计算两个日期之间的夜数,但删除31夜
我有一张带有以下信息的预订表: BookingID,唯一,不为空 StartDate,不为空 EndDate不为空 我需要计算一个人居住的夜数,我可以用EndDate和StartDate之间的DATEDIFF来计算。但是,如果有人在31天的月份内居住了整整一个月,我们只收取他们30英镑 我不知道如何在SQL中实现这一点。我想我必须创建一个变量,每月计算一次,然后添加到变量中,但这看起来很混乱,需要很长时间,尤其是在年底。每天需要对大约5000条记录执行此操作 因此: 如果某人从2014年7月25日开始,到2014年9月2日结束,那么夜晚是38而不是39。 如果某人从2014年10月2日开始,到2014年11月1日结束,那么夜晚是30。 如果某人从2014年10月2日开始,到2014年10月31日结束,那么夜晚是29 我们将计算到未来,因此结束日期是否大于运行报告的日期并不重要Sql Server 2008计算两个日期之间的夜数,但删除31夜,sql,sql-server,sql-server-2008,datediff,Sql,Sql Server,Sql Server 2008,Datediff,我有一张带有以下信息的预订表: BookingID,唯一,不为空 StartDate,不为空 EndDate不为空 我需要计算一个人居住的夜数,我可以用EndDate和StartDate之间的DATEDIFF来计算。但是,如果有人在31天的月份内居住了整整一个月,我们只收取他们30英镑 我不知道如何在SQL中实现这一点。我想我必须创建一个变量,每月计算一次,然后添加到变量中,但这看起来很混乱,需要很长时间,尤其是在年底。每天需要对大约5000条记录执行此操作 因此: 如果某人从2014年7月25
有人知道如何以最佳方式完成此任务吗?检索datediff除以31的整数部分:
SELECT DATEDIFF(day,'2014-07-25', '2014-09-02') - DATEDIFF(day,'2014-07-25', '2014-09-02') / 31
首先,我要创建一个包含31天的所有月份的查找表 比如
DECLARE @month TABLE (start_date date,end_date date)
INSERT INTO @month VALUES ('2014-07-01','2014-07-31'),('2014-08-01','2014-08-31'),('2014-10-01','2014-10-31'),('2014-12-01','2014-12-31')
//populate all months in your calculate range
然后,您可以使用
DECLARE @start DATE = '2014-07-25', @end DATE = '2014-09-02'
SELECT DATEDIFF(day,@start,@end) -
(SELECT COUNT(*) FROM @month WHERE start_date >= @start AND end_date <= @end)
SQLish解决方案是创建一个日历表,其中包含您关心的所有日期以及这些日期可能具有的任何业务含义,例如,这是假日吗?这是淡季吗?或者我们对这一天收费吗 对于习惯了其他编程语言的人来说,这听起来可能很疯狂,但在数据库世界中,这是完全合理的。业务规则和业务逻辑存储为数据,而不是代码 创建此表并填充它:
CREATE TABLE Calendar (
[date] date
,[is_charged] bit
)
然后,您可以编写近乎通俗易懂的代码:
SELECT
[BookingID]
,COUNT([date])
FROM BookingTable
INNER JOIN Calendar
ON [date] >= [StartDate]
AND [date] < [EndDate]
WHERE [is_charged] = 1
GROUP BY [BookingId]
当您的业务规则发生变化时,您只需更新日历,而不是重新编写代码。如果我正确阅读了您的问题,那么您就无法实际使用上面包含计费和非计费天数表的解决方案,因为除非整个月都已预订,否则第31天是计费的 我认为这可能是用户定义函数的工作。从开始日期所在的月份开始,到结束日期所在的月份结束
CREATE FUNCTION dbo.FN_BillableDays (@StartDate date, @EndDate date)
returns int
AS
BEGIN
IF @StartDate > @EndDate
BEGIN
return null --Error
END
DECLARE @Next date
DECLARE @MonthStart date
DECLARE @MonthEnd date
DECLARE @NextMonthStart date
DECLARE @n int =0
SET @Next = @StartDate
SET @MonthStart = DATEADD(day,1-DAY(@Next),@Next)
SET @NextMonthStart = DATEADD(month,1,@MonthStart )
SET @MonthEnd = DATEADD(day,-1,@NextMonthStart)
WHILE DATEDIFF(month,@Next,@EndDate) >0
BEGIN
SET @n = @n +
CASE
WHEN DAY(@next) = 1 AND DAY(@MonthEnd) = 31 THEN 30
WHEN DAY(@next) = 1 THEN DAY(@MonthEnd)
ELSE 1+DAY(@MonthEnd) -DAY(@next) END
SET @MonthStart = @NextMonthStart
SET @NextMonthStart = DATEADD(month,1,@MonthStart )
SET @MonthEnd = DATEADD(day,-1,@NextMonthStart)
SET @Next = @NextMonthStart
END
--Month of the EndDate
SET @n = @n +
CASE
WHEN DAY(@next) = 1 AND DAY(@EndDate) = 31 THEN 29
WHEN DAY(@next) = 1 THEN DAY(@EndDate)-1
ELSE DAY(@MonthEnd) -DAY(@EndDate) END
return @n
END
我试了一些测试日期
SELECT
b.BookingID,
b.StartDate,
b.EndDate,
dbo.FN_BillableDays (b.StartDate,b.EndDate) AS BillableDays
FROM dbo.Booking b
并且得到了以下结果
BookingID StartDate EndDate BillableDays
----------- ---------- ---------- ------------
1 2013-12-31 2014-01-02 2
2 2013-12-31 2014-01-30 30
3 2014-01-01 2014-01-30 29
4 2014-01-01 2014-01-31 29
5 2014-01-01 2014-02-01 30
6 2014-01-01 2014-02-02 31
7 2014-02-02 2014-02-01 NULL
(7 row(s) affected)
这符合我对您想要实现的逻辑的理解,但您可能希望调整最后一位,它会增加最后一个月的天数。如果他们在31号离开,你想免费给他们最后一晚的30号到31号吗
如果没有,则删除该行
什么时候DAY@next=1和DAY@EndDate=31那么29如果datediff是62,答案应该是60吗?谢谢你的建议。我计划使用日历表另一个方面,我们有具体的日期,而不是计费。但是,由于日期是否计费取决于每次停留的时间长短,我认为这个解决方案不适用。谢谢!是的,这就是为什么我没有从日历表开始的原因,这是我最初的想法,因为夜晚是否计费是不同的。我还没有机会测试这个,但这是我认为我必须去的地方。明天我会告诉您它的测试结果。可能值得将结果缓存在查找表中,并更改函数以检查查找表,如果找到则返回值,否则使用相同的方法计算值并将其存储在表中。这样,每对值的计费天数只需计算一次。您甚至可以创建一个巨大的查找表,其中包含您可能需要的每一对日期,使用此函数预填充它,并将该表用于常规流程。最终,您必须决定每次计算对性能的影响是否可以接受是的,我必须考虑性能@EricZ的方法也很有效,所以我需要确定哪种方法更具可扩展性和更好的性能。我还忘了补充一点,在二月份,如果他们呆了整整一个月,那么他们会得到30晚的账单,而不仅仅是28晚。我假设我将不得不添加一些代码来解释这个问题,当月份是2?