Как преобразовать запрос на основе CURSOR в набор на основе SET - PullRequest
4 голосов
/ 22 июня 2019

Я не эксперт по SQL, и я работаю над огромным кодом SQL, и, к сожалению, у него есть CURSOR, который обрабатывает еще два вложенных CURSORS внутри (всего три курсора внутри хранимой процедуры), который обрабатывает миллионы данныхбыть DELETE, UPDATE и INSERT.

Это занимает много времени (более 13 часов и в итоге выдает ошибку «наборы данных имен курсоров уже существуют») из-за построчного выполнения, и я пытаюсьчтобы изменить это в подходе на основе SET, и я застрял, как сделать INSERT, DELETE и UPDATE при преобразовании в подход на основе SET. Иными словами, я не мог сделать все INSERT, DELETE и UPDATE, заменив их в JOINS.

Я знаю, что это большой код, и, возможно, это неправильная попытка опубликовать здесь, но я уже просто выразил это в другом посте, и люди не поняли мой вопрос, поэтому я 'я ставлю здесь фактический код.

Это код, над которым я работаю:

ALTER PROCEDURE [dbo].[proc_Upload]  WITH RECOMPILE 
as
set NoCount on

DECLARE
    @StartTime      datetime,
    @EndTime        datetime,
    @DataID         uniqueidentifier,
    @CollectionDate datetime,
    @Status         int,
    @PeriodID       int,
    @EndDate        datetime,
    @GroupID        uniqueidentifier,
    @ProjectID      INT,
    @FAID           uniqueidentifier,
    @UploadID       int ,
    @Createdate datetime


declare @projects table(
    [ProjectID] [INT],
    [Title] [varchar](255) ,
    [currency] [int] ,
    [Cost_Until_Today] [float] ,
    [StartDate] [datetime] ,
    [EndDate] [datetime] ,
    [MisID] [uniqueidentifier] ,
    [SystemStatus] [int] ,
    [FacilitatingAgency] [uniqueidentifier] ,
    [SyncMode] [int] )

 INSERT @projects
 SELECT ID, PROJECTNAME, PROJECTCURRENCY_ID, Cost_Until_Today, PROJECTESTABLISHEDDATE, EndDate, MisID, 4, FacilitatingAgency, SyncMode 
 FROM [dbo].[T_PROJECTS] /*thsi is the source table where every projectIDs need to be processed*/



 IF exists ( select  *
            from    dbo.sysobjects
            where   id = object_id(N'[dbo].[datasets]') and objectproperty(id, N'IsTable') = 1 ) 
    DROP Table [dbo].datasets


    SELECT mgd.Gd_ID, mg.Grp_ID, mgd.Gd_CollectionDate, mgd.Gd_IsDeleted, mgd.Gd_CreateDate, mg.Grp_Project, mg.Grp_IsDeleted , mg.Grp_Legacy_ID,  p.LegacyProjectId
    INTO datasets
    FROM Savix_Service_Group..Group_Data mgd 
            INNER JOIN Savix_Service_Group..Group_Cycle mgc ON mgc.Gc_ID = mgd.Gd_CycleID
            INNER JOIN Savix_Service_Group..Groups mg ON mg.Grp_ID = mgc.Gc_GroupID 
            inner join SG_Dynamic_Forms..v_projects p ON p.ProjectID = mg.Grp_Project


DECLARE projects1 CURSOR LOCAL FOR Select distinct ProjectID, isnull([FacilitatingAgency], '00000000-0000-0000-0000-000000000000') from @projects P WHERE P.SystemStatus = 4 AND P.SyncMode = 1 
/*First cursor - fetch the cursor from ProjectaTable*/


