如何使用 NHibernate QueryOver 重新创建这个复杂的 SQL 查询?

Posted

技术标签:

【中文标题】如何使用 NHibernate QueryOver 重新创建这个复杂的 SQL 查询?【英文标题】:How can I recreate this complex SQL Query using NHibernate QueryOver? 【发布时间】:2011-07-07 04:22:33 【问题描述】:

想象以下(简化的)数据库布局:

我们有许多“假期”记录,这些记录与在特定日期前往特定住宿等有关。

在给定一组搜索条件(例如持续时间、出发机场等)的情况下,我想从数据库中提取每个住宿的“最佳”假期(即最低价格)。

同一价格会有多条记录,所以我们需要先按优惠保存(降序),再按出发日期升序选择。

我可以编写如下所示的 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,但如果有必要的话,我也愿意看看如何用它来完成(尽管它使建立搜索条件变得更加困难(或更混乱))。

如果使用 NHibernate 无法做到这一点,那么我可以使用 SQL 查询。在这种情况下,我的问题是可以改进/优化 SQL 吗?

【问题讨论】:

不怕。最后我把它分成两个查询。 【参考方案1】:

我已经设法通过使用 Criteria API 来实现这种动态搜索条件。我遇到的问题是具有内部和外部联接的重复项,特别是与排序和分页有关,我不得不求助于使用 2 个查询,第一个查询进行限制,并将第一个查询的结果用作第二个 creteria 中的“in”子句。

【讨论】:

是的,这就是我最终采取的措施。

以上是关于如何使用 NHibernate QueryOver 重新创建这个复杂的 SQL 查询?的主要内容,如果未能解决你的问题,请参考以下文章

NHibernate 将 sql 查询转换为 NHibernate QueryOver 查询

NHibernate 3.0:没有使用QueryOver的FirstOrDefault()?

NHibernate 使用 QueryOver 和 Future 加入两个集合

使用 NHibernate 3.0 QueryOver 或 LINQ 提供程序的权衡

NHibernate 使用带有 WHERE IN 的 QueryOver

使用 QueryOver 与 NHibernate 进行内部连接的 SQL 查询