嘿我有以下存储过程,它按步骤
运行它的第二步似乎需要很长时间,因为对于每个用户,我在一个非常大的表上执行选择查询。
就尺寸而言 我的tblUserQuestionnaireHistory中有大约4000个用户和90000行。
继承人SP
ALTER PROCEDURE [spGetStoreTrainingSummary_Test]
(
@staffId INT = default,
@storeTypeId INT = default,
@storeId INT = default,
@county VARCHAR(50) = default,
@programmeId INT = default,
@profileId INT = default,
@showNulls INT = default,
@position VARCHAR(50) = default,
@roaId INT = default
)
AS
BEGIN
SET NOCOUNT ON;
-- Place all users inner join stores into a temp table
CREATE TABLE #TempMainTable(
[id] INT,
[profileId] INT,
[position] VARCHAR(50),
[storeId] INT,
[county] VARCHAR(50),
[storeTypeId] INT,
[roaId] INT
)
INSERT #TempMainTable
SELECT tblUsers.id, tblUsers.profileId, tblUsers.position, tblStores.id as storeId, tblStores.county, tblStores.storeTypeId, tblStores.roaID
FROM tblUsers INNER JOIN tblStores ON tblUsers.storeId = tblStores.id
WHERE (tblUsers.statusId = 1) AND (tblStores.statusId = 1)
IF @profileId > 0 --## Filter by profile
BEGIN
DELETE FROM #TempMainTable WHERE profileId <> @profileId
END
IF len(@position) > 0 --## Filter by position
BEGIN
DELETE FROM #TempMainTable WHERE position <> @position
END
IF @storeId > 0 --## Filter by store
BEGIN
DELETE FROM #TempMainTable WHERE storeId <> @storeId
END
IF len(@county) > 0 --## Filter by county
BEGIN
DELETE FROM #TempMainTable WHERE county <> @county
END
IF @storeTypeId > 0 --## Filter by storeTypeId
BEGIN
DELETE FROM #TempMainTable WHERE storeTypeId <> @storeTypeId
END
IF @roaId > 0 --## Filter by roaId
BEGIN
DELETE FROM #TempMainTable WHERE roaId <> @roaId
END
-- SELECT * FROM #TempMainTable
CREATE TABLE #TempTable(
[userId] INT,
[menuName] varchar(250),
[stepId] int,
[programmeId] int,
[result] varchar(250)
)
DECLARE @UserId INT
DECLARE @MaxStep INT
DECLARE @Result VARCHAR(100)
DECLARE @UserList CURSOR
SET @UserList = CURSOR FOR
SELECT id
FROM #TempMainTable
GROUP BY id
OPEN @UserList
FETCH NEXT FROM @UserList INTO @UserId
WHILE (@@FETCH_STATUS = 0)
BEGIN
--## Staff Induction Programme
SET @MaxStep = (SELECT MAX(stepId) AS maxId FROM tblUserQuestionnaireHistory WHERE (userId = @UserId) AND (programmeId = 13) AND (success = 1))
SET @Result = (SELECT CASE WHEN @MaxStep = 9 THEN 'Passed' WHEN @MaxStep <> 9 THEN 'Step ' + + CAST(@MaxStep AS VARCHAR(10)) + ' completed out of 9' ELSE 'No steps completed yet' END as Result)
INSERT #TempTable
SELECT @UserId, 'Staff Induction Programme ©', @MaxStep, 13, @Result
--PRINT @UserId
--PRINT @MaxStep
--PRINT @Result
FETCH NEXT FROM @UserList INTO @UserId
END
CLOSE @UserList
DEALLOCATE @UserList
DROP TABLE #TempMainTable
--## Filter by programme id
IF @programmeId IS NOT NULL
BEGIN
DELETE FROM #TempTable WHERE programmeId <> @programmeId
END
IF @showNulls = 1 -- Select All Records
BEGIN
SELECT #TempTable.*, tblUsers.firstName + ' ' + tblUsers.lastName AS fullname, tblUsers.firstName, tblUsers.lastName, tblStores.name AS storeName, tblStores.county, tblStore_Types.name AS storeType, tblUsers.position, tblStores.surname + ' ' + tblStores.name as retailerName, tblProfiles.name as profile, tblRoa.lastname + ' ' + tblRoa.firstname as roa
FROM #TempTable INNER JOIN tblUsers ON #TempTable.userId = tblUsers.id INNER JOIN tblStores ON tblUsers.storeId = tblStores.id INNER JOIN tblStore_Types ON tblStores.storeTypeId = tblStore_Types.id INNER JOIN tblProfiles ON tblUsers.profileId = tblProfiles.id INNER JOIN tblROA ON tblStores.roaID = tblROA.id
END
ELSE IF @showNulls = 2 -- Select Users who have sat at least one training
BEGIN
SELECT #TempTable.*, tblUsers.firstName + ' ' + tblUsers.lastName AS fullname, tblUsers.firstName, tblUsers.lastName, tblStores.name AS storeName, tblStores.county, tblStore_Types.name AS storeType, tblUsers.position, tblStores.surname + ' ' + tblStores.name as retailerName, tblProfiles.name as profile, tblRoa.lastname + ' ' + tblRoa.firstname as roa
FROM #TempTable INNER JOIN tblUsers ON #TempTable.userId = tblUsers.id INNER JOIN tblStores ON tblUsers.storeId = tblStores.id INNER JOIN tblStore_Types ON tblStores.storeTypeId = tblStore_Types.id INNER JOIN tblProfiles ON tblUsers.profileId = tblProfiles.id INNER JOIN tblROA ON tblStores.roaID = tblROA.id
WHERE userId IN (SELECT userId FROM #TempTable WHERE (stepId IS NOT NULL) GROUP BY userId)
END
ELSE -- Select Only Training records that have been sat
BEGIN
SELECT #TempTable.*, tblUsers.firstName + ' ' + tblUsers.lastName AS fullname, tblUsers.firstName, tblUsers.lastName, tblStores.name AS storeName, tblStores.county, tblStore_Types.name AS storeType, tblUsers.position, tblStores.surname + ' ' + tblStores.name as retailerName, tblProfiles.name as profile, tblRoa.lastname + ' ' + tblRoa.firstname as roa
FROM #TempTable INNER JOIN tblUsers ON #TempTable.userId = tblUsers.id INNER JOIN tblStores ON tblUsers.storeId = tblStores.id INNER JOIN tblStore_Types ON tblStores.storeTypeId = tblStore_Types.id INNER JOIN tblProfiles ON tblUsers.profileId = tblProfiles.id INNER JOIN tblROA ON tblStores.roaID = tblROA.id
WHERE (stepId IS NOT NULL)
END
END
有关如何批准此存储过程的任何提示?
编辑显示最新的SP:
ALTER PROCEDURE [u1017987_dbase_user].[spGetStoreTrainingSummary_Test]
(
@staffId INT = default,
@storeTypeId INT = default,
@storeId INT = default,
@county VARCHAR(50) = default,
@programmeId INT = default,
@profileId INT = default,
@showNulls INT = default,
@position VARCHAR(50) = default,
@roaId INT = default
)
AS
BEGIN
SET NOCOUNT ON;
-- Place all users inner join stores into a temp table
CREATE TABLE #TempMainTable(
[id] INT,
[profileId] INT,
[position] VARCHAR(50),
[storeId] INT,
[county] VARCHAR(50),
[storeTypeId] INT,
[roaId] INT
)
CREATE TABLE #TempTable(
[userId] INT,
[menuName] varchar(250),
[stepId] int,
[programmeId] int,
[result] varchar(250)
)
DECLARE @UserId INT
DECLARE @MaxStep INT
DECLARE @Result VARCHAR(100)
DECLARE @UserList CURSOR
;WITH tempMainTable
AS
(
SELECT tblUsers.id, tblUsers.profileId, tblUsers.position, tblStores.id as storeId,
tblStores.county, tblStores.storeTypeId, tblStores.roaID
FROM tblUsers INNER JOIN tblStores ON tblUsers.storeId = tblStores.id
WHERE (tblUsers.statusId = 1) AND (tblStores.statusId = 1)
AND (@profileId = 0 OR profileId = @profileId)
AND (len(@position) = 0 OR position = @position)
AND (@storeId = 0 OR storeId = @storeId)
AND (len(@county) = 0 OR county = @county)
AND (@storeTypeId = 0 OR storeTypeId = @storeTypeId)
AND (@roaId = 0 OR roaId = @roaId)
),
tempTable AS
(
SELECT tempMainTable.userId,
'Staff Induction Programme ©',
(SELECT MAX(stepId) AS maxId FROM tblUserQuestionnaireHistory WHERE (userId = tempMainTable.userId) AND (programmeId = 13) AND (success = 1)),
13,
(SELECT CASE (SELECT MAX(stepId) AS maxId FROM tblUserQuestionnaireHistory WHERE (userId = tempMainTable.userId) AND (programmeId = 13) AND (success = 1)) WHEN 9 THEN 'Passed' ELSE 'Step ' + + CAST(@MaxStep AS VARCHAR(10)) + ' completed out of 9' END as Result)
FROM tempMainTable
WHERE (@programmeId IS NULL OR @programmeId=13)
)
--## Filter by programme id
IF @programmeId IS NOT NULL
BEGIN
DELETE FROM #TempTable WHERE programmeId <> @programmeId
END
IF @showNulls = 1 -- Select All Records
BEGIN
SELECT #TempTable.*, tblUsers.firstName + ' ' + tblUsers.lastName AS fullname, tblUsers.firstName, tblUsers.lastName, tblStores.name AS storeName, tblStores.county, tblStore_Types.name AS storeType, tblUsers.position, tblStores.surname + ' ' + tblStores.name as retailerName, tblProfiles.name as profile, tblRoa.lastname + ' ' + tblRoa.firstname as roa
FROM #TempTable INNER JOIN tblUsers ON #TempTable.userId = tblUsers.id INNER JOIN tblStores ON tblUsers.storeId = tblStores.id INNER JOIN tblStore_Types ON tblStores.storeTypeId = tblStore_Types.id INNER JOIN tblProfiles ON tblUsers.profileId = tblProfiles.id INNER JOIN tblROA ON tblStores.roaID = tblROA.id
END
ELSE IF @showNulls = 2 -- Select Users who have sat at least one training
BEGIN
SELECT #TempTable.*, tblUsers.firstName + ' ' + tblUsers.lastName AS fullname, tblUsers.firstName, tblUsers.lastName, tblStores.name AS storeName, tblStores.county, tblStore_Types.name AS storeType, tblUsers.position, tblStores.surname + ' ' + tblStores.name as retailerName, tblProfiles.name as profile, tblRoa.lastname + ' ' + tblRoa.firstname as roa
FROM #TempTable INNER JOIN tblUsers ON #TempTable.userId = tblUsers.id INNER JOIN tblStores ON tblUsers.storeId = tblStores.id INNER JOIN tblStore_Types ON tblStores.storeTypeId = tblStore_Types.id INNER JOIN tblProfiles ON tblUsers.profileId = tblProfiles.id INNER JOIN tblROA ON tblStores.roaID = tblROA.id
WHERE userId IN (SELECT userId FROM #TempTable WHERE (stepId IS NOT NULL) GROUP BY userId)
END
ELSE -- Select Only Training records that have been sat
BEGIN
SELECT #TempTable.*, tblUsers.firstName + ' ' + tblUsers.lastName AS fullname, tblUsers.firstName, tblUsers.lastName, tblStores.name AS storeName, tblStores.county, tblStore_Types.name AS storeType, tblUsers.position, tblStores.surname + ' ' + tblStores.name as retailerName, tblProfiles.name as profile, tblRoa.lastname + ' ' + tblRoa.firstname as roa
FROM #TempTable INNER JOIN tblUsers ON #TempTable.userId = tblUsers.id INNER JOIN tblStores ON tblUsers.storeId = tblStores.id INNER JOIN tblStore_Types ON tblStores.storeTypeId = tblStore_Types.id INNER JOIN tblProfiles ON tblUsers.profileId = tblProfiles.id INNER JOIN tblROA ON tblStores.roaID = tblROA.id
WHERE (stepId IS NOT NULL)
END
END
答案 0 :(得分:1)
好的,我会尝试尽可能地解决这个问题。
1)临时表非常慢,你可以使用CTE来获得更好的性能 2)SQL中的游标非常慢,大部分逻辑可能会进入您的业务层。
第一个Temp表和相关的DELETE可能是你的第一个CTE,你不需要所有的逻辑,只是一个体面的设置op
;WITH tempMainTable
AS
(
SELECT tblUsers.id, tblUsers.profileId, tblUsers.position, tblStores.id as storeId,
tblStores.county, tblStores.storeTypeId, tblStores.roaID
FROM tblUsers INNER JOIN tblStores ON tblUsers.storeId = tblStores.id
WHERE (tblUsers.statusId = 1) AND (tblStores.statusId = 1)
AND (@profileId = 0 OR profileId = @profileId)
AND (len(@position) = 0 OR position = @position)
AND (@storeId = 0 OR storeId = @storeId)
AND (len(@county) = 0 OR county = @country)
AND (@storeTypeId = 0 OR storeTypeId = @storeTypeId)
AND (@roaId = 0 OR roaId = @roaId)
),
tempTable AS
(
SELECT tempMainTable.userId,
'Staff Induction Programme ©',
(SELECT MAX(stepId) AS maxId FROM tblUserQuestionnaireHistory WHERE (userId = tempMainTable.userId) AND (programmeId = 13) AND (success = 1)),
13,
(SELECT CASE (SELECT MAX(stepId) AS maxId FROM tblUserQuestionnaireHistory WHERE (userId = tempMainTable.userId) AND (programmeId = 13) AND (success = 1)) WHEN 9 THEN 'Passed' WHEN ELSE 'Step ' + + CAST(@MaxStep AS VARCHAR(10)) + ' completed out of 9' END as Result)
FROM tempMainTable
WHERE (@programmeId IS NULL OR @programmeId=13)
)
// do the rest here
立即消除了对第一个临时表,第二个和光标的需要。
但我认为这里的主要优点是不填充大量数据,然后逐步删除它。首先根据您的参数过滤数据来开始设置rt tdata,就像我在上面的第一个CTE中所做的那样,
答案 1 :(得分:0)
您似乎知道光标是您的问题。您可能需要将其转换为设置操作。 这是我将光标操作转换为set ops的一般方法
从源代码我的建议是将curso循环分解为
从最终的临时表中,进行正确的选择。
答案 2 :(得分:0)
一个可能的罪魁祸首是光标。尝试将其重写为基于集合的操作。例如:
INSERT #TempTable
SELECT tmt.id
, 'Staff Induction Programme ©'
, uqh.MaxStep
, 13
, CASE
WHEN uqh.MaxStep = 9 THEN 'Passed'
WHEN uqh.MaxStep <> 9 THEN 'Step ' + cast(uqh.MaxStep AS VARCHAR(10)) +
' completed out of 9'
ELSE 'No steps completed yet'
END
FROM #TempMainTable tmt
LEFT JOIN
(
SELECT uqh.userId
, MAX(uqh.stepId) as MaxStep
FROM tblUserQuestionnaireHistory uqh
WHERE uqh.programmeId = 13
AND uqh.success = 1
GROUP BY
uqh.userId
) uqh
on uqh.userId = tmt.id