OPEN projects1
FETCH NEXT FROM projects1 INTO @ProjectID, @FAID
WHILE @@FETCH_STATUS = 0
BEGIN
    BEGIN TRY
        BEGIN TRAN

        DELETE FROM T_PROJECTGROUPSDATA
            WHERE T_PROJECTGROUPSDATA.UPLOAD_ID IN (SELECT ID FROM T_UPLOADS WHERE project_savix_ID = @ProjectID AND UPLOADFILENAME = 'Automatic upload from web MIS')

        DECLARE datasets CURSOR LOCAL  FAST_FORWARD FOR SELECT Gd_ID, Grp_ID, Gd_CollectionDate, Gd_IsDeleted, Gd_CreateDate 
            FROM datasets
            WHERE LegacyProjectId = @ProjectID AND Grp_IsDeleted = 0 AND Gd_IsDeleted != 1 
            /*Second cursor - this will get the 'collectionDate'field from datasetsTable for every project fetched in above cursor and also get @dataID which is used to insert value in to other table-T_PROJECTGROUPSDATA*/
        OPEN datasets

        FETCH NEXT FROM datasets INTO @DataID, @GroupID, @CollectionDate, @Status, @Createdate
        WHILE @@FETCH_STATUS = 0
        BEGIN
            DECLARE period CURSOR LOCAL  FAST_FORWARD FOR SELECT ID, dbo.fn_GetEndOfPeriod(ID) FROM T_PERIODS
            /* dbo.fn_GetEndOfPeriod(ID) - this function will give the end of the date of that specifc quarter for any given date*/
                WHERE DATEDIFF(dd,@CollectionDate,dbo.fn_GetEndOfPeriod(ID)) >= 0
                ORDER BY [YEAR],[Quarter]
                /*Third Cursor - this will process the records from another table called period with above fetched @collectionDate*/

            OPEN period
            FETCH NEXT FROM period INTO @PeriodID, @EndDate
            WHILE @@FETCH_STATUS = 0
            BEGIN

                IF EXISTS (SELECT * FROM Savix_Service_Group..Group_Data mgd 
                    INNER JOIN Savix_Service_Group..Group_Cycle mgc ON mgc.Gc_ID = mgd.Gd_CycleID
                    WHERE mgc.Gc_GroupID = @GroupID
                        AND DATEDIFF(dd,mgd.Gd_CollectionDate,@EndDate) >= 0 
                        AND (mgd.Gd_CollectionDate > @CollectionDate ) 
                        AND mgd.Gd_IsDeleted != 1) 

                BEGIN
                    BREAK
                END

                IF EXISTS (SELECT ID FROM T_UPLOADS u 
                    WHERE  u.project_savix_ID = @ProjectID AND u.PERIOD_ID = @PeriodID AND u.STATUS = 3 AND UPLOADFILENAME != 'Automatic upload from web MIS') 
                BEGIN
                    FETCH NEXT FROM period INTO @PeriodID, @EndDate
                    CONTINUE
                END

                SET @UploadID = (SELECT ID FROM T_UPLOADS u WHERE  u.project_savix_ID = @ProjectID AND u.PERIOD_ID = @PeriodID AND u.STATUS = 3)

                /*If T_uploads doesn't have appropirate period ID from cursor fetch then create a new entry in T_uploads with current projectID*/

                IF @UploadID IS NULL
                        BEGIN

                            declare @Project_ID_Legacy int = ISNULL((select distinct PROJECT_ID from T_UPLOADS where project_savix_ID = @ProjectID),@ProjectID)

                                INSERT INTO T_UPLOADSFIRSTSTEP
                                       (PROJECT_ID
                                       ,UPLOADDATE
                                       ,UPLOADFILENAME
                                       ,UPLOADUSER_ID
                                       ,CURRENTSTEP
                                       ,STATUS
                                       ,Project_ID_MIS)
                                 SELECT @Project_ID_Legacy , --mm 06/12
                                        GETDATE(),
                                        'Automatic upload from web MIS',
                                        2,
                                        2,
                                        0,
                                        @ProjectID

                                INSERT INTO T_UPLOADS
                                       (ID, periodID, projectID,UPLOADDATE,UPLOADFILENAME,UPLOADUSER_ID )
                                 SELECT uf.ID,
                                        @PeriodID,
                                        uf.PROJECT_ID,
                                        uf.UPLOADDATE,
                                        uf.UPLOADFILENAME,
                                        uf.UPLOADUSER_ID

                                 FROM T_UPLOADSFIRSTSTEP uf
                                 INNER JOIN @projects mp ON  uf.Project_ID_MIS = mp.ProjectID
                                 WHERE  uf.Project_ID_MIS = @ProjectID AND uf.STATUS = 0 
                                    AND NOT EXISTS (SELECT * FROM T_UPLOADS u WHERE u.PROJECT_ID  = uf.PROJECT_ID AND u.PERIOD_ID = @PeriodID AND u.STATUS = 3)

                                DELETE FROM T_UPLOADSFIRSTSTEP WHERE STATUS = 0 AND PROJECT_ID = @Project_ID_Legacy 

                                SET @UploadID = (SELECT ID FROM T_UPLOADS u WHERE  u.project_savix_ID = @ProjectID AND u.PERIOD_ID = @PeriodID AND u.STATUS = 3)
                END ELSE
                            UPDATE T_UPLOADS SET 
                                TOTALEXPENDITURES = CASE WHEN DATEDIFF(d,mp.StartDate,mp.EndDate) != 0 
                                            THEN mp.Cost_Until_Today*DATEDIFF(d,mp.StartDate,dbo.fn_GetEndOfPeriod(@PeriodID))/DATEDIFF(d,mp.StartDate,mp.EndDate)/dbo.fn_RateAtDate(mp.Currency,dbo.fn_GetEndOfPeriod(@PeriodID))
                                        ELSE 0 END,
                                TotalExpendituresNative = CASE WHEN DATEDIFF(d,mp.StartDate,mp.EndDate) != 0 
                                            THEN mp.Cost_Until_Today*DATEDIFF(d,mp.StartDate,dbo.fn_GetEndOfPeriod(@PeriodID))/DATEDIFF(d,mp.StartDate,mp.EndDate)
                                        ELSE 0 END
                            FROM @projects mp
                            WHERE T_UPLOADS.ID = @UploadID AND mp.ProjectID = @ProjectID

                        INSERT INTO T_PROJECTGROUPSDATA 
                                (uploadID, fieldA,fieldB,......fieldN )

                         SELECT @UploadID,p.fieldA,mg.fieldB,......mgc.fieldN
                         FROM @projects mp
                         inner join  SG_Dynamic_Forms..v_projects p ON p.LegacyProjectId = mp.projectID 
                         inner join Savix_Service_Group..Groups mg ON mg.Grp_Project = p.ProjectID 
                         INNER JOIN Savix_Service_Group..Group_Cycle mgc ON mgc.Gc_GroupID = mg.Grp_ID
                         INNER JOIN Savix_Service_Group..Group_Data mgd ON mgd.Gd_CycleID = mgc.Gc_ID
                         LEFT JOIN Savix_Service_Trainers..Trainers me ON me.Tr_ID = mgc.Gc_MonitoredBy
                         LEFT JOIN Savix_Service_Dictionaries..Dictionary mgt ON mgt.Dny_ID = me.Tr_Type 
                         --left join v1_Report_UDF_Data_UploadToSavix udf on udf.DataID = mgd.Gd_ID
                         WHERE mgd.Gd_ID = @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



        SELECT ERROR_NUMBER(), ERROR_MESSAGE(),@PeriodID, @ProjectID, @UploadID,@DataID
        IF CURSOR_STATUS('global' , 'period') >= 0
        BEGIN
            CLOSE period
            DEALLOCATE uploadID
        END

        IF CURSOR_STATUS('global' , 'datasets') >= 0
        BEGIN
            CLOSE datasets
            DEALLOCATE datasets
        END

        IF @@TRANCOUNT > 0
            ROLLBACK

        INSERT INTO error_catch_UploadtoSavix

        SELECT cast(ERROR_NUMBER() as nvarchar), ERROR_MESSAGE(),@PeriodID, @ProjectID, @UploadID,@DataID, getdate()
    END CATCH


    FETCH NEXT FROM projects1 INTO @ProjectID, @FAID
