У меня есть приложение, которое использует несколько принципов хранилища данных, таких как размерное моделирование, для создания отчетов в довольно простой базе данных.
Пример (упрощенный) объект с именем Call выглядит так:
public virtual long Id { get; set; }
public virtual string OriginatorNumber { get; set; }
public virtual string DestinationNumber { get; set; }
public virtual DateDimension DateDimension { get; set; }
Некоторые свойства реальной модели были удалены, поскольку они не имеют отношения к делу. Упрощенное DateDimension выглядит следующим образом:
public virtual long Id { get; set; }
public virtual DateTime Date { get; set; }
public virtual int DayOfMonth { get; set; }
public virtual int Weekday { get; set; }
Таких столбцов НАМНОГО больше - они предварительно заполняются для текущего десятилетия настройкой приложения. Таким образом, каждая дата в течение всего десятилетия имеет строку в этой таблице, и у каждого звонка есть ссылка на дату его совершения. Все это отображается в Fluent NHibernate и работает нормально.
Если я хочу сделать некоторые отчеты, я могу легко сделать это с помощью улучшенного поставщика NHibernate LINQ в версии 3.0. Мы хотели бы использовать LINQ для повышения удобства обслуживания, которое он дает нам, но если мы действительно ДОЛЖНЫ, мы рассмотрим HQL, ICriteria или даже простой SQL.
Итак, скажем, я хочу создать отчет, который показывает количество вызовов от определенное число, разделенное на день недели, в котором они происходят. Я могу легко сделать это следующим образом:
var query = Calls
.Where(c => c.OriginatorNumber == "402")
.GroupBy(c => c.DateDimension.Weekday)
.Select(g => new { Day = g.Key, Calls = g.Count() } );
В этом примере «Calls» - это, по сути, IQueryable, возвращаемый провайдером NHibernates LINQ (Query) через интерфейс репозитория. Приведенный выше запрос дает мне правильные результаты, NHibernate Profiler показывает мне, что SQL довольно оптимален, все в порядке.
Однако, если я хочу сделать что-то более продвинутое, я застреваю. Скажем, мне нужно среднее количество звонков в будний день. Не так уж и далеко от вышесказанного, правда? Мне просто нужно вычислить количество уникальных дат каждого дня недели в наборе результатов, разделить на него общее количество звонков, и все готово - верно? Ну нет, здесь я начинаю сталкиваться с ограничениями провайдера NHibernate LINQ. С помощью LINQ to objects я мог бы создать запрос для этого - что-то вроде
.Select(g => g.Count() / g.GroupBy(c => c.DateDimension.Date).Count());
. Однако это не преобразуется в правильный запрос при использовании в NHibernate. Скорее, он превращает оба вызова .Count () в приведенном выше примере на одно и то же количество (*) записей вызовов, поэтому результат всегда равен 1.
Я МОГ, конечно, просто запросить каждый вызов, день недели и дату как новые анонимный объект, затем выполните вычисления на стороне приложения, но согласно общепринятому мнению, это просто неправильно (тм). Я мог закончить тем, что делал это в отчаянии, хотя, когда таблица вырастает до миллиона ++ вызовов, это причиняет боль.
Ниже приведен SQL-запрос, который дает мне результат, который я ищу.
select ss.Weekday, AVG(cast(ss.Count as decimal))
from
(
select dd.Weekday, dd.Date, COUNT(*) as Count
from Call c
left outer join DateDimension dd
on c.DateDimension_id = dd.Id
where c.OriginatorNumber = '402'
group by dd.Weekday, dd.Date
) ss
group by ss.Weekday
order by ss.Weekday
Можно ли сделать это с помощью поставщика NHibernate LINQ? Или, если это невозможно, как близко я могу подойти до того, как мне придется позволить приложению получить промежуточный результат и сделать все остальное?