【发布时间】:2015-07-03 07:56:55
【问题描述】:
我的表结构如下:
AuditUserMethods:
+---------------+---------------+----------+
| ColumnName | DataType | Nullable |
+---------------+---------------+----------+
| Id | INT | NOT NULL |
| CreatedDate | DATETIME | NOT NULL |
| ApiMethodName | NVARCHAR(MAX) | NOT NULL |
| Request | NVARCHAR(MAX) | NOT NULL |
| Result | NVARCHAR(MAX) | NOT NULL |
| Method_Id | INT | NOT NULL |
| User_Id | INT | NULL |
+---------------+---------------+----------+
AuditUserMethodErrorCodes:
+--------------------+----------+----------+
| ColumnName | DataType | Nullable |
+--------------------+----------+----------+
| Id | INT | NOT NULL |
| AuditUserMethod_Id | INT | NOT NULL |
| ErrorCode | INT | NOT NULL |
+--------------------+----------+----------+
ID 是两个表中的 PK。存在一对多的关系。一个AuditUserMethod 可以有多个AuditUserMethodErrorCodes。因此,FK AuditUserMethod_Id。
AuditUserMethods 表中的AuditUserMethod_Id 和CreatedDate 都有两个非聚集索引。
该过程的目的是返回基于过滤器的分页结果集。 @PageSize 确定要返回多少行,@PageIndex 确定要返回哪个页面。所有其他变量都用于过滤。
返回三个结果集。
- 包含
AuditUserMethods详细信息 - 包含
AuditUserMethodErrorCodes详细信息 - 包含找到的总行数(即,如果页面大小为 1000 并且有 5000 行符合所有条件,则返回 5000)。
存储过程:
CREATE PROCEDURE [api].[Audit_V1_GetAuditDetails]
(
@Users XML = NULL,
@Methods XML = NULL,
@ErrorCodes XML = NULL,
@FromDate DATETIME = NULL,
@ToDate DATETIME = NULL,
@PageSize INT = 5,
@PageIndex INT = 0
)
AS
BEGIN
DECLARE @UserIds TABLE (Id INT)
DECLARE @MethodNames TABLE (Name NVARCHAR(256))
DECLARE @ErrorCodeIds TABLE (Id INT)
DECLARE @FilterUsers BIT = 0
DECLARE @FilterMethods BIT = 0
DECLARE @FilterErrorCodes BIT = 0
INSERT @UserIds
SELECT
x.y.value('.', 'int')
FROM
@Users.nodes('Ids/x/@i') AS x (y)
INSERT @MethodNames
SELECT
x.y.value('.', 'NVARCHAR(256)')
FROM
@Methods.nodes('ArrayOfString/string') AS x (y)
INSERT @ErrorCodeIds
SELECT
x.y.value('.', 'int')
FROM
@ErrorCodes.nodes('Ids/x/@i') AS x (y)
IF EXISTS (SELECT TOP 1 0 FROM @UserIds)
SET @FilterUsers = 1
IF EXISTS (SELECT TOP 1 0 FROM @MethodNames)
SET @FilterMethods = 1
IF EXISTS (SELECT TOP 1 0 FROM @ErrorCodeIds)
SET @FilterErrorCodes = 1
DECLARE @StartRow INT = @PageIndex * @Pagesize
DECLARE @PageDataResults TABLE (Id INT,
CreatedDate DATETIME,
ApiMethodName NVARCHAR(256),
Request NVARCHAR(MAX),
Result NVARCHAR(MAX),
MethodId INT,
UserId INT,
TotalRows INT);
WITH PageData AS
(
SELECT
id AS id
, createddate AS createddate
, apimethodname AS apimethodname
, request AS request
, result AS result
, method_id AS method_id
, user_id AS user_id
, ROW_NUMBER() OVER (ORDER BY createddate DESC, id DESC) AS row_number
, COUNT(*) OVER() as TotalRows
FROM
dbo.AuditUserMethods AS aum
WHERE
(@FromDate IS NULL OR
(@FromDate IS NOT NULL AND aum.createddate > @FromDate))
AND (@ToDate IS NULL OR
(@ToDate IS NOT NULL AND aum.createddate < @ToDate))
AND (@FilterUsers = 0 OR
(@FilterUsers = 1 AND aum.user_id IN (SELECT Id FROM @UserIds)))
AND (@FilterMethods = 0 OR
(@FilterMethods = 1 AND aum.ApiMethodName IN (SELECT Name FROM @MethodNames)))
AND (@FiltererRorCodes = 0 OR
(@FiltererRorCodes = 1
AND EXISTS (SELECT 1
FROM AuditUserMethodErrorCodes e
WHERE e.AuditUserMethod_Id = aum.Id
AND e.ErrorCode IN (SELECT Id FROM @ErrorCodeIds)
)
)
)
)
INSERT @PageDataResults
SELECT TOP (@Pagesize)
PageData.id AS id
, PageData.createddate AS createddate
, PageData.apimethodname AS apimethodname
, PageData.request AS request
, PageData.result AS result
, PageData.method_id AS method_id
, PageData.user_id AS user_id
, PageData.TotalRows AS totalrows
FROM
PageData
WHERE
PageData.row_number > @StartRow
ORDER BY
PageData.createddate DESC
SELECT
Id, CreatedDate, ApiMethodName, Request, Result, MethodId, UserId
FROM
@PageDataResults
SELECT
aumec.AuditUserMethod_Id, aumec.ErrorCode
FROM
@PageDataResults ps
INNER JOIN
AuditUserMethodErrorCodes aumec ON ps.Id = aumec.AuditUserMethod_Id
SELECT TOP 1
TotalRowsNumberOfReturnedAuditEntries
FROM @PageDataResults
END
AuditUserMethods 表包含 500,000 行,AuditUserMethodErrorCodes 包含 67843 行。
我正在使用以下参数执行该过程:
EXEC [api].[Audit_V1_GetAuditDetails] @Users = N'<Ids><x i="1" /></Ids>'
,@Methods = NULL
,@ErrorCodes = N'<Ids />'
,@FromDate = '2015-02-15 07:18:59.613'
,@ToDate = '2015-07-02 08:18:59.613'
,@Pagesize = 5000
,@PageIndex = 0
存储过程只需 2 秒多的时间即可执行并返回 5000 行。我需要这个存储过程运行得更快,但我不知道如何改进它。
根据实际执行计划。相对于批次,占 99% 的是 CTE。在 CTE 中,Sort 占据了 95% 的成本:
【问题讨论】:
-
第一次执行是否很快(服务器重新启动后或运行
DBCC FREEPROCCACHE后),然后在更改参数时立即变慢?或者,它只是慢? -
即使在释放 proc 缓存之后似乎也相当一致。
-
我终于完成了我的回答stackoverflow.com/a/31202079/659190
标签: sql tsql stored-procedures sql-server-2014 sqlperformance