END


CLOSE projects1
DEALLOCATE projects1

SELECT 1 as success

Вот то, что я пытался сделать, и застрялk посередине и неполно:

select s.ID,u.* 
from T_PROJECTS_TEMP pt 
INNER JOIN
    (SELECT mgd.Gd_ID, mg.Grp_ID, mgd.Gd_CollectionDate, mgd.Gd_IsDeleted, mgd.Gd_CreateDate, mg.Grp_Project, mg.Grp_IsDeleted , mg.Grp_Legacy_ID,  p.LegacyProjectId
    FROM Savix_Service_Group..Group_Data mgd 
    INNER JOIN Savix_Service_Group..Group_Cycle mgc ON mgc.Gc_ID = mgd.Gd_CycleID
    INNER JOIN Savix_Service_Group..Groups mg ON mg.Grp_ID = mgc.Gc_GroupID 
    inner join SG_Dynamic_Forms..v_projects p ON p.ProjectID = mg.Grp_Project
    WHERE LegacyProjectId = 5047 AND Grp_IsDeleted = 0 AND Gd_IsDeleted != 1 )  dataset  on pt.ID = dataset.LegacyProjectId
INNER JOIN T_PERIODS s ON DATEDIFF(DAY,dataset.Gd_CollectionDate,dbo.fn_GetEndOfPeriod(s.ID)) >= 0 
LEFT JOIN T_UPLOADS u ON u.project_savix_ID = pt.ID AND u.PERIOD_ID = s.ID AND u.STATUS = 3 --AND u.UPLOADFILENAME != 'Automatic upload from web MIS'

WHERE NOT EXISTS (SELECT * FROM Savix_Service_Group..Group_Data mgd 
                    INNER JOIN Savix_Service_Group..Group_Cycle mgc ON mgc.Gc_ID = mgd.Gd_CycleID
                    WHERE mgc.Gc_GroupID = Grp_ID
                        AND DATEDIFF(dd,mgd.Gd_CollectionDate,(select dbo.fn_GetEndOfPeriod(s.ID))) >= 0 
                        AND (mgd.Gd_CollectionDate > Gd_CollectionDate) AND mgd.Gd_IsDeleted != 1 ) 
        AND NOT EXISTS (SELECT ID FROM T_UPLOADS u 
                    WHERE u.project_savix_ID = pt.ID AND u.PERIOD_ID = s.ID AND u.STATUS = 3 AND UPLOADFILENAME != 'Automatic upload from web MIS') 

