5

我有一些复杂的存储过程,可能会返回数千行,并且需要很长时间才能完成。

有没有办法在查询执行和获取数据之前找出要返回的行数?

这适用于 Visual Studio 2005、Winforms 应用程序和 SQL Server 2005。

4

11 回答 11

6

您提到您的存储过程需要很长时间才能完成。在从数据库中选择行或将行返回给调用者的过程中是否占用了大部分时间?

如果是后者,也许您可​​以创建一个 SP 的镜像版本,它只获取计数而不是实际行。如果是前者,那么您实际上无能为力,因为这是查找符合条件的行的行为,速度很慢。

于 2008-10-31T15:43:03.457 回答
0

制作一个存储过程来首先计算行数。

从表中选择计数(*)

于 2008-10-31T15:42:09.810 回答
0

除非您的应用程序的业务逻辑的某些方面允许计算此值,否则不会。数据库必须执行所有 where 和 join 逻辑来确定行的行数,而这是在 SP 中花费的绝大多数时间。

于 2008-10-31T15:43:13.583 回答
0

如果不执行该过程,您将无法获得该过程的行数。

您可以创建一个接受相同参数的不同过程,其目的是告诉您另一个过程应该返回多少行。但是,此过程所需的步骤通常与主过程的步骤非常相似,以至于它所花费的时间与执行主过程的时间差不多。

于 2008-10-31T15:43:14.533 回答
0

您必须编写不同版本的存储过程才能获得行数。这可能会快得多,因为您可以消除未过滤的连接表,删除排序等。例如,如果您的存储过程执行了以下 sql:

select firstname, lastname, email, orderdate  from 
customer inner join productorder on customer.customerid=productorder.productorderid
where orderdate>@orderdate order by lastname, firstname;

您的计数版本将类似于:

select count(*) from productorder where orderdate>@orderdate;
于 2008-10-31T15:45:37.273 回答
0

一般不会。

通过对存储过程操作的了解,您可能能够得到估计或准确的计数(例如,如果能够快速计算查询的“核心”或“基”表,但它是复杂的连接和/或摘要会推动时间向上)。

但是您必须先调用计数 SP,然后再调用数据 SP,或者您可以考虑使用多结果集 SP。

于 2008-10-31T15:45:57.570 回答
0

获取行数可能需要与获取实际数据一样长的时间,因此我不建议在大多数情况下执行计数。

一些可能性:

1) SQL Server 是否以某种方式公开其查询优化器的发现?即你能解析查询然后获得行数的估计吗?(我不知道 SQL Server)。

2)也许根据用户给你的标准,你可以自己做一些估计。例如,如果用户在客户姓氏字段中输入“S%”来查询订单,您可以确定匹配 7%(例如)的客户记录,并推断查询可能返回大约 7% 的订单记录。

于 2008-10-31T15:51:52.893 回答
0

解决您的问题的方法可能是重写存储过程,以便将结果集限制为某个数字,例如:

SELECT TOP 1000 * FROM tblWHATEVER

在 SQL Server 中,或

SELECT * FROM tblWHATEVER WHERE ROWNUM <= 1000

在甲骨文。或者实施分页解决方案,使每次调用的结果集小到可以接受。

于 2008-10-31T15:55:24.913 回答
0

继续 Tony Andrews 在他的回答中所说的话,您可以通过以下方式获得对您的查询的调用的估计查询计划:

SET showplan_text OFF
GO
SET showplan_all on
GO
--Replace with call you your stored procedure
select * from MyTable
GO 
SET showplan_all ofF
GO

这应该返回一个表或许多表,这些表将使您获得查询的估计行数。

于 2008-10-31T17:01:41.020 回答
0

您需要分析返回的数据集,以确定返回的结果集的逻辑(有意义的)主键是什么。一般来说,这将比完整的过程快得多,因为服务器不是从每个表的每一行的所有列中的数据构造一个结果集,它只是简单地计算行数......一般来说,它甚至可能不会需要从磁盘读取实际的表行来执行此操作,它可能只需要计算索引节点...

然后编写另一个 SQL 语句,该语句仅包含生成这些键列所需的表(希望这是主 sql 查询中表的子集),以及具有相同过滤谓词值的相同 where 子句...

然后将另一个可选参数添加到名为 @CountsOnly 的存储过程中,默认值为 false (0),因此...

Alter Procedure <storedProcName>
@param1 Type, 
-- Other current params
@CountsOnly TinyInt = 0
As
Set NoCount On

   If @CountsOnly = 1
       Select Count(*) 
       From TableA A 
          Join TableB B On   etc. etc...
       Where < here put all Filtering predicates >

   Else
      <Here put old SQL That returns complete resultset with all data>

  Return 0

然后,您可以调用相同的存储过程并将 @CountsOnly 设置为 1 来获取记录数。调用 proc 的旧代码仍会像以前一样运行,因为如果不包含参数值,则参数值默认设置为 false (0)

于 2008-10-31T19:22:25.947 回答
0

至少在技术上可以运行一个将结果集放在临时表中的过程。然后,您可以在将数据从服务器移动到应用程序之前找到行数,并且不必创建两次结果集。

但我怀疑这是否值得麻烦,除非创建结果集需要很长时间,在这种情况下,它可能足够大以至于临时表会成为问题。几乎可以肯定,通过网络移动大表所需的时间将是创建它所需时间的许多倍。

于 2008-10-31T19:49:31.930 回答