确保所有invoke-sqlcmd 命令都发生在同一个事务中?

Posted

技术标签:

【中文标题】确保所有invoke-sqlcmd 命令都发生在同一个事务中?【英文标题】:ensuring that all invoke-sqlcmd commands happen within the same transaction? 【发布时间】:2021-12-13 19:06:14 【问题描述】:

我们如何确保在 1 个数据库事务中执行以下函数?

#
# Get SQL Files recursively
#
function GetFiles($path = $pwd) 
 
    $subFolders = Get-ChildItem -Path $path -Directory  | Select-Object FullName,Name | Sort-Object -Property Name 
    $sqlFiles = Get-ChildItem -Path $path -Filter *.sql | Select-Object FullName,Name | Sort-Object -Property Name
    
    foreach ($file in $sqlFiles)
    
      Write-Host "file: " $file.Name
      Invoke-Sqlcmd -ServerInstance $ServerInstance -Database $DBName -Username $SvcAdminAccount -Password $SvcAdminPassword -InputFile $file.FullName -QueryTimeout 65535 -ea stop
     

    foreach ($folder in $subFolders)
    
       Write-Host "`nGetting files for subfolder: " $folder.Name
       GetFiles $folder.FullName
     

这个函数遍历一个目录并且会为每个文件调用-sqlcmd。 我们如何保证整个函数在一个sql事务中执行>?

【问题讨论】:

您需要创建一个大批次以通过invoke-sqlcmd 传递。您可能最好使用 ForEach 创建 SQL 文件,并且必须确保在批处理开始时启用 XACT_ABORT 并声明事务,这样如果任何语句失败,一切都会回滚. 每个Invoke-Sqlcmd 都是与数据库的单独会话。事务不能跨越会话。正如@Larnu 建议的那样,如果您在 SQL Server 上,请创建一个带有适当放置的 BEGIN TRANSACTION;END TRANSACTION; 语句的 .sql 脚本。 或者你可以使用SqlCommandSqlConnection,在那里你可以使用BeginTransaction 【参考方案1】:

我们可以利用标准的 ADO.Net 类 SqlConnectionSqlCommand

最好将这些放在try/finally 块中,为此我使用了Using-Object 函数from this answer

$sqlFiles = Get-ChildItem -Path $path -Filter *.sql | Select-Object FullName,Name | Sort-Object -Property Name;

$connString = "Server=YourServer;Database=$DBName;User Id=$SvcAdminAccount;Password=$SvcAdminPassword";

Using-Object ($conn = New-Object System.Data.SqlClient.SqlConnection $connString) 
    $conn.Open();
    
    Using-Object ($tran = $conn.BeginTransaction()) 
        foreach ($file in $sqlFiles)
        
          Write-Host "file: " $file.Name
          $comm = New-Object System.Data.SqlClient.SqlCommand $file,$conn,$tran;
          $comm.ExecuteNonQuery();
        
        $tran.Commit();
    ;
;
function Using-Object

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [AllowEmptyString()]
        [AllowEmptyCollection()]
        [AllowNull()]
        [Object]
        $InputObject,

        [Parameter(Mandatory = $true)]
        [scriptblock]
        $ScriptBlock
    )

    try
    
        . $ScriptBlock
    
    finally
    
        if ($null -ne $InputObject -and $InputObject -is [System.IDisposable])
        
            $InputObject.Dispose()
        
    

【讨论】:

New-Object : 找不到“SqlCommand”的重载和参数计数:“3”。 抱歉现在应该修复

以上是关于确保所有invoke-sqlcmd 命令都发生在同一个事务中?的主要内容,如果未能解决你的问题,请参考以下文章

Powershell运行Invoke-Sqlcmd命令的先决条件

Invoke-SqlCmd 缩短结果

PowerShell 中对 Invoke-Sqlcmd 的 Unicode 支持

Powershell invoke-sqlcmd 打印错误的输出

如何使用 Invoke-Sqlcmd 连接到 SQL Server LocalDB?

Invoke-sqlcmd 等待恢复完成,然后再进行下一个任务