Вопрос по c#, tsql, queryover, nhibernate – Подзапрос Nhibernate с объединением нескольких столбцов

1

У меня есть структура базы данных для планов и планов в следующих отношениях:

 +------+                  +-------------+
 | Plan |  --------------> | PlanVersion |
 +------+  1        (1..n) +-------------+

PlanVersion - это таблица версий, которая отслеживает все изменения версий и имеетActiveFromData а такжеActiveToData столбцы показывают нам, когда эта версия была активной. План также может иметь подпланы, которые могут меняться во времени, поэтому PlanVersion также имеетParrentPlanId столбец, который говорит нам, какой был текущий подплан для версии.

То, что я хочу, чтобы получить все изменения всех SubPlans с некоторого времени и для конкретного плана. Этот запрос - то, с чем я пришел:

DECLARE @since datetime;
set @since = '2014-08-27 12:00:00.000';

DECLARE @parrentPlan bigint;
set @parrentPlan = 1;

SELECT pv.* 
FROM [dbo].[PlanVersion] pv
INNER JOIN
    /* Query Over subselect*/
    (
       SELECT PlanId, MAX(ActiveFromDate) AS MaxActiveFromDate
       FROM [dbo].[PlanVersion] pv 
       WHERE pv.[email protected]
       GROUP BY PlanId
    ) groupedVersions
ON pv.ParentPlanId = groupedVersions.PlanId 
    AND pv.ActiveFromDate = groupedVersions.MaxActiveFromDate
WHERE (pv.ActiveFromDate>[email protected] OR pv.ActiveToDate>@since) 

Теперь я хочу перевести это в Nhibernate QueryOver: у меня есть этот код

var subquery = QueryOver.Of<PlanVersion>()
                    .Where(x => x.ParentPlan.Id == parrentPlanId)
                    .Select(
                         Projections.Group<PlanVersion>(e => e.ParrentPlan.Id),
                         Projections.Max<PlanVersion>(e => e.ActiveFromDate)
                    );

Но я не знаю, как написать это внутреннее соединение двух столбцов из запроса в QueryOver.

Заметки:

Мы используем Nhibernate 3.3 с 4.0 в тестированииЭтот запрос будет частью опроса, поэтому производительность очень важна для меня
К сожалению, у вас не так много вариантов в мире NH. Вы всегда можете написать хранимую процедуру и вернуться к ней для этого конкретного запроса Andrew Whitaker
Если производительность - ваша главная проблема - я бы остановился на raw sql (используйте именованный sql-запрос). Я никогда не возражаю против использования правильного инструмента для правильной работы ... NHibernate дает тонны гибкости в этом отношении. DanP
Это действительно разочарование, что мы не можем использовать подзапрос в качестве производной таблицы. Мой босс сказал нет хранимым процессам. Мне нужно вернуть 4 последних вендора по максимуму (транзакции) для каждого продукта. Не нашел нигде решения с месяца. Если у вас есть идеи, помогите. Builder
Вы не можете присоединиться к производной таблице с помощью QueryOver. Вы должны присоединиться к сопоставленным ассоциациям сущностей. Andrew Whitaker
Спасибо за ответ, но это большое разочарование для NHibernate. Я знаю, что могу сделать QueryOver <PlanVersion> .WithSubquery.WhereProperty <PlanVersion> (pv => pv.ParrentPlan.Id) .In (подзапрос), но это работает, только если я выбираю идентификаторы в подзапросе. Есть ли какое-либо решение или альтернатива для этого в Nhibernate? Они должны решать такие проблемы в зрелой среде ORM, такой как NHibernate. LightCZ

Ваш Ответ

1   ответ
0

Я бы сказал, что это решение. На самом деле мы должны использовать немного более сложный SQL. Этот подход я уже глубоко объяснил здесь:

Запрос ссылки на HasMany

Ниже приведен только черновик, основанный на черновике вашего подзапроса. То, что мы делаем, на самом деле создает два подвыбора (проверьте предполагаемый SQLВот)

PlanVersion planVersion = null;

// the most INNER SELECT
var maxSubquery = QueryOver.Of<PlanVersion>()
   .SelectList(l => l
    .SelectGroup(item => item.ParentPlan.Id)
    .SelectMax(item => item.ActiveFromDate)
    )
    // WHERE Clause
   .Where(item => item.ParentPlan.Id == planVersion.ParentPlan.Id)
   // HAVING Clause
   .Where(Restrictions.EqProperty(
      Projections.Max<PlanVersion>(item => item.ActiveFromDate),
      Projections.Property(() => planVersion.ActiveFromDate)
    ));

// the middle SELECT
var successSubquery = QueryOver.Of<PlanVersion>(() => planVersion )
    // the Plan ID
    .Select(pv => pv.ParentPlan.Id)
    .WithSubquery
    .WhereExists(maxSubquery)
    ;

имея эти подзапросы, мы можем запросить сам план:

// the most outer SELECT
var query = session.QueryOver<Plan>()
    .WithSubquery
    .WhereProperty(p => p.Id)
    .In(successSubquery)
    .List<Plan>();

Там могут быть некоторые незначительные опечатки, но проект должен дать вам четкий ответ, как ...

Хорошо, спасибо, так я решил свою проблему. LightCZ
Удивительно видеть, сэр! Наслаждайтесь NHibernate, отличным инструментом;) Radim Köhler

Похожие вопросы