2015-09-29 42 views
4

问题

我们正试图了解为什么通过调用存储过程与在执行存储过程的内容执行相同的代码一个查询窗口显示非常不同的执行时间,但返回完全相同的183行结果集。通过EXEC SP执行相同的代码与在查询窗口中报告相同的结果EXEC SP代码,但不同的执行时间

的Test1

执行从SSMS以下SP需要5分钟,以返回结果。

EXEC uspFleetSummaryReportSelectByDateCommand @UserID = 1468,@date = '2015年9月28日'

作为参考,这是SP细节:

CREATE PROCEDURE [dbo].[uspFleetSummaryReportSelectByDateCommand] 
    (
     @UserID int, 
     @Date DateTime 
    ) 
    AS 

    DECLARE @CustomerID int 
    SET @CustomerID = (Select CustomerID FROM [User] WHERE UserID = @UserID) 

    SELECT j.JourneyID, 
      j.DeviceID, 
      j.StartDate, 
     j.EndDate, 
     ISNULL(JourneyDistance, 0.0) AS [JourneyDistance], 
     CONVERT(VARCHAR(8), DATEADD(SECOND, DATEDIFF(SECOND, j.StartDate, j.EndDate), 0), 114) AS [Duration], 
     v.Registration, 
     v.InitOdometer, 
     jt.Name AS [JourneyType], 
     dt.Name AS [DeviceType], 
     PrivateJourney = (dbo.fxIsPrivateJourney(j.JourneyTypeID, j.DeviceID, @UserID)), 
     CONVERT(VARCHAR(8), DATEADD(SECOND, ISNULL(e.IdleTime, 0), 0), 114) AS [IdleTime] 
FROM Journey j WITH (NOLOCK) 
INNER JOIN Vehicle v WITH (NOLOCK) ON v.DeviceID = j.DeviceID 
INNER JOIN JourneyType jt WITH (NOLOCK) ON jt.JourneyTypeID = j.JourneyTypeID 
INNER JOIN Device d WITH (NOLOCK) ON d.DeviceID = j.DeviceID 
    INNER JOIN Configuration config WITH (NOLOCK) ON config.ConfigurationID = d.ConfigurationID 
INNER JOIN DeviceType dt WITH (NOLOCK) ON dt.DeviceTypeID = config.DeviceTypeID 
LEFT OUTER JOIN (
    SELECT 
    e.JourneyId, 
    SUM(DATEDIFF(SECOND, e.StartDateTime, e.EndDateTime)) AS [IdleTime] 
    FROM [Event] e WITH (NOLOCK) 
    WHERE e.JourneyId = JourneyID AND e.EventType = 4/*Idle Event*/ 
    GROUP BY e.JourneyId 
) e ON e.JourneyId = j.JourneyID 
WHERE j.StartDate BETWEEN @Date AND DATEADD(DAY,1,@Date) 
     AND (j.JourneyDistance IS NOT NULL) 
     AND DATEDIFF(MINUTE,j.StartDate,ISNULL(j.EndDate,getdate())) > 0 
     AND j.DeviceID IN (Select v.DeviceID 
            FROM Vehicle v WITH (NOLOCK) 
           INNER JOIN Customer c WITH (NOLOCK) ON   c.CustomerID = v.CustomerID 
           INNER JOIN [User] u ON u.CustomerID = c.CustomerID 
           WHERE v.CustomerID = @CustomerID AND u.UserID = @UserID 
             AND (v.LevelOneID = u.LevelOneID Or u.LevelOneID is null) 
             AND (v.LevelTwoID = u.LevelTwoID Or u.LevelTwoID is null) 
             AND (v.LevelThreeID = u.LevelThreeID Or u.LevelThreeID is null) 
             AND (v.LevelFourID = u.LevelFourID Or u.LevelFourID is null) 
             AND (v.LevelFiveID = u.LevelFiveID Or u.LevelFiveID is null) 
             AND (v.DriverID = u.LevelSixID Or u.LevelSixID is null) 
             AND ISNULL(v.HideFromCustomer,0) != 1 
          ) 
ORDER BY Registration,j.JourneyID 

的Test2

但执行相同的SP代码并设置变量需要10秒才能返回结果。

请在下面找到与变量集相同的SP。以下脚本从SSMS查询窗口执行。

DECLARE @UserID INT = 13651 
DECLARE @Date DATETIME = '2015-09-28' 

