每个月的两个日期之间的LINQ to SQL之夜

时间:2017-04-07 23:01:59

标签: c# sql linq date

在DB中,我有一个BookingRooms集合,其中包含Checkin和Checkout日期,请说:

ID |签到|结帐

1 | 2017/01/01 | 2017年3月1日

2 | 29/01/2017 | 2017年3月2日

2 | 2017/02/04 | 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不会失败。

任何建议的答案

1 个答案:

答案 0 :(得分:1)

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

方法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;
    }

希望它有所帮助......