【发布时间】:2019-10-30 04:16:31
【问题描述】:
我正在编写一个庞大的 SQL 代码,不幸的是,它有一个 CURSOR,它处理其中的另外两个嵌套 CURSORS(存储过程中总共三个游标),它处理数百万个要删除、更新和插入的数据。由于逐行执行,这需要大量时间,我希望将其修改为基于 SET 的方法
许多文章显示不推荐使用 CURSOR,替代方法是使用 WHILE 循环,所以我尝试用三个 WHILE 循环替换三个 CUROSR,虽然我得到了相同的结果,但没有任何改进在性能方面,它与 CUROSR 所用的时间相同。
下面是我正在编写的代码的基本结构(我会尽量简单),并且我会将 cmets 放在他们应该做的事情上。
declare @projects table (
ProjectID INT,
fieldA int,
fieldB int,
fieldC int,
fieldD int)
INSERT INTO @projects
SELECT ProjectID,fieldA,fieldB,fieldC, fieldD
FROM ProjectTable
DECLARE projects1 CURSOR LOCAL FOR /*First cursor - fetch the cursor from ProjectaTable*/
Select ProjectID FROM @projects
OPEN projects1
FETCH NEXT FROM projects1 INTO @ProjectID
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
BEGIN TRAN
DELETE FROM T_PROJECTGROUPSDATA td
WHERE td.ID = @ProjectID
DECLARE datasets CURSOR FOR /*Second cursor - this will get the 'collectionDate'field from datasetsTable for every project fetched in above cursor*/
Select DataID, GroupID, CollectionDate
FROM datasetsTable
WHERE datasetsTable.projectID = @ProjectID /*lets say this will fetch ten records for a single projectID*/
OPEN datasets
FETCH NEXT FROM datasets INTO @DataID, @GroupID, @CollectionDate
WHILE @@FETCH_STATUS = 0
BEGIN
DECLARE period CURSOR FOR /*Third Cursor - this will process the records from another table called period with above fetched @collectionDate*/
SELECT ID, dbo.fn_GetEndOfPeriod(ID)
FROM T_PERIODS
WHERE DATEDIFF(dd,@CollectionDate,dbo.fn_GetEndOfPeriod(ID)) >= 0 /*lets say this will fetch 20 records for above fetched single @CollectionDate*/
ORDER BY [YEAR],[Quarter]
OPEN period
FETCH NEXT FROM period INTO @PeriodID, @EndDate
WHILE @@FETCH_STATUS = 0
BEGIN
IF EXISTS (some conditions No - 1 )
BEGIN
BREAK
END
IF EXISTS (some conditions No - 2 )
BEGIN
FETCH NEXT FROM period INTO @PeriodID, @EndDate
CONTINUE
END
/*get the appropirate ID from T_uploads table for the current projectID and periodID fetched*/
SET @UploadID = (SELECT ID FROM T_UPLOADS u WHERE u.project_savix_ID = @ProjectID AND u.PERIOD_ID = @PeriodID AND u.STATUS = 3)
/*Update some fields in T_uploads table for the current projectID and periodID fetched*/
UPDATE T_uploads
SET fieldA = mp.fieldA, fieldB = mp.fieldB
FROM @projects mp
WHERE T_UPLOADS.ID = @UploadID AND mp.ProjectID = @ProjectID
/*Insert some records in T_PROJECTGROUPSDATA table for the current projectID and periodID fetched*/
INSERT INTO T_PROJECTGROUPSDATA tpd ( fieldA,fieldB,fieldC,fieldD,uploadID)
SELECT fieldA,fieldB,fieldC,fieldD,@UploadID
FROM @projects
WHERE tpd.DataID = @DataID
FETCH NEXT FROM period INTO @PeriodID, @EndDate
END
CLOSE period
DEALLOCATE period
FETCH NEXT FROM datasets INTO @DataID, @GroupID, @CollectionDate, @Status, @Createdate
END
CLOSE datasets
DEALLOCATE datasets
COMMIT
END TRY
BEGIN CATCH
Error handling
IF @@TRANCOUNT > 0
ROLLBACK
END CATCH
FETCH NEXT FROM projects1 INTO @ProjectID, @FAID
END
CLOSE projects1
DEALLOCATE projects1
SELECT 1 as success
我请求您建议任何方法来重写此代码以遵循基于 SET 的方法。
【问题讨论】:
-
样本数据最好使用DDL + DML。请edit您的问题包括它,您当前的尝试和您想要的结果。更多详情,read this.
-
您混淆了太多,删除了太多,并引入了语法错误(例如,将语句插入 T_PROJECTSGROUPDATA),这些错误会妨碍您对逻辑的理解。我可以看到盲目应用的模式(无特殊原因将行加载到表变量中)。看起来您的前 2 个光标可以合并为一个。
标签: sql-server tsql while-loop sql-server-2012 cursor