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 に答える