2009-10-30 19 views
13

Podczas korzystania SetFirstResult(start) i SetMaxResults(count) metody wdrożyć stronicowania Zauważyłem, że wygenerowane zapytanie nie tylko select top count * from some_table i nie bierze się pod uwagę parametr start lub przynajmniej nie na poziomie bazy danych. Wydaje się, że jeśli będę instruować NHibernate wykonać następujące zapytanie:NHibernate stronicowania z SQL Server

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

105 rekordy tranzytowy pomiędzy serwerem bazy danych i aplikacji, która zajmie się rozebrać pierwsze 100 rekordów. W przypadku tabel zawierających wiele wierszy może to stanowić problem.

Sprawdziłem, że z bazą danych SQLite NHibernate wykorzystuje słowa kluczowe OFFSET i LIMIT do filtrowania wyników na poziomie bazy danych. Jestem świadomy, że nie ma odpowiednika słowa kluczowego OFFSET i Oracle ROWNUM w SQL Server 2000, ale czy istnieje jakieś obejście tego problemu? A co z SQL Server 2005/2008?

Odpowiedz

16

T-SQL, wariant języka SQL, który wykorzystuje Microsoft SQL Server, nie posiada limit klauzuli. Ma select top {...} modyfikator, który można zobaczyć NHibernate korzystając z SQL Server 2000.

SQL Server 2005, Microsoft wprowadził funkcję Row_Number() over (order by {...}), który może być stosowany jako zamiennik klauzuli limit i widać NHibernate wykorzystując tego z SQL Server 2005/2008.

Zapytanie SQLite może wyglądać

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

podczas gdy podobne zapytanie SQL Server 2005 może wyglądać

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] 

Lub przy wspólnym stole z wyrażeń, to może wyglądać

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] 

Istnieje sposób, aby to zrobić w SQL Server 2000 oraz

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] 
+0

Świetna odpowiedź @Justice. Dziękuję za Twój czas. –

4

Nhibernate jest wystarczająco inteligentny, aby zoptymalizować zapytanie. Jeśli wybierzesz pierwsze 10 wierszy, zostanie użyta instrukcja TOP. Jeśli wybierzesz nie pierwsze rzędy, użyjesz RowNum.

W sql 2000 nie ma funkcji RowNum, dlatego w zwykłym zapytaniu niemożliwe jest wybranie wymaganej liczby wierszy. Dla sql 2000, jak wiem dla takich widoków optymalizacji zostały użyte.

W zapytaniu SQL 2005/2008 zostanie wybrane tylko wymagane wiersze.


+3

Sprawdziłem, że z sql 2005/2008 NHibernate używa funkcji 'row_number()'. Wydaje się, że przy sql 2000 muszę napisać widoki lub zapisane procedury, aby osiągnąć ten sam efekt. –