我有以下查询:
DECLARE @DaysNotUsed int = 14
DECLARE @DaysNotPhoned int = 7
--Total Unique Students
DECLARE @totalStudents TABLE (SchoolID uniqueidentifier, TotalUniqueStudents int)
INSERT INTO @totalStudents
SELECT
SSGG.School,
COUNT(DISTINCT S.StudentID)
FROM Student S
INNER JOIN StudentStudents_GroupGroups SSGG ON (SSGG.Students = S.StudentID AND SSGG.School = S.School)
INNER JOIN [Group] G ON (G.GroupID = SSGG.Groups AND G.School = SSGG.School)
INNER JOIN SessionHistory SH ON (SH.Student = S.StudentID AND SH.School = S.School AND SH.StartDateTime > GETDATE() - @DaysNotUsed)
WHERE G.IsBuiltIn = 0
AND S.HasStartedProduct = 1
GROUP BY SSGG.School
--Last Used On
DECLARE @lastUsed TABLE (SchoolID uniqueidentifier, LastUsedOn datetime)
INSERT INTO @lastUsed
SELECT
vi.SchoolID,
MAX(sh.StartDateTime)
FROM View_Installation as vi
INNER JOIN SessionHistory as sh on sh.School = vi.SchoolID
GROUP BY vi.SchoolID
SELECT
VI.SchoolID,
INS.DateAdded,
INS.Removed,
INS.DateRemoved,
INS.DateToInclude,
VI.SchoolName AS [School Name],
VI.UsersLicensed AS [Licenses],
ISNULL(TS.TotalUniqueStudents, 0) as [Total Unique Students],
ISNULL(TS.TotalUniqueStudents, 0) * 100 / VI.UsersLicensed as [% of Students Using],
S.State,
LU.LastUsedOn,
DATEDIFF(DAY, LU.LastUsedOn, GETDATE()) AS [Days Not Used],
SI.AreaSalesManager AS [Sales Rep],
SI.CaseNumber AS [Case #],
SI.RenewalDate AS [Renewal Date],
SI.AssignedTo AS [Assigned To],
SI.Notes AS [Notes]
FROM View_Installation VI
INNER JOIN School S ON S.SchoolID = VI.SchoolID
LEFT OUTER JOIN @totalStudents TS on TS.SchoolID = VI.SchoolID
INNER JOIN @lastUsed LU on LU.SchoolID = VI.SchoolID
LEFT OUTER JOIN InactiveReports..SchoolInfo SI ON S.SchoolID = SI.SchoolID
LEFT OUTER JOIN InactiveReports..InactiveSchools INS ON S.SchoolID = INS.SchoolID
WHERE VI.UsersLicensed > 0
AND VI.LastPhoneHome > GETDATE() - @DaysNotPhoned
AND
(
(
SELECT COUNT(DISTINCT S.StudentID)
FROM Student S
INNER JOIN StudentStudents_GroupGroups SSGG ON (SSGG.Students = S.StudentID AND SSGG.School = S.School)
INNER JOIN [Group] G ON (G.GroupID = SSGG.Groups AND G.School = SSGG.School)
WHERE G.IsBuiltIn = 0
AND S.School = VI.SchoolID
) * 100 / VI.UsersLicensed < 50
OR
VI.SchoolID NOT IN
(
SELECT DISTINCT SH1.School
FROM SessionHistory SH1
WHERE SH1.StartDateTime > GETDATE() - @DaysNotUsed
)
)
ORDER BY [Days Not Used] DESC
在SSMS中运行这样的普通sql需要大约10秒才能运行。当我使用完全相同的代码创建存储过程时,查询需要50秒。 proc的实际代码中唯一的区别是IDE默认放入的SET NOCOUNT ON,但是将该行添加到查询中没有任何影响。有什么想法会导致如此戏剧性的减速吗?
编辑我在开头忽略了声明语句。这些不在proc中,而是它的参数。这可能是区别吗?
答案 0 :(得分:2)
我同意潜在的参数嗅探问题,但我也会检查这些设置。
程序:
SELECT uses_ansi_nulls, uses_quoted_identifier
FROM sys.sql_modules
WHERE [object_id] = OBJECT_ID('dbo.procedure_name');
对于查询运行速度快的SSMS查询窗口:
SELECT [ansi_nulls], [quoted_identifier]
FROM sys.dm_exec_sessions
WHERE session_id = @@SPID;
如果其中任何一个不匹配,您可以考虑删除存储过程并使用这两个匹配的设置重新创建它。例如,如果过程具有uses_quoted_identifier = 0且会话的quoted_identifier = 1,则可以尝试:
DROP PROCEDURE dbo.procedure_name;
GO
SET QUOTED_IDENTIFIER ON;
GO
CREATE PROCEDURE dbo.procedure_name
AS
BEGIN
SET NOCOUNT ON;
...
END
GO
理想情况下,所有模块都将使用完全相同的QUOTED_IDENTIFIER和ANSI_NULLS设置创建。设置关闭时可能会创建该过程(两者都默认为打开),或者您执行查询的位置可能会关闭一个或两个选项(您可以在工具/选项/下的SSMS中更改此行为)查询执行/ SQL Server / ANSI)。
我不会对使用不同设置的存储过程的行为做任何免责声明(例如,您可能希望ANSI_NULLS关闭,因此您可以比较NULL = NULL),您必须测试,但是至少你会将正在运行的查询与相同的选项进行比较,这将有助于缩小潜在的参数嗅探问题。但是,如果你故意使用SET ANSI_NULLS OFF,我提醒你找到其他方法,因为这种行为最终将不受支持。
参数嗅探的其他方法:
最后一个选项是我最不喜欢的,但它是在故障排除和用户抱怨时最快/最简单的修复。
答案 1 :(得分:1)
此外,除了提及的所有内容之外,如果您使用的是SQL Server 2008及更高版本,请查看OPTIMIZE FOR UNKNOWN
http://blogs.msdn.com/b/sqlprogrammability/archive/2008/11/26/optimize-for-unknown-a-little-known-sql-server-2008-feature.aspx
答案 2 :(得分:0)
我建议重新编译存储过程的执行计划。
用法:sp_recompile'[target]'
示例:sp_recompile'dbo.GetObject'
当您从SSMS执行查询时,查询计划会在每次执行时自动重做。但是,对于存储过程,sql server会缓存存储过程的执行计划,以及每次调用存储过程时都会使用的执行计划。
您还可以在存储过程中更改要与WITH RECOMPILE子句一起使用的过程。
示例:
CREATE PROCEDURE dbo.GetObject
(
@parm1 VARCHAR(20)
)
WITH RECOMPILE
AS
BEGIN
-- Queries/work here.
END
但是,这会强制执行计划重新编译每次时间调用存储过程。这对于开发/测试,其中proc和/或数据经常变化很有用。确保在将其部署到生产环境时将其删除,因为这可能会影响性能。
sp_recompile仅重新编译执行计划一次。如果您需要在以后再次进行,则需要再次拨打电话。
祝你好运!答案 3 :(得分:0)
好的,谢谢大家的帮助。原来这是一个非常愚蠢的菜鸟错误。我第一次创建proc时,它是在我的用户架构而不是dbo架构下创建的。当我调用proc时,我只是在做'exec proc_name',我现在意识到它正在使用我用户模式下的proc版本。运行'exec dbo.proc_name'按预期运行。