Как я могу воссоздать этот сложный SQL-запрос с помощью NHibernate QueryOver? - PullRequest
41 голосов
/ 02 марта 2011

Представьте себе следующую (упрощенную) структуру базы данных: Database Layout

У нас есть много записей "праздников", которые относятся к посещению определенного размещения в определенную дату и т. Д.

Я бынапример, выбрать из базы данных «лучший» отпуск для каждого размещения (т. е. самая низкая цена), учитывая набор критериев поиска (например, продолжительность, аэропорт вылета и т. д.).

Будет несколько записей с одинаковымицена, поэтому нам нужно выбрать сохранение предложения (по убыванию), затем по дате отъезда по возрастанию.

Я могу написать SQL для этого, который выглядит следующим образом (я не говорю, что это обязательно самый оптимальныйway):

SELECT *
FROM Holiday h1 INNER JOIN (

    SELECT  h2.HolidayID,
        h2.AccommodationID,
        ROW_NUMBER() OVER (
            PARTITION BY h2.AccommodationID
            ORDER BY OfferSaving DESC
        ) AS RowNum
    FROM Holiday h2 INNER JOIN (

        SELECT  AccommodationID,
            MIN(price) as MinPrice
        FROM Holiday
        WHERE TradeNameID = 58001
        /*** Other Criteria Here ***/
        GROUP BY AccommodationID

    ) mp
    ON mp.AccommodationID = h2.AccommodationID
    AND mp.MinPrice = h2.price
    WHERE TradeNameID = 58001
    /*** Other Criteria Here ***/

) x on h1.HolidayID = x.HolidayID and x.RowNum = 1

Как вы можете видеть, это использует подзапрос в другом подзапросе.

Однако по нескольким причинам я бы предпочел добиться такого же результата в NHibernate.

В идеале это должно быть сделано с помощью QueryOver - причина в том, что я выстраиваю критерии поиска динамически, и это намного проще с беглым I QueryOver.nterface.(Вначале я надеялся использовать NHibernate Linq, но, к сожалению, он недостаточно зрелый).

После долгих усилий (будучи новичком в NHibernate) я смог воссоздать самый внутренний запрос, которыйвыбирает все варианты размещения и их минимальную цену.

public IEnumerable<HolidaySearchDataDto> CriteriaFindAccommodationFromPricesForOffers(IEnumerable<IHolidayFilter<PackageHoliday>> filters, int skip, int take, out bool hasMore)
    {
        IQueryOver<PackageHoliday, PackageHoliday> queryable = NHibernateSession.CurrentFor(NHibernateSession.DefaultFactoryKey).QueryOver<PackageHoliday>();

        queryable = queryable.Where(h => h.TradeNameId == website.TradeNameID);

        var accommodation = Null<Accommodation>();
        var accommodationUnit = Null<AccommodationUnit>();
        var dto = Null<HolidaySearchDataDto>();

        // Apply search criteria
        foreach (var filter in filters)
            queryable = filter.ApplyFilter(queryable, accommodationUnit, accommodation);

        var query1 = queryable

            .JoinQueryOver(h => h.AccommodationUnit, () => accommodationUnit)
            .JoinQueryOver(h => h.Accommodation, () => accommodation)
            .SelectList(hols => hols
                                    .SelectGroup(() => accommodation.Id).WithAlias(() => dto.AccommodationId)
                                    .SelectMin(h => h.Price).WithAlias(() => dto.Price)
            );

        var list = query1.OrderByAlias(() => dto.Price).Asc
            .Skip(skip).Take(take+1)
            .Cacheable().CacheMode(CacheMode.Normal).List<object[]>();

        // Cacheing doesn't work this way...
        /*.TransformUsing(Transformers.AliasToBean<HolidaySearchDataDto>())
        .Cacheable().CacheMode(CacheMode.Normal).List<HolidaySearchDataDto>();*/

        hasMore = list.Count() == take;

        var dtos = list.Take(take).Select(h => new HolidaySearchDataDto
                    {
                        AccommodationId = (string)h[0],
                        Price = (decimal)h[1],
                    });

        return dtos;
    }

Поэтому мой вопрос ...

Любые идеи о том, как добиться того, чего я хочу, с помощью QueryOver, или еслинеобходимый Criteria API?

Я бы предпочел не использовать HQL, но если это необходимо, я хочу посмотреть, как это можно сделать с этим тоже (это усложняет (или усложняет) построение)хотя и по критериям поиска).

Если это просто невозможно с помощью NHibernate, то я мог бы использовать запрос SQL.В таком случае мой вопрос: можно ли улучшить / оптимизировать SQL?

1 Ответ

1 голос
/ 23 января 2012

Мне удалось достичь такого динамического критерия поиска с помощью API Criteria.Проблема, с которой я столкнулся, заключалась в дублировании с внутренними и внешними объединениями, особенно в связи с сортировкой и разбиением на страницы, и мне пришлось прибегнуть к использованию 2 запросов, 1-го запроса для ограничения и использования результата 1-го запроса в качестве предложения «in» во 2-м creteria.

...