Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/sql/75.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
Sql Server 2008计算两个日期之间的夜数,但删除31夜_Sql_Sql Server_Sql Server 2008_Datediff - Fatal编程技术网

Sql Server 2008计算两个日期之间的夜数,但删除31夜

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

我有一张带有以下信息的预订表:

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

我们将计算到未来,因此结束日期是否大于运行报告的日期并不重要


有人知道如何以最佳方式完成此任务吗?

检索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?