Stored procedures 从SQL代理作业调用时,工作存储过程失败

Stored procedures 从SQL代理作业调用时,工作存储过程失败,stored-procedures,sql-server-2008-r2,sql-agent-job,Stored Procedures,Sql Server 2008 R2,Sql Agent Job,我有一个存储过程,在SQLServerManagementStudio中运行良好,没有错误。但是,当同一存储过程作为SQL代理作业的一个步骤执行时,它将终止: 错误3930:无法提交当前事务,并且无法支持写入日志文件的操作。回滚事务 存储过程位于名为[Billing]的模式中。它使用的大多数表也在[Billing]模式中 主存储过程开始数据库事务。主存储过程调用的存储过程在继承的事务上完成所有工作。主存储过程负责提交或回滚事务 运行SQL代理作业步骤的数据库用户不是Sysadmin角色,也不是d

我有一个存储过程,在SQLServerManagementStudio中运行良好,没有错误。但是,当同一存储过程作为SQL代理作业的一个步骤执行时,它将终止:

错误3930:无法提交当前事务,并且无法支持写入日志文件的操作。回滚事务

存储过程位于名为[Billing]的模式中。它使用的大多数表也在[Billing]模式中

主存储过程开始数据库事务。主存储过程调用的存储过程在继承的事务上完成所有工作。主存储过程负责提交或回滚事务

运行SQL代理作业步骤的数据库用户不是Sysadmin角色,也不是dbo。它属于db_datareader和db_datawriter数据库角色,并在[Billing]模式中被授予删除、执行、插入、引用、选择和更新权限

以下是主要的存储过程:

CREATE PROCEDURE [Billing].[GenerateBillXml]
  @pUseProductionPsSystem bit
  ,@pPeriodYear int
  ,@pPeriodMonthNumber int
  ,@pDocumentTypeName varchar(20)
  ,@pUser varchar(100)
  ,@pFilePath varchar(500)
  ,@pExportDateTime datetime2(7)
  ,@pResultCode int = 0 OUTPUT
AS
BEGIN
    SET NOCOUNT ON;

  SET @pResultCode = 0;

  DECLARE @transactionBegun bit = 0
          ,@RC int = 0
          ,@processDateTimeUtc datetime2(7) = GETUTCDATE()
          ,@billGenerationId int
          ,@PsJobSuffix char(2)
          ,@periodDate date
          ,@periodDateString char(6)
          ,@PsBaseJobNumber char(6)
          ,@okayToRun int = 0
          ,@msg varchar(500);

  BEGIN TRY
    /* calculate the period date */
    SET @periodDate = CONVERT(date,CAST(@pPeriodMonthNumber as varchar) + '/01/' + CAST(@pPeriodYear as varchar))
    SET @periodDateString = CONVERT(varchar, @periodDate, 12); -- yyMMdd

    /* retrieve the job suffix */
    SELECT @PsJobSuffix = CAST([PsJobSuffix] as char(2))
      FROM [dbo].[DocumentType] dt
      WHERE [Name] like @pDocumentTypeName;

    /* build the base job number */
    SET @PsBaseJobNumber = LEFT(@periodDateString, 4) + @PsJobSuffix

    /*
     * We've made it past the input check - record the fact that we're generating a bill
     */
    INSERT [Billing].[BillGeneration] (
      [PsBaseJobNumber], [Status], [RunBy], [ProcessDateTimeUtc]
    ) VALUES (
      @PsBaseJobNumber, 'Running', @pUser, @processDateTimeUtc
    );

    IF @@ROWCOUNT = 1
      SET @billGenerationId = SCOPE_IDENTITY();

    EXECUTE @RC = [Billing].[_0_OkayToGenerateBill]
      @PsBaseJobNumber
      ,@okayToRun OUTPUT
      ,@pResultCode OUTPUT;

    IF @pResultCode = 0  
    BEGIN
      -- called stored procedure completed without error
      IF @okayToRun = -1 -- this bill has already been generated
      BEGIN
        SET @msg = 'The billing for job ' + CAST(@PsBaseJobNumber as varchar) + ' has already been produced.';
        RAISERROR(@msg, 16, 1)
      END

      IF @okayToRun = -2 -- too early to run billing for this period
      BEGIN
        SET @msg = 'It is too early to generate billing for job ' + CAST(@PsBaseJobNumber as varchar) + '.';
        RAISERROR(@msg, 16, 1)
      END

      IF @okayToRun <> 1 -- unknown error...
      BEGIN
        SET @msg = 'Unknown error occured while determining whether okay to generate bill for job ' + CAST(@PsBaseJobNumber as varchar) + '.';
        RAISERROR(@msg, 16, 1)
      END
    END
    ELSE
    BEGIN
      SET @msg = 'Unknown failure in sub-stored procedure [Billing].[_0_OkayToRun]() for job ' + CAST(@PsBaseJobNumber as varchar) + '.';
      RAISERROR(@msg, 16, 1)  -- will cause branch to CATCH
    END

    /* Okay to generate bill */

    /* If not in a transaction, begin one */
    IF @@TRANCOUNT = 0
    BEGIN
      BEGIN TRANSACTION
      SET @transactionBegun = 1;
    END

    EXECUTE @RC = [Billing].[_1_GeneratePsPreBillData] 
       @PsBaseJobNumber
      ,@pUser
      ,@pResultCode OUTPUT;

    IF @pResultCode = 0
    BEGIN
      -- stored proced ran to successful completion
      EXECUTE @RC = [Billing].[_2_GetBillingDataForXmlGeneration]
         @pUseProductionPsSystem
        ,@PsBaseJobNumber
        ,@pResultCode OUTPUT;

      IF @pResultCode = 0
      BEGIN
        -- stored proced ran to successful completion
        IF @transactionBegun = 1
          -- all table data has been created/updated
          COMMIT TRANSACTION

        -- Output XML bill to file
        EXECUTE @RC = [Billing].[_3_GenerateBillingXmlFilesForPsJob] 
           @PsBaseJobNumber
          ,@pFilePath
          ,@pExportDateTime
          ,@pResultCode OUTPUT;

        IF @pResultCode <> 0
        BEGIN
          -- called stored procedure failed
          SET @msg = '[Billing].[_3_GenerateBillingXmlFilesForPsJob]() failed for job ' + CAST(@PsBaseJobNumber as varchar);
          RAISERROR(@msg, 16, 1)  -- will cause branch to CATCH
        END
      END
      ELSE
      BEGIN
        -- called stored procedure failed
        SET @msg = '[Billing].[_2_GetBillingDataForXmlGeneration]() failed for job ' + CAST(@PsBaseJobNumber as varchar);
        RAISERROR(@msg, 16, 1)  -- will cause branch to CATCH
      END
    END
    ELSE
    BEGIN
        -- called stored procedure failed
      SET @msg = '[Billing].[_1_GeneratePsPreBillData]() failed for job ' + CAST(@PsBaseJobNumber as varchar);
      RAISERROR(@msg, 16, 1)  -- will cause branch to CATCH
    END

    -- bill generation was successful
    IF @billGenerationId IS NOT NULL
      UPDATE [Billing].[BillGeneration]
        SET [Status] = 'Successful', [ProcessEndDateTimeUtc] = GETUTCDATE()
        WHERE [Id] = @billGenerationId;

  END TRY
  BEGIN CATCH
    -- rollback transaction if we started one
    IF @transactionBegun = 1
      ROLLBACK TRANSACTION

    -- record the error
    INSERT [Billing].[BillGenerationError] (
      [DateTime], [Object], [ErrorNumber], [ErrorMessage]
    ) VALUES (
      GETDATE(), OBJECT_NAME(@@PROCID), ERROR_NUMBER(), ERROR_MESSAGE()
    );

    -- bill generation failed
    IF @billGenerationId IS NOT NULL
      UPDATE [Billing].[BillGeneration]
        SET [Status] = 'Failed'
            ,[Note] = ERROR_MESSAGE()
            ,[ProcessEndDateTimeUtc] = GETUTCDATE()
        WHERE [Id] = @billGenerationId;

    SELECT ERROR_NUMBER() as ErrorNumber;
    SELECT ERROR_MESSAGE() as ErrorMessage;
    SET @pResultCode = 1
  END CATCH
