Вопрос

При использовании методов SetFirstResult (start) и SetMaxResults (count) для реализации подкачки я заметил, что сгенерированный запрос только выбирает top count * from some_table и он не учитывает параметр start или, по крайней мере, не на уровне базы данных. Похоже, что если я скажу NHibernate выполнить следующий запрос:

var users = session.CreateCriteria<User>()
                   .SetFirstResult(100)
                   .SetMaxResults(5)
                   .List<User>();

105 записей будут проходить между сервером базы данных и приложением, которое позаботится об удалении первых 100 записей. С таблицами, содержащими много строк, это может быть проблемой.

Я подтвердил, что с помощью базы данных SQLite NHibernate использует преимущества OFFSET и LIMIT для фильтрации результатов на уровне базы данных. Мне известно, что в SQL Server 2000 нет эквивалента ключевого слова OFFSET и Oracle ROWNUM , но есть ли обходной путь? Как насчет SQL Server 2005/2008?

Это было полезно?

Решение

T-SQL, вариант языка SQL, который использует Microsoft SQL Server, не содержит предложения limit . Он имеет модификатор select top {...} , который, как вы видите, использует NHibernate с SQL Server 2000.

В SQL Server 2005 корпорация Майкрософт представила функцию Row_Number () over (order by {...}) , которую можно использовать вместо замены предложения limit и вы можете увидеть, как NHibernate использует это в SQL Server 2005/2008.

Запрос для SQLite может выглядеть следующим образом

select c.[ID], c.[Name]
from [Codes] c
where c.[Key] = 'abcdef'
order by c.[Order]
limit 20 offset 40

в то время как похожий запрос для SQL Server 2005 может выглядеть следующим образом

select c.[ID], c.[Name]
from (
    select c.[ID], c.[Name], c.[Order]
        , [!RowNum] = Row_Number() over (order by c.[Order])
    from [Codes] c
    where c.[Key] = 'abcdef'
) c
where c.[!RowNum] > 40 and c.[!RowNum] <= 60
order by c.[Order]

или, используя общие табличные выражения, это может выглядеть как

with
    [Source] as (
        select c.[ID], c.[Name], c.[Order]
            , [!RowNum] = Row_Number() over (order by c.[Order])
        from [Codes] c
        where c.[Key] = 'abcdef'
    )
select c.[ID], c.[Name]
from [Source] c
where c.[!RowNum] > 40 and c.[!RowNum] <= 60
order by c.[Order]

Есть способ сделать это и в SQL Server 2000

select c.[ID], c.[Name]
from (
    select top 20 c.[ID], c.[Name], c.[Order]
    from (
        select top 60 c.[ID], c.[Name], c.[Order]
        from [Codes] c
        where c.[Key] = 'abcdef'
        order by c.[Order]
    ) c
    order by c.[Order] desc
) c
order by c.[Order]

Другие советы

Nhibernate достаточно умен, чтобы оптимизировать запрос. Если вы выберете первые 10 строк, он будет использовать оператор TOP . Если вы выберете не первые строки, он будет использовать RowNum .

В SQL 2000 нет функции RowNum , поэтому обычным запросом невозможно выбрать требуемое количество строк. Для SQL 2000, как я знаю, для такой оптимизации были использованы представления.

В SQL 2005/2008 запрос будет выбирать только необходимые строки.

<Ч>
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top