sql >> Database >  >> RDS >> Sqlserver

Hoe een op CURSOR gebaseerde query te converteren naar een op SET gebaseerde?

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'

Om uit te leggen hoe ik hieraan kwam, is hier een aangepaste versie van de onderstaande code. Ik kan niet garanderen dat ik alles heb gekregen, ik moest wat aannames doen. Er was een gebrek aan voorbeeldgegevens en alle savix-tabellen ontbreken. Dit is het beste antwoord dat ik kan bedenken gezien de informatie en het beperken van mijn tijd tot minder dan 4 uur geïnvesteerd. Ik zou veel meer kunnen doen, maar je zou me meer moeten geven dan internetfaam.

--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

Het is moeilijk om cursors te werken en te testen, dus heb ik ze (projecten, projecten1, datasets en periode) vervangen door tijdelijke tabellen met een identiteit en heb ik ze doorgelust.

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 bevat alle ID's ( projectID ) van T_Projects, de enige velden die uit deze tabel worden gebruikt zijn de ID, projectcurrency, projectestablisheddate ( startdate ), enddate en syncmode ( moet =1 zijn, dat zullen we later zien ). We kunnen de systeemstatus negeren omdat het een constante is.

De tabel-dastasets worden hier neergezet en geladen. We geven alleen om de gd_id van de tabel Savix_Service_Group..Group_Data, maar we moeten nog steeds lid worden van de andere tabellen voor het geval ze waarden uitfilteren die we niet willen. Deze logica wordt hieronder nogmaals gebruikt bij het invoegen van records.

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

Hier voegen we in projects1 een duidelijke waarde van project-ID's in. Zonder meer gegevens is mijn veronderstelling dat de ID verschillend is in de T_projects-tabel en daarom is deze stap enigszins onnodig, afgezien van het uitfilteren door SyncMode =1. Onthoud dat SystemStatus een constante is en dat we deze hebben ingevuld met de waarde '4', dit criteria konden zonder effect worden verwijderd.

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

Hier voegen we in #datasets filteren op 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

Dit is waarschijnlijk het slechtste gebruik van een cursor hier. We laden alle perioden en doorbladeren ze. Uiteindelijk geven we alleen om perioden die in de T_UPLOADS-tabel staan.

            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

Hier slaan we alle perioden over waar UPLOADFILENAME !='Automatisch uploaden van web-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

Met dat in gedachten geven we er alleen om waar status =3 en UPLOADFILENAME ='Automatisch uploaden van web 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)

Waarom ze een tweede tabel gebruiken ( T_UPLOADFIRSTSTEP ) om een ​​record te genereren waarbij ze alleen de id gebruiken en vervolgens de record met die ID verwijderen bij het maken van de T_Uploads-record, gaat mijn begrip te boven en lijkt me vreselijke codering.

    --                            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

Het invoegen in T_UPLOADS zal altijd mislukken omdat er velden zijn die niet null kunnen zijn die niet in de invoeglijst staan ​​en waaraan geen standaardwaarde is toegewezen.

    --                            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

Dus hier is de echte logica die binnen die 3 cursors wordt gedaan. Als we begrijpen dat we niet om menstruatie geven, loopt de logica door ze allemaal heen. We geven er alleen om dat we bepaalde criteria moeten filteren (status =3 en UPLOADFILENAME ='Automatic upload from web MIS') en dat ze een overeenkomende T_Project.ID-tabel hebben met de criteria 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'

Het invoegen in T_PROJECTGROUPDATA heeft de logica gedupliceerd die is gebruikt om de @dataid-variabele te maken die is afgeleid van #datasets. Die is afgeleid van de tabel met gegevenssets die we hierboven hebben neergezet en gemaakt.

                        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

Daar heb je het. Met bijna 300 regels en 3 cursors tot 30 regels en geen cursors.




  1. Heeft mysql het equivalent van de analytische functies van Oracle?

  2. mysql indien niet aanwezig

  3. Django converteren van SQLite naar PostgreSQL

  4. Tel het voorkomen van het veld terwijl je groepeert op 2