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

时间:2014-09-25 18:09:34

标签: sql sql-server sql-server-2008 datediff

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

BookingID,(唯一,非空) StartDate,(不为null) EndDate(非空)

我需要计算一个人居住的夜晚,我可以在EndDate和StartDate之间使用DATEDIFF。但是,如果有人在31个月内整个月都在居住,我们只收取30美元。

我不确定如何在SQL中执行此操作。我以为我必须创建一个变量,按月计算并添加到变量,但这似乎很麻烦,需要很长时间,特别是在年底。这需要每天为大约5,000条记录完成。

所以: 如果有人在2014年7月25日开始并于2014年2月9日结束,那么晚上的时间是38而不是39。 如果有人在2014年10月2日开始并于2014年1月11日结束,则夜晚为30。 如果某人在2014年10月2日开始并于2014年10月31日结束,则夜晚为29。

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

有没有人有任何想法如何以最佳方式实现这一目标?

4 个答案:

答案 0 :(得分:0)

检索datediff的整数部分除以31:

SELECT DATEDIFF(day,'2014-07-25', '2014-09-02') - DATEDIFF(day,'2014-07-25', '2014-09-02') / 31

答案 1 :(得分:0)

我首先要创建一个包含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)

答案 2 :(得分:0)

SQLish解决方案是创建一个日历表,其中包含您将关注的所有日期以及这些日期可能具有的任何业务含义,例如&#34;这是假期吗?&#34;,&# 34;这是淡季吗?&#34;或者&#34;我们这一天收费吗?&#34;

对于习惯使用其他编程语言的人来说,这可能听起来很疯狂,但在数据库世界中这是非常明智的。业务规则和业务逻辑存储为数据,而不是代码。

制作此表并填充它:

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]

当您的业务规则发生变化时,您只需更新日历而不是重写代码。

答案 3 :(得分:0)

如果我已正确阅读您的问题,那么您实际上无法使用上面的解决方案,其中包含可结算和不可计费的日期表,因为除非整个月都已预订,否则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日)。

如果你没有删除该行        当天(@next)= 1和DAY(@EndDate)= 31那么29