ORDER BY s.ID

Пример данных

Таблицы и данные для T_Projects и наборов данных - http://www.sqlfiddle.com/#!18/00205/2

Таблицы и данные для T_Periods - http://www.sqlfiddle.com/#!18/3b6b1/2

Таблицы и данные для T_uploads (до запуска курсора) - http://www.sqlfiddle.com/#!18/7d12f3/1

Данные других таблиц 1 - http://www.sqlfiddle.com/#!18/6e499

Данные других таблиц 2 - http://www.sqlfiddle.com/#!18/30e3b/3

function fn_getEndOFperiod - http://www.sqlfiddle.com/#!18/54d66

PLease note - в любой из приведенных выше таблиц будет более одной строки, однако приведенный мной пример будет содержать только одну запись.

Исключенные данные

Таблица T_uploads - http://sqlfiddle.com/#!18/027cc/1

T_ProjectsGroupData также будет иметь в общей сложности 15 записей, т.е. для каждого T_uploads.ID (который относится к полю T_ProjectsGroupData .UploadID) будет записьв T_ProjectsGroupData

I, за исключением того, что этот код на основе CURSOR будет преобразован в простой подход на основе SET, любая помощь и предложение по улучшению этого кода будут очень значительными.

1 Ответ

2 голосов
/ 28 июня 2019
INSERT INTO T_PROJECTGROUPSDATA (uploadID, fieldA,fieldB,......fieldN )
SELECT t.ID,p.fieldA,mg.fieldB,......mgc.fieldN
FROM T_Projects mp
INNER JOIN T_UPLOADS t mp.ID = t.project_savix_ID
INNER JOIN  SG_Dynamic_Forms..v_projects p ON p.LegacyProjectId = mp.ID 
INNER JOIN Savix_Service_Group..Groups mg ON mg.Grp_Project = p.ProjectID 
INNER JOIN Savix_Service_Group..Group_Cycle mgc ON mgc.Gc_GroupID = mg.Grp_ID
INNER JOIN Savix_Service_Group..Group_Data mgd ON mgd.Gd_CycleID = mgc.Gc_ID
LEFT JOIN Savix_Service_Trainers..Trainers me ON me.Tr_ID = mgc.Gc_MonitoredBy
LEFT JOIN Savix_Service_Dictionaries..Dictionary mgt ON mgt.Dny_ID = me.Tr_Type 
WHERE mp.SyncMode = 1
AND t.[STATUS] = 3 AND t.UPLOADFILENAME = 'Automatic upload from web MIS'
AND mg.Grp_IsDeleted = 0 AND mgd.Gd_IsDeleted != 1


UPDATE t SET 
TOTALEXPENDITURES = CASE WHEN DATEDIFF(d,mp.PROJECTESTABLISHEDDATE,mp.EndDate) != 0 
    THEN p.Cost_Until_Today*DATEDIFF(d,mp.PROJECTESTABLISHEDDATE,dbo.fn_GetEndOfPeriod(t.Period_ID))/DATEDIFF(d,mp.PROJECTESTABLISHEDDATE,mp.EndDate)/dbo.fn_RateAtDate(p.PROJECTCURRENCY_ID,dbo.fn_GetEndOfPeriod(t.Period_ID))
    ELSE 0 
    END,
TotalExpendituresNative = CASE WHEN DATEDIFF(d,mp.PROJECTESTABLISHEDDATE,mp.EndDate) != 0 
    THEN p.Cost_Until_Today*DATEDIFF(d,mp.PROJECTESTABLISHEDDATE,dbo.fn_GetEndOfPeriod(t.Period_ID))/DATEDIFF(d,mp.PROJECTESTABLISHEDDATE,mp.EndDate)
    ELSE 0 
    END
FROM T_UPLOADS t
JOIN T_Projects mp ON mp.ID = t.project_savix_ID AND mp.SyncMode = 1
WHERE t.[STATUS] = 3 AND t.UPLOADFILENAME = 'Automatic upload from web MIS'

Чтобы объяснить, как я пришел к этому, вот модифицированная версия кода ниже. Я не могу гарантировать, что я получил все, я должен был сделать некоторые предположения. Не хватало примеров данных, и все таблицы savix отсутствуют. Это лучший ответ, который я могу придумать, учитывая информацию и ограничив мое время менее 4 часами. Я мог бы сделать гораздо больше, но ты должен был бы дать мне больше, чем интернет-слава.

