繁体   English   中英

如何在存储过程中发生OFFSET之前设置总行数

[英]How to set total number of rows before an OFFSET occurs in stored procedure

我创建了一个存储过程,用于对 DataTable 进行筛选和分页。

问题:我需要为在OFFSET发生之前找到的@TotalRecords设置一个OUTPUT变量,否则它@TotalRecord @RecordPerPage设置为@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 

先感谢您!

你可以尝试这样的事情:

  • 创建一个 CTE 来获取你想要返回的数据
  • 在那里包含一个COUNT(*) OVER()以获得总行数
  • 仅从 CTE 返回一个子集(基于您的OFFSET .. FETCH NEXT

所以你的代码看起来像这样:

-- 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

作为一种习惯,在可能的 null 之前,我更喜欢非 null 条目。 我没有在下面的回复中引用这些内容,并将工作示例仅限于您最关心的两个输入。

我相信可能有一些更干净的方法来应用您的局部变量来过滤查询结果,而无需执行偏移。 您可以返回临时表或永久使用表来清理自身并使用未返回的 ID 作为设置页面的方式。 更流畅,更少大惊小怪。

但是,我知道这并不总是可行的,而且我对那些试图为您解决用例而不试图回答问题的人感到沮丧。 通常有多种方法可以解决任何问题。 你的工作是决定哪一个最适合你的场景。 我们的工作是帮助您找出脚本。

话虽如此,这里有一个使用动态 SQL 的潜在解决方案。 我非常相信动态 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;

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM