0

以下 NHibernate QueryOver 查询正在计算给定日期范围内每个月的应用程序数量。

但是,对于没有任何应用程序的月份,我没有得到任何结果,但我实际上希望这些月份返回 Count = 0。

那么我将如何更改查询以在其中没有任何应用程序的月份也返回一行?

DateTimeOffset endDate = DateTimeOffset.Now;
DateTimeOffset startDate = endDate.AddMonths(-12);

var result = Session.QueryOver<Application>()
    .WhereRestrictionOn(c => c.SubmissionDate).IsBetween(startDate).And(endDate)
    .SelectList(list => list
        .Select(Projections.SqlGroupProjection(
            "YEAR(SubmissionDate) As [Year]",
            "YEAR(SubmissionDate)",
            new[] { "YEAR" },
            new IType[] { NHibernateUtil.Int32 }))
        .Select(Projections.SqlGroupProjection(
            "MONTH(SubmissionDate) As [Month]",
            "MONTH(SubmissionDate)",
            new[] { "MONTH" },
            new IType[] { NHibernateUtil.Int32 }))
        .SelectCount(x => x.Id))
    .OrderBy(Projections.SqlFunction(
        "YEAR",
        NHibernateUtil.Int32,
        Projections.Property<Application>(item => item.SubmissionDate))).Asc
    .ThenBy(Projections.SqlFunction(
        "MONTH",
        NHibernateUtil.Int32,
        Projections.Property<Application>(item => item.SubmissionDate))).Asc
    .List<object[]>()
    .Select(n => new
    {
        Year = n[0],
        Month = n[1],
        Count = (int)n[2]
    }));
4

3 回答 3

2

更新:带着你的想法DateTime.AddMonths()变得更短

    DateTime lastMonth = startdate;
    var unionresults = result.SelectMany(r =>
    {
        var actualDate = new DateTime(r.Year, r.Month, 1);

        var results = Enumerable.Repeat(1, Months)
            .Select(i => lastMonth.AddMonths(i))
            .TakeWhile(date => date < actualDate)
            .Select(date => new { Year = date.Year, Month = date.Month, Count = 0 })
            .Concat(new[] { r });

        lastMonth = actualDate;

        return results;
    });

原来的:

我认为您必须在查询后添加该数据。这是一个使用 linq 填写缺失月份的示例

var result = <query>;

int lastMonth = 1;
var unionresults = result.SelectMany(r =>
{
    var results = new[] { r }.AsEnumerable();

    if (lastMonth > r.Month)
    {
        results = Enumerable.Range(lastMonth, 12 - lastMonth).Select(month => new { Year = r.Year, Month = month, Count = 0 })
            .Concat(Enumerable.Range(1, r.Month).Select(month => new { Year = r.Year, Month = month, Count = 0 }))
            .Concat(results);
    }
    else if (lastMonth < r.Month)
    {
        results = Enumerable.Range(lastMonth, r.Month - lastMonth)
            .Select(month => new { Year = r.Year, Month = month, Count = 0 })
            .Concat(results);
    }

    lastMonth = r.Month + 1;
    if (lastMonth > 12)
    {
        lastMonth = 1;
    }

    return results;
});
于 2012-07-02T08:27:17.323 回答
1

它不能通过一些简单的改变来完成。由 QueryOver() 生成的 SQL 查询首先无法计算不存在的内容。您可能可以使用虚拟/临时表(取决于 DBMS)使用 UNION 或 JOIN 来执行此操作,但这会使查询过于复杂。

我建议在您的查询之后添加一个循环,该循环遍历列表,将元素复制到新列表并将任何不存在的月份添加到该新列表中。像这样的东西:

class YearMonthCount
{
    public int Year { get; set; }
    public int Month { get; set; }
    public int Count { get; set; }
}

// Start and End dates
DateTime startDate = new DateTime(2011, 9, 1);
DateTime endDate = new DateTime(2012, 6, 1);
// this would be a sample of the QueryOver() result
List<YearMonthCount> result = new List<YearMonthCount>();
result.Add(new YearMonthCount { Year = 2011, Month = 10, Count = 2 });
result.Add(new YearMonthCount { Year = 2011, Month = 11, Count = 3 });
result.Add(new YearMonthCount { Year = 2012, Month = 1, Count = 4 });
result.Add(new YearMonthCount { Year = 2012, Month = 2, Count = 1 });
result.Add(new YearMonthCount { Year = 2012, Month = 4, Count = 1 });
result.Add(new YearMonthCount { Year = 2012, Month = 5, Count = 1 });

int i = 0;
List<YearMonthCount> result2 = new List<YearMonthCount>();
// iterate through result list, add any missing entry
while (startDate <= endDate)
{
    bool addNewEntry = true;
    // check to avoid OutOfBoundsException
    if (i < result.Count)
    {
        DateTime listDate = new DateTime(result[i].Year, result[i].Month, 1);
        if (startDate == listDate)
        {
            // entry is in the QueryOver result -> add this
            result2.Add(result[i]);
            i++;
            addNewEntry = false;
        }
    }
    if (addNewEntry)
    {
        // entry is not in the QueryOver result -> add a new entry
        result2.Add(new YearMonthCount { 
            Year = startDate.Year, Month = startDate.Month, Count = 0 });
    }
    startDate = startDate.AddMonths(1);
}

这可能可以更优雅地完成,但它可以完成工作。

于 2012-07-02T08:33:32.197 回答
0

感谢所有的答案,这就是我最终这样做的方式:

DateTime endDate = DateTime.Now;
DateTime startDate = endDate.AddMonths(-Months);

var result = Session.QueryOver<Application>()
    .WhereRestrictionOn(c => c.SubmissionDate).IsBetween(startDate).And(endDate)
    .SelectList(list => list
        .Select(Projections.SqlGroupProjection(
        "YEAR(SubmissionDate) As [Year]",
        "YEAR(SubmissionDate)",
        new[] { "YEAR" },
        new IType[] { NHibernateUtil.Int32 }))
    .Select(Projections.SqlGroupProjection(
        "MONTH(SubmissionDate) As [Month]",
        "MONTH(SubmissionDate)",
        new[] { "MONTH" },
        new IType[] { NHibernateUtil.Int32 }))
    .SelectCount(x => x.Id))
    .List<object[]>()
    .Select(n => new
    {
        Year = (int)n[0],
        Month = (int)n[1],
        Count = (int)n[2]
    }).ToList();

var finalResult = result
    .Union(
        Enumerable.Range(0, Months - 1).Select(n => new
        {
            Year = startDate.AddMonths(n).Year,
            Month = startDate.AddMonths(n).Month,
            Count = 0
        })
        .Where(n => !result.Any(r => r.Year == n.Year && r.Month == n.Month)))
    .OrderBy(n => n.Year).ThenBy(n => n.Month);
于 2012-07-02T09:41:15.553 回答