NHibernate подкачки с SQL Server
-
22-07-2019 - |
Вопрос
При использовании методов 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 запрос будет выбирать только необходимые строки.
<Ч>