--ALTER PROCEDURE [dbo].[proc_Upload]  WITH RECOMPILE 
--as
set NoCount on

DECLARE
    @StartTime      datetime,
    @EndTime        datetime,
    @DataID         uniqueidentifier,
    @CollectionDate datetime,
    @Status         int,
    @PeriodID       int,
    @EndDate        datetime,
    @GroupID        uniqueidentifier,
    @ProjectID      INT,
    @FAID           uniqueidentifier,
    @UploadID       int ,
    @Createdate datetime, 
    @MINIDprojects INT,
    @MAXIDprojects INT,
    @MINIDdatasets INT,
    @MAXIDdatasets INT,
    @MINIDperiods INT,
    @MAXIDperiods INT

Сложно работать и тестировать курсоры, поэтому я заменил их (проекты, проекты1, наборы данных и период) на временные таблицы, содержащие идентификационные данные, и прошел через них.

IF OBJECT_ID('tempdb..#projects') IS NOT NULL DROP TABLE #projects
IF OBJECT_ID('tempdb..#projects1') IS NOT NULL DROP TABLE #projects1
IF OBJECT_ID('tempdb..#datasets') IS NOT NULL DROP TABLE #datasets
IF OBJECT_ID('tempdb..#period') IS NOT NULL DROP TABLE #period

CREATE TABLE #projects
(
    [ProjectID] [INT],
    [Title] [varchar](255) ,
    [currency] [int] ,
    [Cost_Until_Today] [float] ,
    [StartDate] [datetime] ,
    [EndDate] [datetime] ,
    [MisID] [uniqueidentifier] ,
    [SystemStatus] [int] ,
    [FacilitatingAgency] [uniqueidentifier] ,
    [SyncMode] [int] 
)

CREATE TABLE #projects1
(
    ID INT IDENTITY(1,1),
    [ProjectID] [INT],
    [FacilitatingAgency] [uniqueidentifier]
)

CREATE TABLE #datasets
(
    ID INT IDENTITY(1,1),
    Gd_ID [uniqueidentifier], 
    Grp_ID [uniqueidentifier], 
    Gd_CollectionDate DATETIME, 
    Gd_IsDeleted BIT, 
    Gd_CreateDate DATETIME
)

CREATE TABLE #period
(
    ID INT IDENTITY(1,1),
    IDPeriod INT,
    EndDate DATETIME
)

 INSERT #projects ( [ProjectID], [Title], {currency], [Cost_Until_Today], [StartDate], [EndDate], [MisID], [SystemStatus], [FacilitatingAgency], [SyncMode] )
 SELECT ID, PROJECTNAME, PROJECTCURRENCY_ID, Cost_Until_Today, PROJECTESTABLISHEDDATE, EndDate, MisID, 4, FacilitatingAgency, SyncMode 
 FROM [dbo].[T_PROJECTS] /*thsi is the source table where every projectIDs need to be processed*/

Projects содержит все идентификаторы (projectID) из T_Projects, из этой таблицы используются только поля ID, projectcurrency, projectestablisheddate (startdate), enddate и syncmode (должно быть = 1, мы увидим это позже). Мы можем игнорировать системный статус, поскольку он является константой.

Табличные наборы данных удаляются и загружаются здесь. Мы заботимся только о gd_id из таблицы Savix_Service_Group..Group_Data, но нам все равно нужно присоединиться к другим таблицам в случае, если они отфильтровывают значения, которые нам не нужны. Эта логика снова используется ниже при вставке записей.

IF exists ( select  * from    dbo.sysobjects where   id = object_id(N'[dbo].datasets]') and objectproperty(id, N'IsTable') = 1 ) DROP Table [dbo].datasets

SELECT mgd.Gd_ID, mg.Grp_ID, mgd.Gd_CollectionDate, mgd.Gd_IsDeleted, mgd.Gd_CreateDate, mg.Grp_Project, mg.Grp_IsDeleted , mg.Grp_Legacy_ID,  p.LegacyProjectId
INTO datasets
FROM Savix_Service_Group..Group_Data mgd 
        INNER JOIN Savix_Service_Group..Group_Cycle mgc ON mgc.Gc_ID = mgd.Gd_CycleID
        INNER JOIN Savix_Service_Group..Groups mg ON mg.Grp_ID = mgc.Gc_GroupID 
        inner join SG_Dynamic_Forms..v_projects p ON p.ProjectID = mg.Grp_Project

--DECLARE projects1 CURSOR LOCAL FOR Select distinct ProjectID, isnull([FacilitatingAgency], '00000000-0000-0000-0000-000000000000') from @projects P WHERE P.SystemStatus = 4 AND P.SyncMode = 1 
--/*First cursor - fetch the cursor from ProjectaTable*/

