2017-04-07 89 views
0

在DB我有BookingRooms的集合,其中包含登记入住和结帐日期让说:LINQ到两个日期之间的SQL夜每个月

ID |签入|结帐

1 | 01/01/2017 | 03/01/2017

2 | 29/01/2017 | 03/02/2017

2 | 04/02/2017 | 2017年5月2日

(格式为DD/MM/YYYY)

现在我要统计有多少个夜晚,在每一个可测量的一个月,被黄牌警告,因此结果应该是这样的:

ID |区域|夜间

1 | 01/2017 | 5

2 | 02/2017 | 3

  var result = (from boo in context.BookingRooms 
         group boo by new { boo.CheckIn.Year, boo.CheckIn.Month } into book 
         orderby book.Key.Year descending, book.Key.Month descending 
         select new NightsPerMonth 
         { 
          Area = book.Key.Month + "/" + book.Key.Year, 
          Nights = ?? 

         }).ToList(); 

这是我到目前为止,但问题是,如果我这组由入住者谁是前几个月检查将不组,另外,如果我是成功分组我不知道如何根据DbFunctions计算当前分组月份的夜晚(避免在前一个月或下一个月计算夜晚,即使Checkout或Checkin超出当前月份),以便LINQ to SQL不会失败。

Thx对于任何建议

+0

在你的第一个表,你有ID 2出现两次?这个ID代表什么? –

+0

@DavidLee它的表的自动增量键 - 它没有任何进一步的用法 –

回答

1

我认为你的要求是相当复杂的linq查询。 我建议2种方法来解决它。

方法1: 这里是我的SQL查询来获取欲望造成的,您可以创建存储过程或函数中使用它。

DECLARE @SampleData AS TABLE (Id int IDENTITY (1,1), Checkin Date, Checkout date) 

INSERT INTO @SampleData VALUES ('2017-01-01', '2017-01-03'), ('2017-01-29', '2017-02-03'), ('2017-02-04', '2017-02-05') 

;WITH temps AS 
(
    SELECT CAST(MIN(sd.Checkin) AS Date) AS MinCheckDate, CAST(max(sd.Checkout) AS Date) as MaxCheckDate 
    FROM @SampleData sd 
) 
-- Calculate all months from min-checkin-date to max-checkout-date. 
-- By Recursive function 
,allMonth AS 
(
    SELECT datepart(month,t.MinCheckDate) AS [Month], 
      datepart(year, t.MinCheckDate) AS [Year], 
      datefromparts(datepart(year, t.MinCheckDate) , datepart(month, t.MinCheckDate) ,1) AS StartMonthDate , 
      EOMONTH(datefromparts(datepart(year, t.MinCheckDate) , datepart(month, t.MinCheckDate) ,1)) AS EndMonthDate 
    FROM temps t 

    UNION ALL 

    SELECT datepart(month,dateadd(month, 1, am.StartMonthDate)) AS [Month], 
      datepart(year, dateadd(month, 1, am.StartMonthDate)) AS [Year], 
      datefromparts(datepart(year, dateadd(month, 1, am.StartMonthDate)) , datepart(month, dateadd(month, 1, am.StartMonthDate)) ,1) AS StartMonthDate , 
      EOMONTH(datefromparts(datepart(year, dateadd(month, 1, am.StartMonthDate)) , datepart(month, dateadd(month, 1, am.StartMonthDate)) ,1)) AS EndMonthDate 
    FROM allMonth am 
    CROSS JOIN temps t 
    WHERE dateadd(month, 1, am.StartMonthDate) <= t.MaxCheckDate 
) 
SELECT CONCAT(am.[Month],'/',am.[Year]) AS Area, 
     SUM(
      DATEDIFF(day, 
       CAST(IIF( am.StartMonthDate <= sd.Checkin, sd.Checkin, am.StartMonthDate) AS DATE), 
       CAST(IIF( am.EndMonthDate < sd.Checkout , dateadd(dd, 1,am.EndMonthDate), sd.Checkout) AS date) -- if Checkout > End of month, then End of month will be calculate as a night 
     )) AS Nights 