DECLARE @CustomerID int 
SET @CustomerID = (Select CustomerID FROM [User] WHERE UserID = @UserID) 

SELECT j.JourneyID, 
     j.DeviceID, 
     j.StartDate, 
     j.EndDate, 
     ISNULL(JourneyDistance, 0.0) AS [JourneyDistance], 
     CONVERT(VARCHAR(8), DATEADD(SECOND, DATEDIFF(SECOND, j.StartDate, j.EndDate), 0), 114) AS [Duration], 
     v.Registration, 
     v.InitOdometer, 
     jt.Name AS [JourneyType], 
     dt.Name AS [DeviceType], 
     PrivateJourney = (dbo.fxIsPrivateJourney(j.JourneyTypeID, j.DeviceID, @UserID)), 
     CONVERT(VARCHAR(8), DATEADD(SECOND, ISNULL(e.IdleTime, 0), 0), 114) AS [IdleTime] 
FROM Journey j WITH (NOLOCK) 
INNER JOIN Vehicle v WITH (NOLOCK) ON v.DeviceID = j.DeviceID 
INNER JOIN JourneyType jt WITH (NOLOCK) ON jt.JourneyTypeID = j.JourneyTypeID 
INNER JOIN Device d WITH (NOLOCK) ON d.DeviceID = j.DeviceID 
INNER JOIN Configuration config WITH (NOLOCK) ON config.ConfigurationID =  d.ConfigurationID 
INNER JOIN DeviceType dt WITH (NOLOCK) ON dt.DeviceTypeID = config.DeviceTypeID 
LEFT OUTER JOIN (
    SELECT 
    e.JourneyId, 
    SUM(DATEDIFF(SECOND, e.StartDateTime, e.EndDateTime)) AS [IdleTime] 
    FROM [Event] e WITH (NOLOCK) 
    WHERE e.JourneyId = JourneyID AND e.EventType = 4/*Idle Event*/ 
    GROUP BY e.JourneyId 
) e ON e.JourneyId = j.JourneyID 
    WHERE j.StartDate BETWEEN @Date AND DATEADD(DAY,1,@Date) 
     AND (j.JourneyDistance IS NOT NULL) 
     AND DATEDIFF(MINUTE,j.StartDate,ISNULL(j.EndDate,getdate())) > 0 
     AND j.DeviceID IN (Select v.DeviceID 
            FROM Vehicle v WITH (NOLOCK) 
            INNER JOIN Customer c WITH (NOLOCK) ON  c.CustomerID = v.CustomerID 
            INNER JOIN [User] u ON u.CustomerID =  c.CustomerID 
            WHERE v.CustomerID = @CustomerID AND u.UserID = @UserID 
              AND (v.LevelOneID = u.LevelOneID Or u.LevelOneID is null) 
              AND (v.LevelTwoID = u.LevelTwoID Or u.LevelTwoID is null) 
              AND (v.LevelThreeID =  u.LevelThreeID Or u.LevelThreeID is null) 
              AND (v.LevelFourID =  u.LevelFourID Or u.LevelFourID is null) 
              AND (v.LevelFiveID =  u.LevelFiveID Or u.LevelFiveID is null) 
              AND (v.DriverID = u.LevelSixID Or u.LevelSixID is null) 
              AND ISNULL(v.HideFromCustomer,0) != 1 
          ) 
ORDER BY Registration,j.JourneyID 

调试至今

并排的两个语句侧比较它们是相同的栏变量的设置。

并排比较结果集,它们是相同的。

单独选择变量CUSTOMERID需要几毫秒。

传递的日期变量格式相同。

我们已经多次运行此测试来排除缓存相关问题。

查询执行计划在两次测试中都被检查过。在执行SP时,执行TEST1时清除了表EVENT上的索引。

指数升

