Самый простой способ сделать это - прямой SQL.Примерно так:
DateRange.find_by_sql(%q{
select a.*
from date_ranges a
join date_ranges b on
a.id < b.id
and (
(a.ends_at >= b.starts_at and a.ends_at <= b.ends_at)
or (a.starts_at >= b.starts_at and a.starts_at <= b.ends_at)
or (a.starts_at <= b.starts_at and a.ends_at >= b.ends_at)
)
where season_id = ?
}, season_id)
Основная идея - присоединить таблицу к себе, чтобы вы могли легко сравнивать диапазоны.a.id < b.id
предназначен для получения уникальных результатов и отфильтровывания случаев "диапазоны соответствует самому себе".Внутренние условия or
проверяют оба типа перекрытий:
[as-----ae] [as-----ae]
[bs-----be] [bs-----be]
и
[as--------------ae] [as----ae]
[bs----be] [bs--------------be]
Возможно, вы захотите подумать о конечных точках, хотя этот запрос учитывает два интервала для перекрытияесли они совпадают только в конечной точке и, возможно, это не то, что вам нужно.
Предположительно, у вас уже есть уникальное ограничение на (season_id, starts_at, ends_at)
троек и, вероятно, вы уже гарантируете, что starts_at <= ends_at
.