END

正如@Lukasz Szozda在他对我的问题的一个评论中所暗示的,问题是当SQL代理作业执行BCP.EXE命令时,它是在SQL代理使用的服务帐户下运行的,对我来说,该帐户是相当严格的本地系统帐户。在这一点上,我很明显,必须使用代理帐户。因此,我在操作系统CmdExec下创建了一个代理,这是唯一有意义的选择

我返回到作业步骤,将其更改为使用代理,但随后注意到,在其当前类型的Transact-SQL脚本TSQL中,无法分配代理帐户

在尝试了一些方法之后,最终决定将作业步骤中的TSQL语句放入一个新的存储过程中,然后从SQL命令行可执行文件SQLCMD.EXE调用该存储过程。然后,我将作业步骤类型从Transact-SQL脚本TSQL更改为操作系统CmdExec。然后,我可以将runas字段设置为我先前创建的代理。我指定命令作为CMD.EXE/c SQLCMD.EXE-S[ServerName]-Q EXEC[NewProcedureName][parameters]运行

如果您想知道我为什么在CMD.EXE下运行SQLCMD.EXE,那是因为新存储过程的一个参数是特定格式“%date:~4,10%”的当前日期,SQL Server作业执行环境不支持该格式,但CMD.EXE肯定支持该格式


总的来说,我认为这比我预期的要花费更多的精力。

您是否在子程序或sp_xml_preparedocument中使用任何文件操作?看起来是权限问题。不。但是,要调用的最后一个存储过程[_3_GenerateBillingXmlFilesForPsJob]使用EXEC xp_cmdshell调用BCP.exe并执行查询以提取数据,然后将其写入文件。我已检查以确保运行存储过程的用户的帐户对该文件夹具有修改权限。因此,我们拥有它。您的SQL代理帐户没有访问权限。您可以创建代理帐户。我通过使用数据库用户的域帐户登录到计算机并直接在SQLServerManagementStudio中成功运行存储过程来测试这一点。已成功创建所有文件。是否在SQL Server代理使用的同一用户/帐户上运行SSMS?