Sql [tempdb])但随后它会执行一个额外的步骤,选择所需的记录子集(这不是一个真正的问题,因为它应该仍然在缓冲池中)以返回到实际表中。更糟糕的是,第二步实际上是双I/O,因为该操作也表示在实际表所在的数据库的事务日志中。但是等等,还有更多的问题:下一次运行查询怎么样?你需要清理这张真正的桌子。无论是通过DELETE还是TRUNCATE TABLE,事务日志中都会显示另一个操作(基于使用这两个操作中的哪一个操作的表示量),加上额外操作所花费的额外时间。另外,我们不要忘记从插入的表中选择子集到实际表中的步骤:它没有机会使用索引,因为您无法为插入的表和删除的表编制索引。并不是说您总是希望向临时表添加索引,但有时它会有所帮助(取决于具体情况),您至少可以选择
过于复杂:当两个进程需要同时运行查询时会发生什么?如果它们共享要转储到的同一个实表,然后为最终输出选择出,则需要添加另一列来区分SPID。它可能是Sql [tempdb])但随后它会执行一个额外的步骤,选择所需的记录子集(这不是一个真正的问题,因为它应该仍然在缓冲池中)以返回到实际表中。更糟糕的是,第二步实际上是双I/O,因为该操作也表示在实际表所在的数据库的事务日志中。但是等等,还有更多的问题:下一次运行查询怎么样?你需要清理这张真正的桌子。无论是通过DELETE还是TRUNCATE TABLE,事务日志中都会显示另一个操作(基于使用这两个操作中的哪一个操作的表示量),加上额外操作所花费的额外时间。另外,我们不要忘记从插入的表中选择子集到实际表中的步骤:它没有机会使用索引,因为您无法为插入的表和删除的表编制索引。并不是说您总是希望向临时表添加索引,但有时它会有所帮助(取决于具体情况),您至少可以选择,sql,sql-server,sql-server-2008,tsql,sqlclr,Sql,Sql Server,Sql Server 2008,Tsql,Sqlclr,过于复杂:当两个进程需要同时运行查询时会发生什么?如果它们共享要转储到的同一个实表,然后为最终输出选择出,则需要添加另一列来区分SPID。它可能是@@SPID。或者它可以是在调用实际表中的初始INSERT之前创建的GUID(这样就可以通过CONTEXT\u INFO()或临时表将其传递给而不是触发器)。无论值是什么,一旦选择了最终输出,它都将用于执行DELETE操作。如果不明显,这一部分会影响前面项目符号中提到的性能问题:TRUNCATE TABLE无法使用,因为它清除了整个表,留下DELETE
@@SPID
。或者它可以是在调用实际表中的初始INSERT
之前创建的GUID(这样就可以通过CONTEXT\u INFO()
或临时表将其传递给而不是触发器)。无论值是什么,一旦选择了最终输出,它都将用于执行DELETE
操作。如果不明显,这一部分会影响前面项目符号中提到的性能问题:TRUNCATE TABLE
无法使用,因为它清除了整个表,留下DELETE FROM dbo.RealTable WHERE ProcessID=@WhateverID代码>作为唯一选项
现在,公平地说,可以从触发器本身中进行最终选择。这将减少一些效率低下的情况,因为数据永远不会进入实际表,也永远不需要删除。它还减少了过度的复杂性,因为不需要通过SPID分离数据。但是,这是一个非常有时间限制的解决方案,因为在SQL Server的下一个版本中,从触发器中返回结果的功能将不再存在,因此,以下内容的MSDN页面如是说:
此功能将在下一版本的Microsoft SQL Server中删除。请勿在新的开发工作中使用此功能,请尽快修改当前使用此功能的应用程序。建议将此值设置为1
唯一实际的方法:
- 查询一次
- 获取行的子集
- 并且仍然获得完整结果集的总行数
就是使用.Net。如果程序是从应用程序代码调用的,请参阅底部的“编辑2”。如果您希望能够通过即席查询随机运行各种存储过程,那么它必须是一个SQLCLR存储过程,这样它就可以是通用的,并且可以用于任何查询,因为存储过程可以返回动态结果集,而函数不能。proc至少需要3个参数:
- @QueryToExec NVARCHAR(最大值)
- @RowsToReturn INT
- @全行整数输出
其思想是使用“Context Connection=true;”来利用内部/进程内连接。然后执行以下基本步骤:
调用ExecuteDataReader()
在读取任何行之前,请执行GetSchemaTable()
从SchemaTable中可以获得结果集字段名和数据类型
从结果集结构构建一个SqlDataRecord
使用该SqlDataRecord
调用SqlContext.Pipe.SendResultsStart(\u DataRecord)
现在开始调用Reader.Read()
对于您调用的每一行:
Reader.GetValues()
DataRecord.SetValues()
SqlContext.Pipe.SendResultRow(\u DataRecord)
RowCounter++
而不是执行典型的“while(Reader.Read())
”,而是包含@RowsToReturn参数:while(Reader.Read()&&RowCounter
在while循环之后,调用SqlContext.Pipe.SendResultsEnd()
关闭结果集(您正在发送的结果集,而不是正在读取的结果集)
然后执行第二个while循环,循环遍历结果的其余部分,但从未获得任何字段:
while(Reader.Read())
{
行计数器++;
}
然后只需设置TotalRows=RowCounter
将传回完整结果集的行数,即使您只返回了前n行:)
我不确定这对temp table方法、dual call方法,甚至@M.Ali的方法(我也尝试过,有点像,但问题是不以列形式发送值)的性能如何,但它应该很好,并且确实按照要求完成了任务
编辑:
更好!另一个选项(上述C建议的变体)是使用T-SQL存储过程中的@@ROWCOUNT
,作为输出
参数发送,而不是在SqlDataReader
中的其余行中循环。因此,存储过程类似于:
CREATE PROCEDURE SchemaName.ProcName
(
@Param1 INT,
@Param2 VARCHAR(05),
@RowCount INT OUTPUT = -1 -- default so it doesn't have to be passed in
)
AS
SET NOCOUNT ON;
{any ol' query}
SET @RowCount = @@ROWCOUNT;
然后,在应用程序代码中,为“@RowCount”创建一个新的SqlParameter,Direction=Output。除最后两个步骤(10和11)更改为:
只需调用Reader.Close()
不使用RowCounter变量,而是设置TotalRows=(int)rowconn
DECLARE @N INT = 10
;WITH CTE AS
(
SELECT
A.data1,
A.data2
FROM mytable A
)
SELECT TOP (@N) * , (SELECT COUNT(*) FROM CTE) Total_Rows
FROM CTE
select count(*)
from ...
where ...
select top x
from ...
where ...
order by ...
CREATE PROCEDURE SchemaName.ProcName
(
@Param1 INT,
@Param2 VARCHAR(05),
@RowCount INT OUTPUT = -1 -- default so it doesn't have to be passed in
)
AS
SET NOCOUNT ON;
{any ol' query}
SET @RowCount = @@ROWCOUNT;