FROM allMonth am 
LEFT JOIN @SampleData sd ON am.StartMonthDate BETWEEN sd.Checkin AND sd.Checkout OR am.EndMonthDate BETWEEN sd.Checkin AND sd.Checkout 
         OR sd.Checkin BETWEEN am.StartMonthDate AND am.EndMonthDate OR sd.Checkout BETWEEN am.StartMonthDate AND am.EndMonthDate 
GROUP BY am.[Month],am.[Year] 
OPTION (MAXRECURSION 0) 

演示链接:Rextester

方法2:

您可以加载从BookingRooms表中的所有数据,并在C#计算之夜:

var lstBookRooms = (from boo in context.BookingRooms 
          select new BookingRooms 
          { 
           Id = boo.Id, 
           Checkin = boo.CheckIn, 
           Checkout = boo.CheckOut 
          }).ToList(); 
var lstNightsPerMonth = GetNightsPerMonth(lstBookRooms); 

public class BookingRooms 
{ 
    public int Id { get; set; } 
    public DateTime Checkin { get; set; } 
    public DateTime Checkout { get; set; } 
} 

public class NightsPerMonth 
{ 
    public string Area 
    { 
     get 
     { 
      return string.Format("{0}/{1}", Month, Year); 
     } 
    } 
    public int Month { get; set; } 

    public int Year { get; set; } 

    public int Nights { get; set; } 
} 

而且功能

public List<NightsPerMonth> GetNightsPerMonth(List<BookingRooms> lstBookRooms) 
    { 
     if (lstBookRooms == null || lstBookRooms.Count == 0) return null; 
     var result = new List<NightsPerMonth>(); 
     var minCheckin = lstBookRooms.Min(x => x.Checkin); 
     var maxCheckout = lstBookRooms.Max(x => x.Checkout); 
     var currentMonth = minCheckin; 

     while (currentMonth <= maxCheckout) 
     { 
      result.Add(new NightsPerMonth 
      { 
       Month = currentMonth.Month, 
       Year = currentMonth.Year, 
       Nights = GetNumberNightsOfMonth(currentMonth, lstBookRooms) 
      }); 
      currentMonth = currentMonth.AddMonths(1); 
     } 

     return result; 
    } 

    private int GetNumberNightsOfMonth(DateTime currentMonth, List<BookingRooms> lstBookRooms) 
    { 
     var startDateOfMonth = new DateTime(currentMonth.Year, currentMonth.Month, 1); 
     var endDateOfMonth = startDateOfMonth.AddMonths(1).AddDays(-1); 

     return lstBookRooms.Where(x => IsBookRoom(startDateOfMonth, endDateOfMonth, x.Checkin, x.Checkout)) 
      .Sum(x => NumberNightsBookRoom(startDateOfMonth, endDateOfMonth, x.Checkin, x.Checkout)); 
    } 

    private bool IsBookRoom(DateTime startDateOfMonth, DateTime endDateOfMonth, DateTime checkin, DateTime checkout) 
    { 
     if (startDateOfMonth >= checkin && startDateOfMonth <= checkout) return true; 
     if (endDateOfMonth >= checkin && endDateOfMonth <= checkout) return true; 

     if (checkin >= startDateOfMonth && checkin <= endDateOfMonth) return true; 
     if (checkout >= startDateOfMonth && checkout <= endDateOfMonth) return true; 

     return false; 
    } 

    private int NumberNightsBookRoom(DateTime startDateOfMonth, DateTime endDateOfMonth, DateTime checkin, DateTime checkout) 
    { 
     var startTimeSpan = startDateOfMonth <= checkin ? checkin : startDateOfMonth; 

     // if Checkout > End of month, then End of month will be calculate as a night 
     var endTimeSpan = endDateOfMonth < checkout ? endDateOfMonth.AddDays(1) : checkout; 

     return (endTimeSpan - startTimeSpan).Days; 
    } 

希望它可以帮助....

+0

Thx,它不是我正在寻找的(它不是LINQ),但它解决了问题!谢谢。 –

相关问题