--OPEN projects1
--FETCH NEXT FROM projects1 INTO @ProjectID, @FAID
--WHILE @@FETCH_STATUS = 0

Здесь мы вставляем в проекты1 отдельное значение идентификаторов проекта. Без дополнительных данных, я предполагаю, что идентификатор отличается в таблице T_projects, и поэтому этот шаг несколько ненужен, кроме фильтрации по SyncMode = 1. Помните, что SystemStatus является константой, и мы заполнили ее значением '4', это критерии могут быть удалены без эффекта.

INSERT INTO #projects1 ( ProjectID, FacilitatingAgency )
SELECT DISTINCT ProjectID, isnull([FacilitatingAgency], '00000000-0000-0000-0000-000000000000')  
FROM #projects p
WHERE SystemStatus = 4 AND SyncMode = 1

SELECT @MINIDprojects = MIN(ID), @MAXIDprojects = MAX(ID) FROM #projects1
WHILE @MINIDprojects < @MAXIDprojects + 1
BEGIN
--    BEGIN TRY
--        BEGIN TRAN

SELECT @ProjectID = ProjectID, @FAID = FacilitatingAgency FROM #projects1 WHERE ID = @MINIDprojects

        --DELETE FROM T_PROJECTGROUPSDATA WHERE T_PROJECTGROUPSDATA.UPLOAD_ID IN (SELECT ID FROM T_UPLOADS WHERE project_savix_ID = @ProjectID AND UPLOADFILENAME = 'Automatic upload from web MIS')

        --DECLARE datasets CURSOR LOCAL  FAST_FORWARD FOR SELECT Gd_ID, Grp_ID, Gd_CollectionDate, Gd_IsDeleted, Gd_CreateDate 
        --    FROM datasets
        --    WHERE LegacyProjectId = @ProjectID AND Grp_IsDeleted = 0 AND Gd_IsDeleted != 1 
        --    /*Second cursor - this will get the 'collectionDate'field from datasetsTable for every project fetched in above cursor and also get @dataID which is used to insert value in to other table-T_PROJECTGROUPSDATA*/
        --OPEN datasets

        --FETCH NEXT FROM datasets INTO @DataID, @GroupID, @CollectionDate, @Status, @Createdate
        --WHILE @@FETCH_STATUS = 0

Здесь мы вставляем в # наборы данных фильтрацию по T_Project.ID AND Savix_Service_Group..Groups.Grp_IsDeleted = 0 AND Savix_Service_Group..Group_Data.Gd_IsDeleted! = 1

        INSERT INTO #datasets ( Gd_ID, Grp_ID, Gd_CollectionDate, Gd_IsDeleted, Gd_CreateDate )
        SELECT Gd_ID, Grp_ID, Gd_CollectionDate, Gd_IsDeleted, Gd_CreateDate FROM datasets WHERE LegacyProjectId = @ProjectID AND Grp_IsDeleted = 0 AND Gd_IsDeleted != 1 

        SELECT @MINIDdatasets = MIN(ID), @MAXIDdatasets = MAX(ID) FROM #datasets

        WHILE @MINIDdatasets < @MAXIDdatasets + 1
        BEGIN

            SELECT @DataID = Gd_ID, @GroupID = Grp_ID, @CollectionDate = Gd_CollectionDate, @Status = Gd_IsDeleted, @Createdate = Gd_CreateDate FROM #datasets WHERE ID = @MINIDdatasets

            --DECLARE period CURSOR LOCAL  FAST_FORWARD FOR SELECT ID, dbo.fn_GetEndOfPeriod(ID) FROM T_PERIODS
            --/* dbo.fn_GetEndOfPeriod(ID) - this function will give the end of the date of that specifc quarter for any given date*/
            --    WHERE DATEDIFF(dd,@CollectionDate,dbo.fn_GetEndOfPeriod(ID)) >= 0
            --    ORDER BY [YEAR],[Quarter]
            --    /*Third Cursor - this will process the records from another table called period with above fetched @collectionDate*/

            --OPEN period
            --FETCH NEXT FROM period INTO @PeriodID, @EndDate
            --WHILE @@FETCH_STATUS = 0

