Как установить общее количество строк перед смещением в хранимой процедуре - PullRequest
0 голосов
/ 31 марта 2020

Я создал хранимую процедуру, которая фильтрует и разбивает на страницы для DataTable.

Проблема : мне нужно установить переменную OUTPUT для @TotalRecords, найденную до OFFSET происходит, в противном случае он устанавливает @TotalRecord на @RecordPerPage.

Я перепутался с CTE и также просто пробую это:

SELECT *, @TotalRecord = COUNT(1)
FROM dbo

Но это тоже не работает.

Вот моя хранимая процедура, с большей частью извлеченных вещей:

ALTER PROCEDURE [dbo].[SearchErrorReports]
    @FundNumber varchar(50) = null,
    @ProfitSelected bit = 0,

    @SortColumnName varchar(30) = null,
    @SortDirection varchar(10) = null,
    @StartIndex int = 0,
    @RecordPerPage int = null,

    @TotalRecord INT = 0 OUTPUT  --NEED TO SET THIS BEFORE OFFSET!
AS
BEGIN
    SET NOCOUNT ON;

    SELECT *
    FROM 
        (SELECT *
         FROM dbo.View
         WHERE (@ProfitSelected = 1 AND Profit = 1)) AS ERP
    WHERE   
        ((@FundNumber IS NULL OR @FundNumber = '') 
         OR (ERP.FundNumber LIKE '%' + @FundNumber + '%'))
    ORDER BY      
        CASE 
           WHEN @SortColumnName = 'FundNumber' AND @SortDirection = 'asc' 
              THEN ERP.FundNumber 
        END ASC,
        CASE 
           WHEN @SortColumnName = 'FundNumber' AND @SortDirection = 'desc' 
              THEN ERP.FundNumber 
        END DESC
        OFFSET @StartIndex ROWS 
           FETCH NEXT @RecordPerPage ROWS ONLY 

Заранее спасибо!

Ответы [ 2 ]

0 голосов
/ 01 апреля 2020

Как обычно, я предпочитаю ненулевые записи перед возможным нулевым. Я не ссылался на них в своем ответе ниже и ограничил рабочий пример только двумя входными данными, которые вас больше всего интересуют.

Я полагаю, что могут быть более чистые способы применения ваших локальных переменных для фильтрации запроса. результаты без необходимости выполнять смещение. Вы можете вернуться к временной таблице или постоянной таблице использования, которая очищает себя и использует идентификаторы, которые не возвращаются в качестве способа задания страниц. Плавнее, с меньшими усилиями.

Однако я понимаю, что это не всегда выполнимо, и я разочарован тем, кто пытается решить ваш сценарий использования для вас, не пытаясь ответить на вопрос. Довольно часто есть несколько способов решения любой проблемы. Ваша задача - решить, какой из них лучший в вашем сценарии. Наша задача - помочь вам разобраться в сценарии.

С учетом вышесказанного, вот потенциальное решение с использованием 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;
0 голосов
/ 31 марта 2020

Вы можете попробовать что-то вроде этого:

  • создать CTE, который получает данные, которые вы хотите вернуть
  • , включите туда COUNT(*) OVER(), чтобы получить общее количество строк
  • возвращает только подмножество (на основе вашего OFFSET .. FETCH NEXT) из CTE

Таким образом, ваш код будет выглядеть примерно так:

-- CTE definition - call it whatever you like
WITH BaseData AS
(
    SELECT
        -- select all the relevant columns you need
        p.ProductID,
        p.ProductName,
        -- using COUNT(*) OVER() returns the total count over all rows
        TotalCount = COUNT(*) OVER()
    FROM 
        dbo.Products p
)
-- now select from the CTE - using OFFSET/FETCH NEXT, get only those rows you
-- want - but the "TotalCount" column still contains the total count - before 
-- the OFFSET/FETCH
SELECT *
FROM BaseData
ORDER BY ProductID
OFFSET 20 ROWS FETCH NEXT 15 ROWS ONLY
...