寻求退出循环的帮助

Looking for help exiting out of loop

我一直在努力复制 AutoSys 的 Box 功能。我遇到了此处显示的解决方案 (https://dba.stackexchange.com/a/161658),它非常非常有效。我开始添加它,检查结果,并添加我们使用的另一个 ETL 解决方案的处理。

我 运行 遇到麻烦的地方是,当我意识到没有检查工作是否有效时。我想检查一下,以防工作名称拼写错误,或者有人删除了工作。如果没有,我不想假设正在执行一项工作。我添加了对有效 SQL 代理作业名称的检查。如果作业名称有效,则此方法有效。但是,如果作业名称无效,该过程将陷入循环并显示错误消息 'NO JOB',直到我停止该过程。

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE [dbo].[usp_start_job_sequence8]
(
 @JobList JobSequenceTable READONLY
,@PrntJob VARCHAR(100) = 'Unknown_Job'
)
AS
BEGIN
    SET NOCOUNT ON;
    SET XACT_ABORT ON;
    SET QUOTED_IDENTIFIER ON;
    SET ANSI_NULLS ON;
    SET ANSI_PADDING ON;
    SET ARITHABORT ON;
    SET CONCAT_NULL_YIELDS_NULL ON;
    SET NUMERIC_ROUNDABORT OFF;
    SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
    SET ANSI_WARNINGS OFF;


 ---------------------************TRY BLOCK************---------------------

    BEGIN TRY

        BEGIN
            DECLARE
                @JobNumber TINYINT = 1
               ,@JobName VARCHAR(100)
               ,@IsRunning BIT
               ,@IsEnabled BIT
               ,@JOB_ID VARCHAR(60) = NULL
               ,@JOB_HIST_ID INT
               ,@JOB_STATUS VARCHAR(30)
               ,@JOB_STATUS_ID INT
               ,@esub VARCHAR(100)
               ,@ebdy VARCHAR(500)
               ,@Envt VARCHAR(4)
               ,@OVJOB_ID VARCHAR(60)
               ,@OVJOB_NAME VARCHAR(120)
               ,@JOB_TYPE CHAR(3)
               ,@epri VARCHAR(6);


            --- Set server environment for emails
            SELECT
                    @Envt = CASE WHEN @@SERVERNAME LIKE '%D%' THEN 'Dev'
                                 WHEN @@SERVERNAME LIKE '%U%' THEN 'UAT'
                                 WHEN @@SERVERNAME LIKE '%P%' THEN 'Prod'
                                 WHEN @@SERVERNAME LIKE '%R%' THEN 'BCP'
                                 ELSE ''
                            END

             --- Set server environment for email priority
                   ,@epri = CASE WHEN @@SERVERNAME LIKE '%D%' THEN 'Low'
                                 WHEN @@SERVERNAME LIKE '%U%' THEN 'Normal'
                                 WHEN @@SERVERNAME LIKE '%P%' THEN 'High'
                                 WHEN @@SERVERNAME LIKE '%R%' THEN 'High'
                                 ELSE ''
                            END;


            BEGIN
                WHILE (@JobNumber <= (SELECT
                                            MAX(JobNumber)
                                        FROM
                                            @JobList
                                     ))
                    BEGIN
                        SELECT
                                @JobName = JobName
                            FROM
                                @JobList
                            WHERE
                                JobNumber = @JobNumber;

                        --VALID JOB?

                        IF NOT EXISTS(SELECT j.name FROM msdb.dbo.sysjobs_view J WITH(NOLOCK) 
                                    WHERE j.Name = @JobName)
                            BEGIN
                                PRINT 'NO JOB'
                                END;
                        ELSE
                            BEGIN
                            PRINT 'YES WE FOUND THE JOB';
                        --END




                        SELECT
                                @JOB_ID = job_id
                            FROM
                                msdb.dbo.sysjobs_view
                            WHERE
                                name = @JobName;



                        SELECT
                                @IsEnabled = enabled
                            FROM
                                msdb.dbo.sysjobs_view
                            WHERE
                                name = @JobName;
                       --- Very important step here.  Ouvvi job names must start with Ouvvi
                        SELECT
                                @JOB_TYPE = CASE WHEN @JobName LIKE 'Ouvvi%'
                                                 THEN 'OVI'
                                                 ELSE 'SQL'
                                            END;


                         --- Check if the job already running
                        SELECT
                                @IsRunning = dbo.fnJobStatusCheck(@JobName);

                        IF @IsRunning = 0
                            BEGIN
                                IF @IsEnabled = 0   --- Job is disabled error and send email
                                    BEGIN
                                        PRINT 'Job ' + @JobName
                                            + ' is disabled and cannot be started';
                                        SET @esub = 'SQL Agent job '
                                            + @JobName + ' in ' + @Envt
                                            + ' is disabled and cannot be started';
                                        SET @ebdy = 'SQL Agent job '
                                            + @JobName
                                            + ' was scheduled to run in box job '
                                            + @PrntJob + ' on server '
                                            + @@SERVERNAME + '.  '
                                            + @JobName
                                            + ' could not start as it is disabled.'
                                            + CHAR(10) + CHAR(13)
                                            + +'The job ' + @JobName
                                            + ' should either be enabled, or removed from box job '
                                            + @PrntJob + '.';
                                        EXEC msdb.dbo.sp_send_dbmail
                                                --@profile_name = '',
                                                --  @recipients = 'group@mail.com'
                                            @recipients = 'person@mail.com',
                                            @importance = @epri,
                                            @subject = @esub,@body = @ebdy;
                                    END;




                                ELSE  ---- @IsEnabled = 1
                                        ----- Job is not running nor disabled.  Split for different types

                                        ---OUVVI
                                        BEGIN
                                            IF @JOB_TYPE = 'OVI'
                                                BEGIN
                                                --PRINT 'OUVVI JOB';   --- TESTING

                                                --- Parse Ouvvi Project ID - Used for success-failure

                                                SET @OVJOB_ID = (SELECT
                                                          RTRIM(SUBSTRING(command,
                                                          CHARINDEX('/start/',
                                                          command) + 7,3))
                                                          FROM
                                                          msdb.dbo.sysjobsteps
                                                          WHERE
                                                          job_id = @JOB_ID
                                                          AND step_id = 1
                                                          );

                                                --- START Ouvvi Job             
                                                EXEC msdb.dbo.sp_start_job @job_name = @JobName;

                                               --PRINT @OVJOB_ID; --- TESTING


                                                -- Waiting for the job to finish - Ouvvi jobs don't start immediately

                                                WAITFOR DELAY '00:00:01';
                                                WHILE (SELECT
                                                          1
                                                        FROM
                                                          Ouvvi.dbo.Queue
                                                        WHERE
                                                          ProjectID = @OVJOB_ID
                                                      ) IS NOT NULL
                                                    BEGIN
                                                        WAITFOR DELAY '00:00:15';
                                                        IF (SELECT
                                                          1
                                                          FROM
                                                          Ouvvi.dbo.Queue
                                                          WHERE
                                                          ProjectID = @OVJOB_ID
                                                          ) IS NULL
                                                          BREAK;
                                                    END;


                                                 --- Get Ouvvi Job Hist ID

                                                SET @JOB_HIST_ID = (SELECT
                                                          Instance.ID
                                                          FROM
                                                          Ouvvi.dbo.Instance
                                                          WHERE
                                                          Instance.ProjectID = @OVJOB_ID
                                                          AND Instance.EndTime = (SELECT
                                                          MAX(EndTime)
                                                          FROM
                                                          Ouvvi.dbo.Instance
                                                          WHERE
                                                          Instance.ProjectID = @OVJOB_ID
                                                          )
                                                          );

                                                --- Get Ouvvi Result
                                                SET @JOB_STATUS_ID = (SELECT
                                                          ISNULL(I.Result,
                                                          9)
                                                          FROM
                                                          Ouvvi.dbo.Instance I
                                                          WHERE
                                                          I.ID = @JOB_HIST_ID
                                                          );

                                                SET @JOB_STATUS = (SELECT
                                                          CASE
                                                          WHEN I.Result = 1
                                                          THEN 'Succeeded'
                                                          WHEN I.Result = 2
                                                          THEN 'Failed'
                                                          WHEN I.Result = 3
                                                          THEN 'Cancelled'
                                                          ELSE 'Unknown'
                                                          END
                                                          FROM
                                                          Ouvvi.dbo.Instance I
                                                          WHERE
                                                          I.ID = @JOB_HIST_ID
                                                          );



                                                IF @JOB_STATUS_ID <> 1
                                                    BEGIN

                                                        PRINT @JobName
                                                          + ' erred with the following status: '
                                                          + @JOB_STATUS;
                                                        SET @esub = 'Ouvvi SQL Agent job '
                                                          + @JobName
                                                          + ' in ' + @Envt
                                                          + ' erred with the following status: '
                                                          + @JOB_STATUS;
                                                        SET @ebdy = 'An Ouvvi job, scheduled in SQL Agent '
                                                          + @JobName
                                                          + ' has erred with the following status: '
                                                          + @JOB_STATUS
                                                          + ' on server '
                                                          + @@SERVERNAME
                                                          + '.';
                                                        EXEC msdb.dbo.sp_send_dbmail
                                                              --@profile_name   = '',
                                                                --  @recipients = 'group@mail.com'
                                                          @recipients = 'person@mail.com',
                                                          @importance = @epri,
                                                          @subject = @esub,
                                                          @body = @ebdy;
                                                    END;

                                            END;








                                        ----Its a SQL Server Job
                                        ELSE
                                            BEGIN
                                                EXEC msdb.dbo.sp_start_job @job_name = @JobName;
                                            END;

                                        WAITFOR DELAY '00:00:15.000';
                                        SELECT
                                                @IsRunning = dbo.fnJobStatusCheck(@JobName);

                                        WHILE @IsRunning = 1
                                            BEGIN
                                                WAITFOR DELAY '00:00:15.000';
                                                SELECT
                                                        @IsRunning = dbo.fnJobStatusCheck(@JobName);
                                            END;




                                        BEGIN
                                            SET @JOB_HIST_ID = (SELECT
                                                          job_history_id
                                                          FROM
                                                          msdb.dbo.sysjobactivity
                                                          WHERE
                                                          job_id = @JOB_ID
                                                          AND run_requested_date = (SELECT
                                                          MAX(run_requested_date)
                                                          FROM
                                                          msdb.dbo.sysjobactivity
                                                          WHERE
                                                          job_id = @JOB_ID
                                                          )
                                                          );

                                            SET @JOB_STATUS_ID = (SELECT
                                                          ISNULL(run_status,
                                                          9)
                                                          FROM
                                                          msdb.dbo.sysjobhistory
                                                          WHERE
                                                          instance_id = @JOB_HIST_ID
                                                          );

                                            SET @JOB_STATUS = (SELECT
                                                          CASE
                                                          WHEN @JOB_STATUS_ID = 0
                                                          THEN 'Failed'
                                                          WHEN @JOB_STATUS_ID = 1
                                                          THEN 'Succeeded'
                                                          WHEN @JOB_STATUS_ID = 2
                                                          THEN 'Retry'
                                                          WHEN @JOB_STATUS_ID = 3
                                                          THEN 'Cancelled'
                                                          ELSE 'Unknown'
                                                          END
                                                          );

                                            BEGIN
                                                IF @JOB_STATUS_ID <> 1
                                                    BEGIN

                                                        PRINT @JobName
                                                          + ' erred with the following status: '
                                                          + @JOB_STATUS;
                                                        SET @esub = 'SQL Agent job '
                                                          + @JobName
                                                          + ' in ' + @Envt
                                                          + ' erred with the following status: '
                                                          + @JOB_STATUS;
                                                        SET @ebdy = 'SQL Agent job '
                                                          + @JobName
                                                          + ' erred with the following status: '
                                                          + @JOB_STATUS
                                                          + ' on server '
                                                          + @@SERVERNAME
                                                          + '.';
                                                        EXEC msdb.dbo.sp_send_dbmail
                                                             --@profile_name    = '',
                                                             --  @recipients = 'group@mail.com'
                                                          @recipients = 'person@mail.com',
                                                          @importance = @epri,
                                                          @subject = @esub,
                                                          @body = @ebdy;
                                                    END;
                                            END;

                                        END;

                                    END;
                                SET @JOB_ID = NULL;
                                SET @JobNumber = @JobNumber + 1;
                            END;


                    END;
                END;
            END;
        END;



    END TRY
  ---------------------*********************************--------------------


 ---------------------************CATCH BLOCK**********-------------------

    BEGIN CATCH

-- Print Error Information
        DECLARE @ERRORMESSAGE NVARCHAR(4000);
        DECLARE @ERRORSEVERITY INT;
        DECLARE @ERRORSTATE INT;

        SELECT
                @ERRORMESSAGE = ERROR_MESSAGE()
               ,@ERRORSEVERITY = ERROR_SEVERITY()
               ,@ERRORSTATE = ERROR_STATE();

        RAISERROR (@ERRORMESSAGE, @ERRORSEVERITY, @ERRORSTATE);


-- Rollback uncommittable transactions

        IF (XACT_STATE()) = -1
            BEGIN
                PRINT 'The transaction is in an uncommittable state.'
                    + ' Rolling back transaction.';
                ROLLBACK TRANSACTION;
            END;


-- Inserting error related information into the Error Log table  

        INSERT INTO dbo.tbl_Object_ErrorLog
                (ObjectName
                ,ErrorNumber
                ,ErrorMessage
                ,ErrorSeverity
                ,ErrorState
                ,ErrorlINE
                ,SystemUser
                ,LogDate
                )
                SELECT
                        ERROR_PROCEDURE()
                       ,ERROR_NUMBER()
                       ,ERROR_MESSAGE()
                       ,ERROR_SEVERITY()
                       ,ERROR_STATE()
                       ,ERROR_LINE()
                       ,SYSTEM_USER
                       ,GETDATE();

    END CATCH;

---------------------********************************----------------------

END;

GO

启动程序的代码:

SET ANSI_WARNINGS OFF
GO
DECLARE @JobList AS JobSequenceTable
INSERT INTO @JobList
VALUES 

('x_test3')
,('NoJobHere')
,('x_test1')

EXEC dba.dbo.usp_start_job_sequence8 @JobList, 'TESTING'

我想要发生的是:当它检查有效的作业名称时,打印 NO JOB 并结束,它应该转到第 378 行,将 1 添加到 @JobNumber,结束该轮,转到下一个作业。

我不明白为什么它会卡在循环中。 感谢您的帮助。

由于您的循环很长且包含嵌套块且难以调试,我将执行以下操作:
- 在进入循环之前,验证缺少哪些作业
- 创建一个 table 来保存丢失的工作,例如@MissingJobs,在你处理现有工作之前用它们做你的事情
- 基于@MissingJobs 从@JobList table 中删除缺失的作业,然后循环遍历这些作业,因此在这种情况下无需调试长循环块。或者创建一个新的 table 如果您需要原始文件并在循环中使用它 - 但在这种情况下,需要做更多的工作才能从 @JobList 更改为新文件。如果将所有减少的@JobList 与@MissingJobs 联合,您可以取回原始列表。无论如何,您需要删除 --VALID JOB 之后的块? (直到 PRINT 'YES WE FOUND THE JOB';)因为你可以根据这个逻辑打印出来 - 在这种情况下你还必须在 SET @JobNumber = @JobNumber + 1; 之后删除一个 END;因为你删除了一个未关闭的 ELSE BEGIN。