Это, вероятно, худшее использование курсора здесь. Мы загружаем все периоды и перебираем их. В конце мы заботимся только о периодах, которые находятся в таблице T_UPLOADS.

            INSERT INTO #period ( IDPeriod, EndDate ) SELECT ID, dbo.fn_GetEndOfPeriod(ID) FROM T_PERIODS WHERE DATEDIFF(dd,@CollectionDate,dbo.fn_GetEndOfPeriod(ID)) >= 0
            SELECT @MINIDperiods = MIN(ID), @MAXIDperiods = MAX(ID) FROM #period

            WHILE @MINIDperiods < @MAXIDperiods + 1
            BEGIN

                --IF EXISTS (SELECT * FROM Savix_Service_Group..Group_Data mgd 
                --    INNER JOIN Savix_Service_Group..Group_Cycle mgc ON mgc.Gc_ID = mgd.Gd_CycleID
                --    WHERE mgc.Gc_GroupID = @GroupID
                --        AND DATEDIFF(dd,mgd.Gd_CollectionDate,@EndDate) >= 0 
                --        AND (mgd.Gd_CollectionDate > @CollectionDate ) 
                --        AND mgd.Gd_IsDeleted != 1) 

                --BEGIN
                --    BREAK
                --END

Здесь мы пропускаем любые периоды, когда UPLOADFILENAME! = 'Автоматическая загрузка из веб-MIS'

                --IF EXISTS (SELECT ID FROM T_UPLOADS u WHERE  u.project_savix_ID = @ProjectID AND u.PERIOD_ID = @PeriodID AND u.STATUS = 3 AND UPLOADFILENAME != 'Automatic upload from web MIS') 
                --BEGIN
                --    FETCH NEXT FROM period INTO @PeriodID, @EndDate
                --    CONTINUE
                --END

Имея это в виду, мы заботимся только о том, где статус = 3 и UPLOADFILENAME = 'Автоматическая загрузка из веб-MIS'

                SET @UploadID = (SELECT ID FROM T_UPLOADS u WHERE  u.project_savix_ID = @ProjectID AND u.PERIOD_ID = @PeriodID AND u.[STATUS] = 3 AND UPLOADFILENAME = 'Automatic upload from web MIS')

                /*If T_uploads doesn't have appropirate period ID from cursor fetch then create a new entry in T_uploads with current projectID*/

                IF @UploadID IS NOT NULL
                        BEGIN

                            --declare @Project_ID_Legacy int = ISNULL((select distinct PROJECT_ID from T_UPLOADS where project_savix_ID = @ProjectID),@ProjectID)

Почему они используют вторую таблицу (T_UPLOADFIRSTSTEP) для генерации записи, в которой они используют только идентификатор, а затем удаляют запись с использованием этого идентификатора при создании записи T_Uploads, мне непонятно и кажется мне ужасным кодированием.

    --                            INSERT INTO T_UPLOADSFIRSTSTEP
    --                                   (PROJECT_ID
    --                                   --,UPLOADDATE
    --                                   --,UPLOADFILENAME
    --                                   --,UPLOADUSER_ID
    --                                   --,CURRENTSTEP
    --                                   ,[STATUS]
    --                                   ,Project_ID_MIS)
    --                             SELECT ISNULL((select distinct PROJECT_ID from T_UPLOADS where project_savix_ID = @ProjectID),@ProjectID),
    --                                    --GETDATE(),
    --                                    --'Automatic upload from web MIS',
    --                                    --2,
    --                                    --2,
    --                                    0,
    --                                    @ProjectID

Вставка в T_UPLOADS всегда будет неудачной, поскольку существуют поля, которые не могут иметь значение NULL, которых нет в списке вставок и которым не назначено значение по умолчанию.

    --                            INSERT INTO T_UPLOADS ( ID, periodID, projectID,UPLOADDATE,UPLOADFILENAME,UPLOADUSER_ID )
    --                             SELECT uf.ID,
    --                                    @PeriodID,
    --                                    ISNULL((select distinct PROJECT_ID from T_UPLOADS where project_savix_ID = @ProjectID),@ProjectID),
    --                                    GETDATE(),
    --                                    'Automatic upload from web MIS',
    --                                    2
    --                             FROM T_UPLOADSFIRSTSTEP uf
    --                             INNER JOIN #projects mp ON  uf.Project_ID_MIS = mp.ProjectID
    --                             WHERE  uf.Project_ID_MIS = @ProjectID AND uf.[STATUS] = 0 
    --                                AND NOT EXISTS (SELECT * FROM T_UPLOADS u WHERE u.PROJECT_ID  = uf.PROJECT_ID AND u.PERIOD_ID = @PeriodID AND u.[STATUS] = 3)

    --                            DELETE FROM T_UPLOADSFIRSTSTEP WHERE STATUS = 0 AND PROJECT_ID = ISNULL((select distinct PROJECT_ID from T_UPLOADS where project_savix_ID = @ProjectID),@ProjectID)

    --                            --SET @UploadID = (SELECT ID FROM T_UPLOADS u WHERE  u.project_savix_ID = @ProjectID AND u.PERIOD_ID = @PeriodID AND u.[STATUS] = 3)
    --            END 
                --ELSE

