如何在C#中识别SQL作业是否成功执行

How to Identify whether SQL job is successfully executed or not in C#

我有一个 C# 方法来执行 SQL 作业。它成功执行了 SQL 作业。 代码运行完美。

我为此使用标准 SQL 存储过程 msdb.dbo.sp_start_job

这是我的代码..

public int ExcecuteNonquery()
{
     var result = 0;
     using (var execJob =new SqlCommand())
     {
          execJob.CommandType = CommandType.StoredProcedure;
          execJob.CommandText = "msdb.dbo.sp_start_job";
          execJob.Parameters.AddWithValue("@job_name", "myjobname");
          using (_sqlConnection)
          {
               if (_sqlConnection.State == ConnectionState.Closed) 
                  _sqlConnection.Open();

               sqlCommand.Connection = _sqlConnection;
               result = sqlCommand.ExecuteNonQuery();

               if (_sqlConnection.State == ConnectionState.Open) 
                 _sqlConnection.Close();
          }
     }
     return result;
}

这是在作业中执行的sp

ALTER PROCEDURE [Area1].[Transformation]
              AS 
              BEGIN
              SET NOCOUNT ON;

              SELECT NEXT VALUE FOR SQ_COMMON
              -- Transform Master Data
              exec [dbo].[sp_Transform_Address];
              exec [dbo].[sp_Transform_Location];
              exec [dbo].[sp_Transform_Product];
              exec [dbo].[sp_Transform_Supplier];
              exec [dbo].[sp_Transform_SupplierLocation];

              -- Generate Hierarchies and Product References
              exec [dbo].[sp_Generate_HierarchyObject] 'Area1',FGDemand,1;
              exec [dbo].[sp_Generate_HierarchyObject] 'Area1',RMDemand,2;
              exec [dbo].[sp_Generate_Hierarchy] 'Area1',FGDemand,1;
              exec [dbo].[sp_Generate_Hierarchy] 'Area1',RMDemand,2;
              exec [dbo].[sp_Generate_ProductReference] 'Area1',FGDemand,1;
              exec [dbo].[sp_Generate_ProductReference] 'Area1',RMDemand,2;

              -- Transform Demand Allocation BOM 
              exec [Area1].[sp_Transform_FGDemand];
              exec [Area1].[sp_Transform_FGAllocation];
              exec [Area1].[sp_Transform_RMDemand];
              exec [Area1].[sp_Transform_RMAllocation];
              exec [Area1].[sp_Transform_BOM];
              exec [Area1].[sp_Transform_RMDemand_FK];

              -- Transform Purchasing Document Data
              exec [dbo].[sp_Transform_PurchasingDoc];
              exec [dbo].[sp_Transform_PurchasingItem];
              exec [dbo].[sp_Transform_ScheduleLine];


              exec [dbo].[sp_CalculateRequirement] 'Area1'
              exec [dbo].[sp_Create_TransformationSummary] 'Area1'
              -- Trauncate Integration Tables 
              exec [dbo].[sp_TruncateIntegrationTables] 'Area1'

              END

问题是,无论作业是否成功执行,它总是returns -1。如何确定作业是否成功执行。

来自MSDN 关于SqlCommand.ExecuteNonQuery 方法:

For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command. When a trigger exists on a table being inserted or updated, the return value includes the number of rows affected by both the insert or update operation and the number of rows affected by the trigger or triggers. For all other types of statements, the return value is -1. If a rollback occurs, the return value is also -1.

这一行:

result = sqlCommand.ExecuteNonQuery();

您想 return 受命令影响的行数并将其保存到一个 int 变量但是由于语句的类型是 select 所以它 returns -1。如果您使用 INSERTDELETEUPDATE 语句对其进行测试,您将获得正确的结果。

顺便说一下,如果你想获得受 SELECT 命令影响的行数并将其保存到一个 int 变量中,你可以尝试这样的事情:

select count(*) from jobs where myjobname = @myjobname

然后用ExecuteScalar得到正确的结果:

result = (int)execJob.ExecuteScalar();

在 运行 msdb.dbo.sp_start_job 之后 return 代码映射到输出参数。您有机会在执行前控制参数的名称:

public int StartMyJob( string connectionString )
{
 using (var sqlConnection = new SqlConnection( connectionString ) )
 {
   sqlConnection.Open( );
   using (var execJob = sqlConnection.CreateCommand( ) )
   {
      execJob.CommandType = CommandType.StoredProcedure;
      execJob.CommandText = "msdb.dbo.sp_start_job";
      execJob.Parameters.AddWithValue("@job_name", "myjobname");
      execJob.Parameters.Add( "@results", SqlDbType.Int ).Direction = ParameterDirection.ReturnValue;      
      execJob.ExecuteNonQuery();
      return ( int ) sqlCommand.Parameters["results"].Value;
    }
  }
}

您需要知道 return 代码的数据类型才能执行此操作 - 对于 sp_start_job,它是 SqlDbType.Int

然而,这只是开始作业的结果,值得了解,而不是运行[的结果=41=]你的工作。要获得作业的结果运行,您可以定期执行:

msdb.dbo.sp_help_job @jobName

该过程 return 编辑的其中一列是 last_run_outcome,可能包含您真正感兴趣的内容。它将是 5(未知),而它仍然是 运行 .

一个作业通常是由多个步骤组成的 - 其中每个步骤可能会或可能不会根据先前步骤的结果执行。另一个名为 sp_help_jobhistory 的过程支持大量过滤器以指定您感兴趣的作业的哪些特定调用 and/or 步骤。

SQL 喜欢将工作视为计划工作 - 但没有什么可以阻止你开始临时工作 - 尽管它并没有真正为你提供太多支持来关联你的广告-实例的临时作业是作业历史记录。日期已经差不多了(除非有人知道我不知道的技巧。)

我已经看到作业是在 创建的 临时作业之前 运行 它,因此当前的临时执行是唯一的执行 return编辑。但是你最终会得到很多重复或接近重复的工作,这些工作永远不会再次执行。如果你走那条路,你将不得不计划事后清理。

关于您使用 _sqlConnection 变量的说明。你不想那样做。您的代码处理了它,但它显然是在调用此方法之前在其他地方创建的。那是糟糕的 juju。您最好只创建连接并以相同的方法处理它。依靠 SQL 连接池来加快连接速度 - 这可能已经打开了。

此外 - 在您发布的代码中 - 看起来您从 execJob 开始但切换到 sqlCommand - 有点搞砸了编辑。我一直以为您指的是 execJob - 这在示例中有所体现。

你需要运行存储过程msdb.dbo.sp_help_job

     private int CheckAgentJob(string connectionString, string jobName) {
        SqlConnection dbConnection = new SqlConnection(connectionString);
        SqlCommand command = new SqlCommand();
        command.CommandType = System.Data.CommandType.StoredProcedure;
        command.CommandText = "msdb.dbo.sp_help_job";
        command.Parameters.AddWithValue("@job_name", jobName);
        command.Connection = dbConnection;
        using (dbConnection)
        {
            dbConnection.Open();      
            using (command){
                SqlDataReader reader = command.ExecuteReader();
                reader.Read();
                int status = reader.GetInt32(21); // Row 19 = Date Row 20 = Time 21 = Last_run_outcome
                reader.Close();
                return status;
            }
        }
     }

enum JobState { Failed = 0, Succeeded = 1, Retry = 2, Cancelled = 3, Unknown = 5};

继续轮询“未知”,直到您得到答案。让我们希望它成功:-)