如何测量查询持续时间而不显示查询结果?

时间:2017-08-31 12:25:14

标签: sql sql-server

我需要测量几个查询的持续时间,如下所示:

declare @dtStart1 as datetime;
declare @dtStart2 as datetime;
declare @dtStart3 as datetime;
declare @dtStart4 as datetime;
declare @dtStart5 as datetime;
declare @dtStart6 as datetime;
declare @dtStart7 as datetime;
declare @dtStart8 as datetime;
declare @dtStart9 as datetime;
declare @dtStart10 as datetime;
declare @duration1 as int;
declare @duration2 as int;
declare @duration3 as int;
declare @duration4 as int;
declare @duration5 as int;
declare @duration6 as int;
declare @duration7 as int;
declare @duration8 as int;
declare @duration9 as int;
declare @duration10 as int;

set @dtStart1 = (select getutcdate());
--query1
set @duration1 = (select datediff(millisecond, @dtStart1, GETUTCDATE()));

set @dtStart2 = (select getutcdate());
--query2
set @duration2 = (select datediff(millisecond, @dtStart2, GETUTCDATE()));

set @dtStart3 = (select getutcdate());
--query3
set @duration3 = (select datediff(millisecond, @dtStart3, GETUTCDATE()));

set @dtStart4 = (select getutcdate());
--query4
set @duration4 = (select datediff(millisecond, @dtStart4, GETUTCDATE()));

set @dtStart5 = (select getutcdate());
--query5
set @duration5 = (select datediff(millisecond, @dtStart5, GETUTCDATE()));

set @dtStart6 = (select getutcdate());
--query6
set @duration6 = (select datediff(millisecond, @dtStart6, GETUTCDATE()));

set @dtStart7 = (select getutcdate());
--query7
set @duration7 = (select datediff(millisecond, @dtStart7, GETUTCDATE()));

set @dtStart8 = (select getutcdate());
--query8
set @duration8 = (select datediff(millisecond, @dtStart8, GETUTCDATE()));

set @dtStart9 = (select getutcdate());
--query9
set @duration9 = (select datediff(millisecond, @dtStart9, GETUTCDATE()));

set @dtStart10 = (select getutcdate());
--query10
set @duration10 = (select datediff(millisecond, @dtStart10, GETUTCDATE()));

select @duration1 / 1000.0 as q1,
       @duration2 / 1000.0 as q2,
       @duration3 / 1000.0 as q3,
       @duration4 / 1000.0 as q4,
       @duration5 / 1000.0 as q5,
       @duration6 / 1000.0 as q6,
       @duration7 / 1000.0 as q7,
       @duration8 / 1000.0 as q8,
       @duration9 / 1000.0 as q9,
       @duration10 / 1000.0 as q10;

问题是除了我真正感兴趣的结果之外,我还得到了查询的结果。我尝试为每个查询使用cursor,但即使是长时间查询也是如此。可能它只是定义但未执行。我已尝试将FMTONLY设置为ON,然后设置为OFF,但这对于长查询也是即时的,甚至在结果中显示了列名称。我想实现执行查询,获取持续时间就像查询通常是unning的情况一样,但不会将其返回给我的应用程序服务器,在那里处理查询可能返回的数百万条记录会有问题,而不是提到内存的巨大浪费与获得我感兴趣的一行的理想结果相比,即结果。

2 个答案:

答案 0 :(得分:3)