CREATE NONCLUSTERED INDEX [290915_EventTypeJourneyID, EventTypeJID,>] ON  [dbo].[Event] 
(
    [EventType] ASC, 
    [JourneyId] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB =  OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS =  ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
GO 

结果

执行时间为TEST1下降到1秒。

问题

确定这样的原则问题就解决了,但是从根本上,我不明白为什么,基本上都运行相同的代码两次测试之间的性能差距?其相同的代码应该使用相同的索引,执行时间应该相似。

谢谢你对这种行为的任何见解。

参考

sql server 2008 64bit标准版。

Table.JOURNEY(350米行)

CREATE TABLE [dbo].[Journey](
    [JourneyID] [int] IDENTITY(1,1) NOT NULL, 
    [StartAddress] [varchar](500) NULL, 
    [StartPostcode] [varchar](50) NULL, 
    [EndAddress] [varchar](500) NULL, 
    [EndPostcode] [varchar](50) NULL, 
    [JourneyTypeID] [int] NULL, 
    [Comment] [varchar](500) NULL, 
    [DriverID] [int] NULL, 
    [StartDate] [datetime] NULL, 
    [EndDate] [datetime] NULL, 
    [IdleTimeEngineOn] [int] NULL, 
    [TimeSinceLastJourney] [int] NULL, 
    [JourneyDistance] [decimal](8, 2) NULL, 
    [DeviceID] [int] NOT NULL, 
    [tempJourneyID] [int] NULL, 
    [tempCustomerID] [int] NULL, 
CONSTRAINT [Journey_PK] PRIMARY KEY CLUSTERED 
(
    [JourneyID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

GO 

SET ANSI_PADDING OFF 
GO 

ALTER TABLE [dbo].[Journey] WITH CHECK ADD CONSTRAINT [Device_Journey_FK1]  FOREIGN KEY([DeviceID]) 
REFERENCES [dbo].[Device] ([DeviceID]) 
GO 

ALTER TABLE [dbo].[Journey] CHECK CONSTRAINT [Device_Journey_FK1] 
GO 

ALTER TABLE [dbo].[Journey] WITH CHECK ADD CONSTRAINT [Driver_Journey_FK1]  FOREIGN KEY([DriverID]) 
REFERENCES [dbo].[Driver] ([DriverID]) 
GO 

ALTER TABLE [dbo].[Journey] CHECK CONSTRAINT [Driver_Journey_FK1] 
GO 

ALTER TABLE [dbo].[Journey] WITH NOCHECK ADD CONSTRAINT [JourneyType_Journey_FK1] FOREIGN KEY([JourneyTypeID]) 
REFERENCES [dbo].[JourneyType] ([JourneyTypeID]) 
GO 

ALTER TABLE [dbo].[Journey] CHECK CONSTRAINT [JourneyType_Journey_FK1] 
GO 

Table.EVENT(36米行)

CREATE TABLE [dbo].[Event](
    [EventID] [int] IDENTITY(1,1) NOT NULL, 
    [StartDateTime] [datetime] NULL, 
    [EndDateTime] [datetime] NULL, 
    [StartLocationID] [int] NOT NULL, 
    [EndLocationID] [int] NULL, 
    [AlertRaised] [bit] NULL, 
    [EventRuleID] [int] NULL, 
    [DeviceID] [int] NOT NULL, 
    [EventMessage] [varchar](max) NULL, 
    [TopSpeed] [decimal](4, 1) NULL, 
    [SpeedZone] [int] NULL, 
    [EventType] [int] NULL, 
    [ImpactId] [int] NULL, 
    [NotificationStatus] [bit] NULL, 
    [CableBreakZone0] [int] NULL, 
    [CableBreakDistance0] [int] NULL, 
    [CableBreakZone1] [int] NULL, 
    [CableBreakDistance1] [int] NULL, 
    [AdValue] [int] NULL, 
    [DriverId] [int] NULL, 
    [VehicleId] [int] NULL, 
    [JourneyId] [int] NULL, 
CONSTRAINT [Event_PK] PRIMARY KEY CLUSTERED 
(
    [EventID] ASC 
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY =  OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 
) ON [PRIMARY] 

GO 

SET ANSI_PADDING OFF 
GO 

ALTER TABLE [dbo].[Event] WITH CHECK ADD CONSTRAINT [Device_Event_FK1]  FOREIGN KEY([DeviceID]) 
REFERENCES [dbo].[Device] ([DeviceID]) 
GO 

ALTER TABLE [dbo].[Event] CHECK CONSTRAINT [Device_Event_FK1] 
GO 

ALTER TABLE [dbo].[Event] WITH CHECK ADD CONSTRAINT [Event_Impact_FK]  FOREIGN KEY([ImpactId]) 
REFERENCES [dbo].[Impact] ([ImpactID]) 
GO 

ALTER TABLE [dbo].[Event] CHECK CONSTRAINT [Event_Impact_FK] 
GO 

ALTER TABLE [dbo].[Event] WITH CHECK ADD CONSTRAINT [EventRule_Event_FK1]  FOREIGN KEY([EventRuleID]) 
REFERENCES [dbo].[EventRule] ([EventRuleID]) 
GO 

ALTER TABLE [dbo].[Event] CHECK CONSTRAINT [EventRule_Event_FK1] 
GO 

ALTER TABLE [dbo].[Event] WITH CHECK ADD CONSTRAINT [FK_Event_Driver]  FOREIGN KEY([DriverId]) 
REFERENCES [dbo].[Driver] ([DriverID]) 
GO 

ALTER TABLE [dbo].[Event] CHECK CONSTRAINT [FK_Event_Driver] 
GO 

ALTER TABLE [dbo].[Event] WITH CHECK ADD CONSTRAINT [FK_Event_Journey]  FOREIGN KEY([JourneyId]) 
REFERENCES [dbo].[Journey] ([JourneyID]) 
ON DELETE CASCADE 
GO 

ALTER TABLE [dbo].[Event] CHECK CONSTRAINT [FK_Event_Journey] 
GO 

ALTER TABLE [dbo].[Event] WITH CHECK ADD CONSTRAINT [FK_Event_Vehicle]  FOREIGN KEY([VehicleId]) 
REFERENCES [dbo].[Vehicle] ([VehicleID]) 
ON DELETE CASCADE 
GO 

ALTER TABLE [dbo].[Event] CHECK CONSTRAINT [FK_Event_Vehicle] 
GO 
+0

你分析了这么多,问问题的方式是明确的..very explantive – mohan111

+1

您可能需要阅读有关参数嗅探 – Shnugo

+0

另一个想法(特别是如果你的数据库有点老了...)可能是ARITHABORT,这也被称为造成奇怪的性能差异... – Shnugo

回答

2

有几件事会影响SQL Server中查询计划的创建,这可能会导致真正奇怪的事情发生。

  1. 用于存储过程的计划是(通常)在第一执行创建,使用从该时刻的参数。即使参数改变,该计划也会保存并用于将来的所有执行。

    • 如果例如统计信息发生变化,程序可以得到一个新的计划。

    • 如果程序使得最优计划完全不同,具体取决于传入的值或者程序中有很多所谓的可选参数(例如field = @variable或@variable为NULL编码的风格) - 这可能会导致非常糟糕的情况,这通常被称为参数嗅探。

    • 用于编译计划的参数可以在计划中最左边的对象的属性中看到。

  2. 如果您在Management Studio中相同的语句,但参数是局部变量是在一开始分配,该声明将具有未知值的优化,因为值没有在这一点众所周知,即使看起来很明显这些值是什么。

    • 这与使用过程中定义局部变量的程序是一样的。
  3. 如果您正在运行具有不同会话设置的应用程序,则在步骤1中创建的计划可能无法使用,并且会存储和使用不同的计划。这可能会导致在应用程序和管理工作室中使用相同参数执行相同过程时表现不同的情况。

有关详细信息,你可以检查出来,例如通过Slow in the Application, Fast in SSMS? Understanding Performance Mysteries厄兰Sommarskog。

编辑:为了理解发生了什么,请始终查看实际执行计划和统计IO输出。这些应该告诉你为什么有些东西比其他慢(除非它的阻止,等待等相关)

+0

谢谢你的帖子詹姆斯,对推迟的投票抱歉。 –

1
  • Source
  • 在批处理的运行查询之间

差分SSMS中的脚本与在存储过程中运行查询至少有两种方式:

  1. 在创建SP之前,请使用SET ANSI_NULLS ON
  2. 使用内部变量,而不是在您的查询SP的参数是这样的:

    DECLARE @pUserID INT, @pDate DATETIME; 
    SELECT @pUserID = @UserID, @pDate = @Date; 
    
    SELECT ... @pUserID ...; 
    
+0

你有第二点的参考或解释吗?我不会猜到这会有所作为 – HoneyBadger

+0

@HoneyBadger请检查问题下面的最后一条评论;)。 –

0

有一个SQL Server 2008中 运行此一次固定它,我同样的问题:

USE master 
ALTER DATABASE [dbname] SET ARITHABORT ON WITH NO_WAIT; 

ssms始终设置ARITHABORT,用dotnet编写的客户端不会,这就是为什么两者都使用具有不同统计信息的不同计划。

相关问题