sql >> データベース >  >> RDS >> Sqlserver

CURSOR ベースのクエリを SET ベースに変換する方法

    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
    

    カーソルを操作してテストするのは難しいため、それら (projects、projects1、datasets、および period) を ID を含む一時テーブルに置き換えて、それらをループしました。

    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 には、T_Projects のすべての ID ( projectID ) が含まれます。このテーブルから使用されるフィールドは、ID、projectcurrency、project Establisheddate ( startdate )、enddate および syncmode ( =1 でなければなりません。後で確認します) のみです。 systemstatus は定数なので無視できます。

    テーブル データセットはここにドロップされてロードされます。 Savix_Service_Group..Group_Data テーブルからの gd_id のみを考慮しますが、不要な値を除外する場合に備えて、他のテーブルに結合する必要があります。このロジックは、レコードを挿入するときに下で再び使用されます。

    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
    

    ここでは、projects1 に個別のプロジェクト ID の値を挿入しています。これ以上のデータがない場合、ID は 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 による #datasets フィルタリングに挿入します

            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 !='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
    

    そのことを念頭に置いて、status =3 および UPLOADFILENAME ='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)
    

    彼らが 2 番目のテーブル ( T_UPLOADFIRSTSTEP ) を使用して ID のみを使用するレコードを生成し、T_Uploads レコードの作成でその ID を使用してレコードを削除する理由は、私の理解を超えており、コーディングがひどいように思えます。

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

    したがって、これらの 3 つのカーソル内で実行される実際のロジックは次のとおりです。ピリオドは気にしないことを理解すると、ロジックはそれらすべてをループします。特定の基準 ( status =3 および UPLOADFILENAME ='Web MIS からの自動アップロード' ) をフィルタリングする必要があることと、基準 T_Projects.SyncMode =1 を持つ一致する T_Project.ID テーブルがあることのみが重要です。

                                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 への挿入は、#datasets から派生した @dataid 変数を作成するために使用されるロジックを複製しました。これは、上でドロップして作成したデータセット テーブルから派生したものです。

                            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 行とカーソルなしに凝縮します。




    1. MySQLは、ビューを含むバックアップを確実に復元できますか?

    2. mysqlデータベースで行とタイムスタンプの共通部分を検索します

    3. PostgresのOPENXML

    4. MySQL groupwise MAX()は予期しない結果を返します