代码之家  ›  专栏  ›  技术社区  ›  Tom H zenazn

获取分页SQL搜索存储过程的计数

  •  2
  • Tom H zenazn  · 技术社区  · 17 年前

    我使用SQLServer2005编写了一个分页搜索存储过程。它需要许多参数,搜索条件也相当复杂。

    由于前端架构,我需要能够返回返回的结果数量 没有 实际上返回结果。然后,前端将第二次调用存储过程以获取实际结果。

    一方面,我可以编写两个存储过程——一个处理计数,一个处理实际数据,但我需要在至少两个不同的地方维护搜索逻辑。或者,我可以编写存储过程,使其接受一个位参数,并在此基础上返回数据或仅返回一个计数。也许用数据填充一个临时表,如果只是计数,就从中进行计数,否则从中进行选择。这里的问题是,计数过程可以被优化,所以看起来会有很多额外的开销(必须获得不需要的列等)。此外,在存储过程中使用这种逻辑可能会导致糟糕的查询计划,因为它在两种使用之间来回切换。

    系统中的数据量并不太高(即使是更大的表,也只有几百万行)。不过,可能会有许多并发用户。

    人们对这些方法有什么看法?以前有人用我没有想过的方式解决过这个问题吗?

    他们 不能 从一次通话中同时获取结果和计数。

    谢谢!

    6 回复  |  直到 17 年前
        1
  •  4
  •   Mitchel Sellers    17 年前

    我个人倾向于双查询方法,是的,你必须在两个地方维护搜索逻辑,但我发现性能优化的好处和代码的整体清洁度最终会得到回报。

    使用传递给单个过程的标志是一种潜在的解决方案,但我发现这很难维护,尤其是对于复杂的搜索逻辑。

    使用临时表等的方法,只会增加比所需更多的开销。

    因此,为什么我选择了双查询方法。我在网上找到的所有东西都推荐这种方法。

        2
  •  2
  •   ChrisLively    17 年前

    这不是一个正常的问题,你通常希望在获得页面的同时获得总计数。

    也就是说,使用两种不同的程序。原因是你有两种截然不同的行为,只是表面上相似。

        3
  •  2
  •   Kristen    16 年前

    我相信你已经考虑过这一点:如果数据正在更改COUNT,那么任何后续的实际分页都可能不同(如果添加/删除行)

    您可以使用一个用户定义函数来返回匹配行的PKs,这相对容易实现

    SELECT COUNT(*) FROM dbo.MyQueryFunction(@Param1, @Param2)
    

    计算一下,然后

    SELECT Col1, Col2, ...
    FROM dbo.MyQueryFunction(@Param1, @Param2) AS FN
         JOIN dbo.MyTable AS T
             ON T.ID = FN.ID
         ... more JOINs ...
    

    以获取数据。

    不知道这与Row_Number在后续分页中有多好,但它会保留MyQueryFunction中包含的实际“查询逻辑”——你仍然会在Sproc和Function中重复检索任何列的所有JOIN。

        4
  •  1
  •   PhilGriffin    17 年前

    可能无法解决您的特定问题,但SQL 2005引入了Row_Number函数,该函数对于分页检查非常方便

    Row_number example

    比临时表容易得多。

        5
  •  1
  •   cloggins    16 年前

    我发现这个线程在研究其他东西,我想我应该提到,通过一个查询可以返回结果集和记录计数。您只需要一个“out”参数来携带该值。下面是一个Oracle示例的复制/粘贴,但SQL Server的技术非常相似(我无法访问SQL Server atm)。

    SQL Server的一大特点是,您可能需要使用row_number()与rownum。

    procedure get_sample_results (
        startrow in number default 1,
        numberofrows in number default 10,
        whereclause in varchar2,
        matchingrows out number,
        rc  out sys_refcursor
    )
    is
        stmnt varchar2(5000);
        endrow number;
    begin
    
        stmnt := stmnt || 'select * from table t where 1=1';
        if whereclause is not null then
            stmnt := stmnt || ' and ' || whereclause;
        end if;
    
        execute immediate 'select count(*) from (' || stmnt || ')' into matchingrows;
    
        stmnt := 'select * from (' || stmnt || ') where rownum between :1 and :2';        
    
        -- must subtract one to compenstate for the inclusive between clause
        endrow := startrow + numberofrows - 1;
        open rc for stmnt using startrow, endrow;
    
    end get_sample_results;
    
        6
  •  0
  •   Pure.Krome    15 年前

    我知道这是一个老问题(已经标记了),但您可以返回一个记录集(也称为结果)并具有一个OUTPUT(或多个输出)值,这意味着您只需要一次往返数据库的操作。

    这是我大声想出来的(而且已经过了我的就寝时间……)

    CREATE PROCEDURE WhatEver
    (
       @SomeParam1 NVARCHAR(200),
       ....
       @SomeParam_X INT,
       @NumberOfResults INTEGER OUTPUT
    )
    BEGIN
        SET NOCOUNT ON
    
        -- Do your search stuff.
        -- ....
        SELECT Whatever
        FROM WhatWhat
        ...
    
        -- Ok, the results/recordset has been sent prepared.
        -- Now the rowcount
        SET @NumberOfResults = @@ROWCOUNT
    END
    

    HTH。

    推荐文章