如何使此NHibernate QueryOver查询返回空组的行

时间:2012-07-01 13:56:54

标签: c# nhibernate group-by queryover

以下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]
    }));

3 个答案:

答案 0 :(得分: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;
});

答案 1 :(得分: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);
}

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

答案 2 :(得分: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);