【问题标题】:Getting counts for a paged SQL search stored procedure获取分页 SQL 搜索存储过程的计数
【发布时间】:2010-09-20 12:25:07
【问题描述】:

我使用 SQL Server 2005 编写了一个分页搜索存储过程。它需要许多参数并且搜索条件比较复杂。

由于前端架构的原因,我需要能够返回将返回的结果的数量而不实际返回结果。然后前端会再次调用存储过程以获取实际结果。

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

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

人们对这些方法有何看法?以前有没有人以我没有想到的方式解决过这个问题?

他们不能在一次调用中同时获取结果和计数。

谢谢!

【问题讨论】:

标签: sql tsql search stored-procedures paging


【解决方案1】:

我知道这是一个老问题(已经被标记),但是您可以返回一个记录集(也就是结果)并且有一个 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。

【讨论】:

  • 如果您阅读了之前的评论“感谢您的建议。不幸的是,如上所述,即使作为输出参数,前端也无法同时获得两个结果'所以我认为这个解决方案不适用于他们
【解决方案2】:

我确定您已经考虑过这一点:如果数据正在更改 COUNT 并且任何后续的实际分页可能会有所不同(如果添加/删除了行)

你可以有一个用户定义的函数来返回匹配行的 PK,相对容易做一个

从 dbo.MyQueryFunction(@Param1,@Param2)中选择计数(*)

获取计数,然后

选择 Col1, Col2, ... FROM dbo.MyQueryFunction(@Param1, @Param2) AS FN 加入 dbo.MyTable 作为 T ON T.ID = FN.ID ... 更多 JOIN ...

获取数据。

不知道这与 Row_Number 的后续分页效果如何,但它会保留 MyQueryFunction 中包含的实际“查询逻辑” - 您仍将获得要检索的任何列的所有 JOIN 重复Sproc 和函数。

【讨论】:

  • 一个有趣的建议,谢谢。我们已经将代码发布到生产环境中并且运行良好,所以我认为我们目前不会进行任何更改,但我一定会牢记这一点以备不时之需。
【解决方案3】:

我发现这个线程正在研究其他内容,并认为我会提到可以通过一个查询返回结果集和记录数。你只需要一个'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;

【讨论】:

  • 感谢您的建议。不幸的是,如上所述,即使作为输出参数,前端也无法同时获取两个结果。
  • 我昨天病了,所以我完全错过了那部分。
【解决方案4】:

可能对您的具体问题没有帮助,但 SQL 2005 引入了 Row_Number 函数,该函数便于分页检查

Row_number example

比临时表容易得多。

【讨论】:

  • 谢谢。这实际上就是我正在使用的(或者非常相似的东西)。临时表的想法只是为了让我可以在任一方法中使用完全相同的查询来获取结果。否则,我需要控制流逻辑,将我置于需要保持同步的 2 个不同查询中。
【解决方案5】:

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

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

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

因此,我为什么用两种查询方法登陆。我在网上找到的所有内容也都推荐这种方法。

【讨论】:

    【解决方案6】:

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

    也就是说,使用两种不同的程序。原因是你有两个非常不同的动作,它们只是表面上相似。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-06-03
      • 2012-05-03
      • 1970-01-01
      • 2012-03-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-20
      相关资源
      最近更新 更多