41

次の(簡略化された)データベースレイアウトを想像してみてください。 データベースレイアウト

特定の日に特定の宿泊施設に行くことなどに関連する多くの「休日」の記録があります。

一連の検索条件(期間、出発空港など)を前提として、データベースから各宿泊施設に行く「最高の」休日(つまり最低価格)を取得したいと思います。

同じ価格のレコードが複数あるため、オファーの保存(降順)、出発日の昇順で選択する必要があります。

私はこれを行うために次のようなSQLを書くことができます(これが必ずしも最適な方法であるとは限りません):

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を使用して実行されます。これは、検索条件を動的に構築するためです。これは、QueryOverの流暢なインターフェイスを使用するとはるかに簡単になります。(私は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を使用したくないのですが、それが必要な場合は、HQLを使用してどのように実行できるかを確認します(ただし、検索条件を作成するのが難しくなります(または面倒になります)。

これがNHibernateを使用して実行できない場合は、SQLクエリを使用できます。その場合、私の質問はSQLを改善/最適化できるかということです。

4

1 に答える 1

1

Criteria APIを使用して、このような動的な検索基準を達成することができました。私が遭遇した問題は、内部結合と外部結合の重複であり、特に並べ替えとページ付けに関連しており、2つのクエリ、制限のための1番目のクエリ、および2番目のクレテリアの「in」句としての1番目のクエリの結果の使用に頼らざるを得ませんでした。

于 2012-01-23T13:36:04.650 に答える