次の(簡略化された)データベースレイアウトを想像してみてください。
特定の日に特定の宿泊施設に行くことなどに関連する多くの「休日」の記録があります。
一連の検索条件(期間、出発空港など)を前提として、データベースから各宿泊施設に行く「最高の」休日(つまり最低価格)を取得したいと思います。
同じ価格のレコードが複数あるため、オファーの保存(降順)、出発日の昇順で選択する必要があります。
私はこれを行うために次のような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を改善/最適化できるかということです。