Как обычно, я предпочитаю ненулевые записи перед возможным нулевым. Я не ссылался на них в своем ответе ниже и ограничил рабочий пример только двумя входными данными, которые вас больше всего интересуют.
Я полагаю, что могут быть более чистые способы применения ваших локальных переменных для фильтрации запроса. результаты без необходимости выполнять смещение. Вы можете вернуться к временной таблице или постоянной таблице использования, которая очищает себя и использует идентификаторы, которые не возвращаются в качестве способа задания страниц. Плавнее, с меньшими усилиями.
Однако я понимаю, что это не всегда выполнимо, и я разочарован тем, кто пытается решить ваш сценарий использования для вас, не пытаясь ответить на вопрос. Довольно часто есть несколько способов решения любой проблемы. Ваша задача - решить, какой из них лучший в вашем сценарии. Наша задача - помочь вам разобраться в сценарии.
С учетом вышесказанного, вот потенциальное решение с использованием Dynami c SQL. Я большой сторонник динамического c SQL и широко его использую для пользовательского контроля таблиц и простоты управления отображением ETL.
use TestCatalog;
set nocount on;
--Builds a temp table, just for test purposes
drop table if exists ##TestOffset;
create table ##TestOffset
(
Id int identity(1,1)
, RandomNumber decimal (10,7)
);
--Inserts 1000 random numbers between 0 and 100
while (select count(*) from ##TestOffset) < 1000
begin
insert into ##TestOffset
(RandomNumber)
values
(RAND()*100)
end;
set nocount off;
go
create procedure dbo.TestOffsetProc
@StartIndex int = null --I'll reference this like a page number below
, @RecordsPerPage int = null
as
begin
declare @MaxRows int = 30; --your front end will probably manage this, but don't trust it. I personally would store this on a table against each display so it can also be returned dynamically with less manual intrusion to this procedure.
declare @FirstRow int;
--Quick entry to ensure your record count returned doesn't excede max allowed.
if @RecordsPerPage is null or @RecordsPerPage > @MaxRows
begin
set @RecordsPerPage = @MaxRows
end;
--Same here, making sure not to return NULL to your dynamic statement. If null is returned from any variable, the entire statement will become null.
if @StartIndex is null
begin
set @StartIndex = 0
end;
set @FirstRow = @StartIndex * @RecordsPerPage
declare @Sql nvarchar(2000) = 'select
tos.*
from ##TestOffset as tos
order by tos.RandomNumber desc
offset ' + convert(nvarchar,@FirstRow) + ' rows
fetch next ' + convert(nvarchar,@RecordsPerPage) + ' rows only'
exec (@Sql);
end
go
exec dbo.TestOffsetProc;
drop table ##TestOffset;
drop procedure dbo.TestOffsetProc;