很少有选择。

  1. 抑制查询结果集的一种明显方法是将查询结果插入到#temp表中,然后删除临时表。这会影响查询运行时间,但相对容易实现。只需在查询INTO #temp之后添加SELECT子句即可。调用应用程序不需要更改。

  2. 更改调用应用程序并使其期望这些结果集。测量"第一行时间"一旦应用程序收到第一行就停止查询。实施这将是一项相当重要的任务。

  3. 更改查询,以使其结果存储在变量中,而不是临时表中。每列一个变量。

  4. 注意:在评论中指出Martin Smith,将列值分配到变量中可能会改变计划的形状,如answer中所示: sql execution latency when assign to a variable,所以你应该谨慎使用选项3。

    例如,如果您有查询

    SELECT 
        Col1
        ,Col2
        ,Col3
    FROM YourTable
    ... some complex logic
    ;
    

    将其更改为以下内容:

    DECLARE @VarCol1 bigint;
    DECLARE @VarCol2 int;
    DECLARE @VarCol3 datetime2(0);
    -- use appropriate types that match the query columns
    
    SELECT 
        @VarCol1 = Col1
        ,@VarCol2 = Col2
        ,@VarCol3 = Col3
    FROM YourTable
    ... some complex logic
    ;
    

    这样的查询将完全运行(而不是在SELECT COUNT(*)中包装查询),但其结果将存储在局部变量中。每个新行都将覆盖变量值,但它应该比使用#temp table更少开销。

    您可以通过添加

    轻松验证和比较方法1和3
    SET STATISTICS TIME ON;
    SET STATISTICS IO ON; 
    

    在查询和

    之前
    SET STATISTICS TIME OFF;
    SET STATISTICS IO OFF;
    
    查询后

    尝试运行原始查询,查询并将结果保存到#temp表中,查询并将结果保存到变量中并比较CPU和读数。

    在我的测试中,对于将结果保存到变量中的正常查询和查询,读取次数相同。 elapsed time中包含变量的查询要快得多,但有CPU time,因为没有网络流量。

    将结果保存到临时表中的查询具有更多读取,并且比将结果保存到变量的查询稍慢。

    我有一个大表,我的测试查询只是从中读取1M行:

    SELECT 
    TOP (1000000)
    [ID]
    ,[ElevatorID]
    ,[TimestampUTC]
    FROM [dbo].[ArchivePlaybackStatsDay];
    
    
    DECLARE @VarID bigint;
    DECLARE @VarElevatorID int;
    DECLARE @VarTimestampUTC int;
    
    SELECT 
    TOP (1000000)
    @VarID = [ID]
    ,@VarElevatorID = [ElevatorID]
    ,@VarTimestampUTC = [TimestampUTC]
    FROM [dbo].[ArchivePlaybackStatsDay];
    
    
    SELECT 
    TOP (1000000)
    [ID]
    ,[ElevatorID]
    ,[TimestampUTC]
    INTO #Temp
    FROM [dbo].[ArchivePlaybackStatsDay];
    
    DROP TABLE #Temp;
    

    我在SQL Sentry Plan Explorer中运行它并得到了这些统计信息:

    stats

    您可以看到第一行和第二行的读取相同,CPU已关闭,但持续时间非常不同,因为第一个查询实际上将1M行传输到客户端。与使用变量的第二个查询相比,使用#temp表的第三个查询有一些额外的开销。

    我添加了另一个变体,它将所有列转换为varbinary变量以统一变量声明。不幸的是,转换为varbinary,特别是varbinary(max)会产生明显的开销。

    DECLARE @VarBin varbinary(8000);
    
    SELECT 
    TOP (1000000)
    @VarBin = [ID]
    ,@VarBin = [ElevatorID]
    ,@VarBin = [TimestampUTC]
    FROM [dbo].[ArchivePlaybackStatsDay];
    
    DECLARE @VarBinMax varbinary(max);
    
    SELECT 
    TOP (1000000)
    @VarBinMax = [ID]
    ,@VarBinMax = [ElevatorID]
    ,@VarBinMax = [TimestampUTC]
    FROM [dbo].[ArchivePlaybackStatsDay];
    

    stats2

答案 1 :(得分:1)

您可以尝试通过

获得非常近似的衡量标准
declare @dummyCounter as int;
set @dummyCounter = (Select count(*) 
from ( 
/* original query */ 
) t);

最终它可能与原始计划有不同的计划