【问题标题】:Multiple Invoke-SqlCmd and Sql Server transaction多个 Invoke-SqlCmd 和 Sql Server 事务
【发布时间】:2012-12-08 07:07:03
【问题描述】:

如何确保以下语句在一个事务中运行?

try
{
  Invoke-SqlCmd -Query 'begin tran; ...sql script...'
  # Other Powershell script
  Invoke-SqlCmd -Query '...sql script...; if @@transcation > 0 commit tran' 
}
catch 
{
   Invoke-SqlCmd -Query 'if @@transaction > 0 rollback tran;'
}

在事务中运行 Powershell 脚本的最佳方法是什么?

【问题讨论】:

  • PowerShell 有 Start-Transaction,你指的是这个吗? technet.microsoft.com/en-us/library/hh849772.aspx
  • @ShawnMelton 不,我试过Start-Transaction; Invoke-Sqlcmd 'select 1 a into ##test' -ServerInstance myserver; Undo-Transaction,它仍然创建了表格。

标签: sql-server powershell


【解决方案1】:

不幸的是,Powershell 中的SqlProvider 没有像Registry 提供程序那样的事务能力。因此Start-transaction 在这里不起作用。

您可以原生并使用TransactionScope 类。

Try{
   $scope = New-Object -TypeName System.Transactions.TransactionScope

   Invoke-Sqlcmd -server Darknite -Database AdventureWorks2008 -Query $Query1 -ea stop
   Invoke-Sqlcmd -server Darknite -Database AdventureWorks2008 -Query $Query2 -ea stop

   $scope.Complete() 
}
catch{
    $_.exception.message
}
finally{
    $scope.Dispose() 
}

在 $scope assignment 和 $scope.complete() 之间的所有 Invoke-sqlcmds 将被视为一个事务。如果它们中的任何一个出错,都将被回滚。

【讨论】:

  • 这在最新的 powershell 版本中效果不佳
【解决方案2】:

Nitesh 举了一个很好的例子,帮助了我,谢谢! 要添加的一件事是 Scope.Complete() 不会将事务提交发送到数据库。 如果您在循环中使用此代码,这将是显而易见的。

提交是在 end using 为您的范围执行的,因此确保提交的工作示例是在完成后添加显式处置,如下所示:

$scope.Complete();
$scope.Dispose();

查看详情 TransactionScope Complete() doesn't commit the transaction before exiting the USING statement

【讨论】:

    【解决方案3】:

    Invoke-sqlcmd 不支持 ADO.NET 事务。您有两种解决方法,或者将等效的 Powershell 代码写入此 MSDN 文档中针对 ADO.NET 事务显示的 C# 代码: http://msdn.microsoft.com/en-us/library/2k2hy99x(v=vs.110).aspx

    或者使用 T-SQL 事务。在不向每个脚本添加 T-SQL Try/Catch 的情况下执行此操作的一种快速方法是将 XACAT_ABORT 设置为 ON;然后将脚本包装在开始并提交事务中。请记住,这可能无法捕获终止错误:

    http://msdn.microsoft.com/en-us/library/ms188792.aspx

    【讨论】:

      【解决方案4】:

      我最近发布了一个用于与支持事务的数据库交互的新模块:

      Install-Module -Name InvokeQuery
      

      如果发生错误,您无需显式回滚。使用 Start-Transaction 创建的环境事务会为您处理这些事务。

      try {
          $db = "test"
          Start-Transaction
      
          $sql = "insert into table1 values (NEWID(), 8765, 'transactions!', GETDATE())"
          $rowcount = $sql | Invoke-SqlServerQuery -Database $db -UseTransaction -CUD -Verbose
          Write-Host "Inserted $rowcount rows!"
      
          $sql = "insert into table1 values (NEWID(), 5555, 'transaction too!', GETDATE())"
          $rowcount = $sql | Invoke-SqlServerQuery -Database $db -UseTransaction -CUD -Verbose
          Write-Host "Inserted $rowcount rows!"
      
          Complete-Transaction
      }
      catch {
          ##Transaction will automatically be rolled back....
          Write-Error $_
      }
      

      如果要在事务中显式回滚,则抛出错误:

      throw 'rollback because of reason X!'
      

      更多示例: https://github.com/ctigeek/InvokeQueryPowershellModule/blob/master/README.md#transactions

      【讨论】:

      • 很遗憾,InvokeQuery 包含错误,不再维护。
      • @Monsignor 你是对的。我正在对 Powershell 7 进行彻底的重写。
      【解决方案5】:

      我建议查看Invoke-SqlCmd2,它采用现有 SqlConnection 的参数。

      安装:

      Install-Module Invoke-SqlCmd2 -Scope CurrentUser
      

      在事务中使用它看起来像这样:

      $Connection = [System.Data.SqlClient.SqlConnection]::new($ConnectionString)
      $Connection.Open()
      $Transaction = $Connection.BeginTransaction()
      
      try {
         Invoke-SqlCmd2 -SqlConnection $Connection -Query "..." -ErrorAction Stop
         Invoke-SqlCmd2 -SqlConnection $Connection -Query "..." -ErrorAction Stop
         $Transaction.Commit()
      }
      catch {
         $Transaction.Rollback()
      }
      finally {
         $Connection.Close()
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-06-09
        • 2021-09-25
        • 2016-01-15
        • 1970-01-01
        • 2017-12-21
        • 1970-01-01
        • 2013-05-15
        相关资源
        最近更新 更多