静态查询与存储过程的性能问题
Problems with performance of static query vs. stored procedure
我有两个几乎相同的查询,第一个是带有变量的静态查询,第二个是带有输入参数的存储过程。
静态查询
DECLARE @GLNumber INT = 1043
DECLARE @StartDate DATE = '1/1/2015'
DECLARE @EndDate DATE = '4/20/2015'
SELECT
T1.EMPLOYEE AS [Empolyee Number],
T2.LAST_NAME AS [Last Name],
T2.FIRST_NAME AS [First Name],
T1.DST_ACCT_UNIT AS [Distribution Account Unit],
T3.DESCRIPTION,
SUM(T1.WAGE_AMOUNT) AS [Wage Amount]
FROM
JOBCODE T3,
EMPLOYEE T2 LEFT OUTER JOIN
PRTIME T1 ON
T2.COMPANY = T1.COMPANY and
T2.EMPLOYEE = T1.EMPLOYEE
WHERE
T1.DIST_COMPANY = @GLNumber and
CONVERT(DATE, TR_DATE, 101) BETWEEN @StartDate AND @EndDate AND
T1.PAY_SUM_GRP in ('TPC', 'TDS') and
T2.JOB_CODE = T3.JOB_CODE and
T2.COMPANY = T3.COMPANY
GROUP BY
T1.EMPLOYEE,
T2.LAST_NAME,
T2.FIRST_NAME,
T1.DST_ACCT_UNIT,
T3.DESCRIPTION
存储过程
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[MySproc]
(
@GLNumber INT = NULL,
@StartDate DATE = NULL,
@EndDate DATE = NULL
)
AS
BEGIN
SET NOCOUNT ON;
SELECT
T1.EMPLOYEE AS [Empolyee Number],
T2.LAST_NAME AS [Last Name],
T2.FIRST_NAME AS [First Name],
T1.DST_ACCT_UNIT AS [Distribution Account Unit],
T3.DESCRIPTION,
SUM(T1.WAGE_AMOUNT) AS [Wage Amount]
FROM
JOBCODE T3,
EMPLOYEE T2 LEFT OUTER JOIN
PRTIME T1 ON
T2.COMPANY = T1.COMPANY and
T2.EMPLOYEE = T1.EMPLOYEE
WHERE
T1.DIST_COMPANY = @GLNumber and
CONVERT(DATE, TR_DATE, 101) BETWEEN @StartDate AND @EndDate AND
T1.PAY_SUM_GRP in ('TPC', 'TDS') and
T2.JOB_CODE = T3.JOB_CODE and
T2.COMPANY = T3.COMPANY
GROUP BY
T1.EMPLOYEE,
T2.LAST_NAME,
T2.FIRST_NAME,
T1.DST_ACCT_UNIT,
T3.DESCRIPTION
END
正如您所看到的,这两个查询之间最大的区别在于第一个查询使用声明的变量,而第二个使用输入参数。
两个查询 return 相同的正确结果。但是,运行 我系统上的静态查询不到 1 秒,而从我的系统执行存储过程需要 3 1/2 分钟以上。
我不明白为什么静态查询和存储过程之间的执行时间会有如此大的差异(我希望执行时间相似,尤其是在这样一个简单的查询上)。
我遇到这种效率差异的潜在原因是什么?
我知道存储过程通常不会提供相对于静态查询的性能优势,但是,与静态查询相比,我从未遇到过存储过程性能如此差的情况。
有什么办法可以解决这个性能问题吗?我在 SQL Server 2008 R2, SP1 上运行。
澄清
当 运行 存储过程时,我传递的参数与静态查询中使用的参数相同。
在静态版本中,StartDate和EndDate的变量已经赋值。
在SP版中为NULL。
SP 版本必须 运行 涵盖整个数据日期范围,而静态版本只能在相关日期范围内运行(可能使用索引)。
好的,所以我能够通过更新查询中的 FROM
和 WHERE
子句来解决这个问题。本质上,出于某种原因,存储过程不喜欢使用逗号代替 INNER JOIN ... ON
。这使存储过程的执行时间减少到一秒以下。
最终确定的流程如下:
ALTER PROCEDURE [dbo].[rptTipReport_ByCompany]
(
@GLNumber INT = NULL,
@StartDate DATE = NULL,
@EndDate DATE = NULL
)
AS
BEGIN
SET NOCOUNT ON;
SELECT
T1.EMPLOYEE AS [Empolyee Number],
T2.LAST_NAME AS [Last Name],
T2.FIRST_NAME AS [First Name],
T1.DST_ACCT_UNIT AS [Distribution Account Unit],
T3.DESCRIPTION,
SUM(T1.WAGE_AMOUNT) AS [Wage Amount]
FROM
PROD90.dbo.PRTIME T1
LEFT JOIN PROD90.dbo.EMPLOYEE T2 on T1.EMPLOYEE = T2.EMPLOYEE
INNER JOIN PROD90.dbo.JOBCODE T3 on T2.JOB_CODE = T3.JOB_CODE
WHERE
T1.DIST_COMPANY = @GLNumber and
CONVERT(DATE, TR_DATE, 101) BETWEEN @StartDate AND @EndDate AND
T1.PAY_SUM_GRP in ('TPC', 'TDS')
GROUP BY
T1.EMPLOYEE,
T2.LAST_NAME,
T2.FIRST_NAME,
T1.DST_ACCT_UNIT,
T3.DESCRIPTION
END
我有两个几乎相同的查询,第一个是带有变量的静态查询,第二个是带有输入参数的存储过程。
静态查询
DECLARE @GLNumber INT = 1043
DECLARE @StartDate DATE = '1/1/2015'
DECLARE @EndDate DATE = '4/20/2015'
SELECT
T1.EMPLOYEE AS [Empolyee Number],
T2.LAST_NAME AS [Last Name],
T2.FIRST_NAME AS [First Name],
T1.DST_ACCT_UNIT AS [Distribution Account Unit],
T3.DESCRIPTION,
SUM(T1.WAGE_AMOUNT) AS [Wage Amount]
FROM
JOBCODE T3,
EMPLOYEE T2 LEFT OUTER JOIN
PRTIME T1 ON
T2.COMPANY = T1.COMPANY and
T2.EMPLOYEE = T1.EMPLOYEE
WHERE
T1.DIST_COMPANY = @GLNumber and
CONVERT(DATE, TR_DATE, 101) BETWEEN @StartDate AND @EndDate AND
T1.PAY_SUM_GRP in ('TPC', 'TDS') and
T2.JOB_CODE = T3.JOB_CODE and
T2.COMPANY = T3.COMPANY
GROUP BY
T1.EMPLOYEE,
T2.LAST_NAME,
T2.FIRST_NAME,
T1.DST_ACCT_UNIT,
T3.DESCRIPTION
存储过程
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE PROCEDURE [dbo].[MySproc]
(
@GLNumber INT = NULL,
@StartDate DATE = NULL,
@EndDate DATE = NULL
)
AS
BEGIN
SET NOCOUNT ON;
SELECT
T1.EMPLOYEE AS [Empolyee Number],
T2.LAST_NAME AS [Last Name],
T2.FIRST_NAME AS [First Name],
T1.DST_ACCT_UNIT AS [Distribution Account Unit],
T3.DESCRIPTION,
SUM(T1.WAGE_AMOUNT) AS [Wage Amount]
FROM
JOBCODE T3,
EMPLOYEE T2 LEFT OUTER JOIN
PRTIME T1 ON
T2.COMPANY = T1.COMPANY and
T2.EMPLOYEE = T1.EMPLOYEE
WHERE
T1.DIST_COMPANY = @GLNumber and
CONVERT(DATE, TR_DATE, 101) BETWEEN @StartDate AND @EndDate AND
T1.PAY_SUM_GRP in ('TPC', 'TDS') and
T2.JOB_CODE = T3.JOB_CODE and
T2.COMPANY = T3.COMPANY
GROUP BY
T1.EMPLOYEE,
T2.LAST_NAME,
T2.FIRST_NAME,
T1.DST_ACCT_UNIT,
T3.DESCRIPTION
END
正如您所看到的,这两个查询之间最大的区别在于第一个查询使用声明的变量,而第二个使用输入参数。
两个查询 return 相同的正确结果。但是,运行 我系统上的静态查询不到 1 秒,而从我的系统执行存储过程需要 3 1/2 分钟以上。
我不明白为什么静态查询和存储过程之间的执行时间会有如此大的差异(我希望执行时间相似,尤其是在这样一个简单的查询上)。
我遇到这种效率差异的潜在原因是什么?
我知道存储过程通常不会提供相对于静态查询的性能优势,但是,与静态查询相比,我从未遇到过存储过程性能如此差的情况。
有什么办法可以解决这个性能问题吗?我在 SQL Server 2008 R2, SP1 上运行。
澄清 当 运行 存储过程时,我传递的参数与静态查询中使用的参数相同。
在静态版本中,StartDate和EndDate的变量已经赋值。
在SP版中为NULL。
SP 版本必须 运行 涵盖整个数据日期范围,而静态版本只能在相关日期范围内运行(可能使用索引)。
好的,所以我能够通过更新查询中的 FROM
和 WHERE
子句来解决这个问题。本质上,出于某种原因,存储过程不喜欢使用逗号代替 INNER JOIN ... ON
。这使存储过程的执行时间减少到一秒以下。
最终确定的流程如下:
ALTER PROCEDURE [dbo].[rptTipReport_ByCompany]
(
@GLNumber INT = NULL,
@StartDate DATE = NULL,
@EndDate DATE = NULL
)
AS
BEGIN
SET NOCOUNT ON;
SELECT
T1.EMPLOYEE AS [Empolyee Number],
T2.LAST_NAME AS [Last Name],
T2.FIRST_NAME AS [First Name],
T1.DST_ACCT_UNIT AS [Distribution Account Unit],
T3.DESCRIPTION,
SUM(T1.WAGE_AMOUNT) AS [Wage Amount]
FROM
PROD90.dbo.PRTIME T1
LEFT JOIN PROD90.dbo.EMPLOYEE T2 on T1.EMPLOYEE = T2.EMPLOYEE
INNER JOIN PROD90.dbo.JOBCODE T3 on T2.JOB_CODE = T3.JOB_CODE
WHERE
T1.DIST_COMPANY = @GLNumber and
CONVERT(DATE, TR_DATE, 101) BETWEEN @StartDate AND @EndDate AND
T1.PAY_SUM_GRP in ('TPC', 'TDS')
GROUP BY
T1.EMPLOYEE,
T2.LAST_NAME,
T2.FIRST_NAME,
T1.DST_ACCT_UNIT,
T3.DESCRIPTION
END