Так вот настоящая логика, которая делается внутри этих трех курсоров. Понимая, что нас не волнуют периоды, логика проходит через них все. Мы заботимся только о том, чтобы отфильтровать определенные критерии (status = 3 и UPLOADFILENAME = 'Автоматическая загрузка из веб-MIS') и чтобы у них была соответствующая таблица T_Project.ID с критериями T_Projects.SyncMode = 1

                            UPDATE t SET 
                                    TOTALEXPENDITURES = CASE WHEN DATEDIFF(d,mp.StartDate,mp.EndDate) != 0 
                                            THEN mp.Cost_Until_Today*DATEDIFF(d,mp.StartDate,dbo.fn_GetEndOfPeriod(t.Period_ID))/DATEDIFF(d,mp.StartDate,mp.EndDate)/dbo.fn_RateAtDate(mp.Currency,dbo.fn_GetEndOfPeriod(t.Period_ID))
                                            ELSE 0 
                                            END,
                                    TotalExpendituresNative = CASE WHEN DATEDIFF(d,mp.StartDate,mp.EndDate) != 0 
                                            THEN mp.Cost_Until_Today*DATEDIFF(d,mp.StartDate,dbo.fn_GetEndOfPeriod(t.Period_ID))/DATEDIFF(d,mp.StartDate,mp.EndDate)
                                            ELSE 0 
                                            END
                            FROM T_UPLOADS t
                            JOIN #projects mp ON mp.ProjectID = t.project_savix_ID
                            WHERE 1=1 -- t.ID = @UploadID 
                            --AND t.project_savix_ID = @ProjectID AND t.PERIOD_ID = @PeriodID 
                            AND t.[STATUS] = 3 AND t.UPLOADFILENAME = 'Automatic upload from web MIS'

Вставка в T_PROJECTGROUPDATA дублировала логику, использованную для создания переменной @dataid, полученной из #datasets. Который получен из таблицы наборов данных, которую мы удалили и создали выше.

                        INSERT INTO T_PROJECTGROUPSDATA (uploadID, fieldA,fieldB,......fieldN )

                         SELECT @UploadID,p.fieldA,mg.fieldB,......mgc.fieldN
                         FROM #projects mp
                         inner join  SG_Dynamic_Forms..v_projects p ON p.LegacyProjectId = mp.projectID 
                         inner join Savix_Service_Group..Groups mg ON mg.Grp_Project = p.ProjectID 
                         INNER JOIN Savix_Service_Group..Group_Cycle mgc ON mgc.Gc_GroupID = mg.Grp_ID
                         INNER JOIN Savix_Service_Group..Group_Data mgd ON mgd.Gd_CycleID = mgc.Gc_ID
                         LEFT JOIN Savix_Service_Trainers..Trainers me ON me.Tr_ID = mgc.Gc_MonitoredBy
                         LEFT JOIN Savix_Service_Dictionaries..Dictionary mgt ON mgt.Dny_ID = me.Tr_Type 
                         --left join v1_Report_UDF_Data_UploadToSavix udf on udf.DataID = mgd.Gd_ID
                         WHERE mgd.Gd_ID = @DataID 

                --FETCH NEXT FROM period INTO @PeriodID, @EndDate
                SET @MINIDperiods = @MINIDperiods + 1
            END
            --CLOSE period
            --DEALLOCATE period

            --FETCH NEXT FROM datasets INTO @DataID, @GroupID, @CollectionDate, @Status, @Createdate
            SET @MINIDdatasets = @MINIDdatasets + 1
        END

        --CLOSE datasets
        --DEALLOCATE datasets

        --COMMIT
    --END TRY
    --BEGIN CATCH



        --SELECT ERROR_NUMBER(), ERROR_MESSAGE(),@PeriodID, @ProjectID, @UploadID,@DataID
        --IF CURSOR_STATUS('global' , 'period') >= 0
        --BEGIN
        --    CLOSE period
        --    DEALLOCATE uploadID
        --END

        --IF CURSOR_STATUS('global' , 'datasets') >= 0
        --BEGIN
        --    CLOSE datasets
        --    DEALLOCATE datasets
        --END

        --IF @@TRANCOUNT > 0
        --    ROLLBACK

        --INSERT INTO error_catch_UploadtoSavix

        --SELECT cast(ERROR_NUMBER() as nvarchar), ERROR_MESSAGE(),@PeriodID, @ProjectID, @UploadID,@DataID, getdate()
    --END CATCH

    SET @MINIDprojects = @MINIDprojects + 1
    --FETCH NEXT FROM projects1 INTO @ProjectID, @FAID
END


--CLOSE projects1
--DEALLOCATE projects1

--SELECT 1 as success

Вот так. Сжатие почти 300 строк и 3 курсоров до 30 строк без курсоров.

...