Linq-Entities: выборка данных, исключая перекрывающиеся диапазоны данных, выбор наибольшего периода - PullRequest
0 голосов
/ 13 апреля 2011

У меня есть 2 таблицы, Импорт и периоды.

Импорт имеет следующую структуру:

AdminID, PeriodID, Some more fields
1,       1
1,       2
1,       6
1,       50

Таблица периодов имеет следующую структуру:

PeriodID, PeriodType, StartDate,   EndDate,    Description
1,        1,          2007-01-01,  2007-12-31, Year 2007
2,        2,          2007-01-01,  2007-03-31, Quarter 1 2007
3,        2,          2007-04-01,  2007-06-30, Quarter 2 2007
4,        2,          2007-07-01,  2007-09-30, Quarter 3 2007
5,        2,          2007-10-01,  2007-12-31, Quarter 4 2007
6,        3,          2007-01-01,  2007-01-31, January 2007
.
.
.
50,       2,          2011-01-01,  2011-03-31, Quarter 1 2011

СейчасМне нужно создать запрос linq для выборки только самого большого периода (игнорируя меньшие перекрывающиеся периоды) на основе данных в таблице импорта!

Когда я запрашиваю AdminID = 1, я должен получить только PeriodID = 1& 50, игнорируя / исключая PeriodID 2 и 6, так как они перекрываются в 1 и 50, так как данные еще не перекрываются!

Ответы [ 3 ]

0 голосов
/ 13 апреля 2011

Я не уверен, есть ли удобный способ сделать это в базе данных, но когда вы извлекаете данные локально, вы можете выполнять запросы LINQ в памяти, если это уместно. Тебе нужно сделать это по шагам.

Шаг 1: Определите класс Range, который позволяет сравнивать периоды (см. Ниже).

Шаг 2: Извлечение периодов из базы данных:

var ranges = (
    from period in context.Periods
    where period.Imports.Any(i => i.AdminID == adminId)
    select new Range(period.StartDate, period.EndDate.AddDays(1)))
    .ToArray();

Обратите внимание на .ToArray(), чтобы вытянуть все локально.

Шаг 3: Объединение / объединение всех периодов в список непересекающихся периодов:

var mergedPeriods = (
    from range in ranges
    select ranges.Where(p => p.OverlapsWith(range))
        .Aggregate((r1, r2) => r1.Merge(r2)))
    .Distinct();

Чтобы это работало, вам нужен специально разработанный тип Range, который содержит методы OverlapsWith, Merge и Equals. Это может выглядеть так:

public class Range : IEquatable<Range>
{
    public Range(DateTime start, DateTime exclusiveEnd)
    {
        if (exclusiveEnd < start)
            throw new ArgumentException();

        this.Start = start; this.End = exclusiveEnd;
    }

    public DateTime Start { get; private set; }
    public DateTime End { get; private set; }
    public TimeSpan Duration { get { return this.End - this.Start; } }

    public Range Merge(Range other)
    {
        if (!this.OverlapsWith(other)) throw new ArgumentException();

        var start = this.Start < other.Start ? this.Start : other.Start;
        var end = this.End > other.End ? this.End : other.End;

        return new Range(start, end);
    }

    public bool Contains(Range other)
    {
        return this.Start <= other.Start && this.End > other.End;
    }

    public bool OverlapsWith(Range other)
    {
        return this.OverlapsOnStartWith(other) ||
            other.OverlapsOnStartWith(this) ||
            this.Contains(other) ||
            other.Contains(this);
    }

    private bool OverlapsOnStartWith(Range other)
    {
        return this.Start >= other.Start && this.Start < other.End;
    }

    public bool Equals(Range other)
    {
        return this.Start == other.Start && this.End == other.End;
    }
}

Надеюсь, это поможет.

0 голосов
/ 13 апреля 2011

Ну, после долгой борьбы я нашел ответ!С помощью одного запроса к базе данных!И для всеобщего блага выкладывать одинаково.

var oImportPeriods = 
    from o in Imports
    where o.Administration.AdminID == 143
    orderby o.Period.PeriodID
    select o.Period;

var oIgnorePeriodList = (
    from oPeriod in oImportPeriods
    from oSearchPeriod in oImportPeriods
        .Where(o => o.PeriodID != oPeriod.PeriodID)
    where oPeriod.StartDate >= oSearchPeriod.StartDate
    where oPeriod.EndDate <= oSearchPeriod.EndDate
    select oPeriod.PeriodID)
    .Distinct();

var oDeletablePeriods = oAdministrationPeriods
    .Where(o => !oIgnorePeriodList.Contains(o.PeriodID));   

foreach(var o in oDeletablePeriods)
    Console.WriteLine(o.Name);
0 голосов
/ 13 апреля 2011

Вы можете максимально помочь в выборе наибольшего периода и при получении значений путем сравнения значений PeriodID в обеих таблицах.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...