在Powershell中使用不同的sql源和dest连接指定插入到列表中

时间:2017-10-26 18:32:59

标签: sql-server powershell

我有这个powershell脚本,如果我的DEST表只有从我的SOURCE服务器中选择列中列出的列,但是DEST表有更多,那么它将起作用。我无法找到任何提供如何指定我要插入的dest表中的列的示例的内容。请注意,SourceServer和DestServer不是链接服务器。

Param ( 
  #[parameter(Mandatory = $true)] 
  [string] $SrcServer = "SourceServer", 
  [parameter(Mandatory = $true)] 
  [string] $SrcDatabase = "SourceDb", 
  #[parameter(Mandatory = $true)] 
  [string] $SrcTable = "stage.InternalNotes",
  #[parameter(Mandatory = $true)] 
  [string] $DestServer = "DestServer", 
  #[parameter(Mandatory = $true)] 
  [string] $DestDatabase = "DestDb", 
  [parameter(Mandatory = $true)] 
  [string] $DestTable = "dbo.InternalNotes", 
)

Function ConnectionString([string] $ServerName, [string] $DbName)
{
  "Data Source=$ServerName;Initial Catalog=$DbName;Integrated Security=True;User ID=$UID;Password=$PWD;"
}

$SrcConnStr = ConnectionString $SrcServer $SrcDatabase 
$SrcConn = New-Object System.Data.SqlClient.SQLConnection($SrcConnStr) 
$CmdText = "SELECT
   ino.UserId
  ,ino.StoreId
  ,ino.PostedById
  ,ino.DatePosted
  ,ino.NoteSubject
  ,ino.NoteText
  ,ino.NoteType
  ,ino.Classify
  ,ino.CreatedBy
  ,ino.CreatedUtc
  ,IsReadOnly = 0
FROM
   stage.InternalNotes AS ino
"  
$SqlCommand = New-Object system.Data.SqlClient.SqlCommand($CmdText, $SrcConn) 
$SrcConn.Open() 
[System.Data.SqlClient.SqlDataReader] $SqlReader = $SqlCommand.ExecuteReader() 

Try 
{ 
  $DestConnStr = ConnectionString $DestServer $DestDatabase 
  $bulkCopy = New-Object Data.SqlClient.SqlBulkCopy($DestConnStr, [System.Data.SqlClient.SqlBulkCopyOptions]::KeepIdentity) 
  $bulkCopy.DestinationTableName = $DestTable 
  $bulkCopy.WriteToServer($sqlReader)
} 
Catch [System.Exception] 
{ 
  $ex = $_.Exception 
  Write-Host $ex.Message 
} 
Finally
{ 
  Write-Host "Table $SrcTable in $SrcDatabase database on $SrcServer has been copied to table $DestTable in $DestDatabase database on $DestServer" 
  $SqlReader.close() 
  $SrcConn.Close() 
  $SrcConn.Dispose() 
  $bulkCopy.Close() 
} 

基本上,我需要能够做到这一点:

INSERT INTO dbo.InternalNotes --DEST Server table
    (
     userID
    ,StoreID
    ,PostedByID
    ,DatePosted
    ,NoteSubject
    ,NoteText
    ,NoteType
    ,Classify
    ,CreatedBy
    ,CreatedDateUTC
    ,IsReadOnly
    )
SELECT
    ino.UserId
   ,ino.StoreId
   ,ino.PostedById
   ,ino.DatePosted
   ,ino.NoteSubject
   ,ino.NoteText
   ,ino.NoteType
   ,ino.Classify
   ,ino.CreatedBy
   ,ino.CreatedUtc
   ,IsReadOnly = 0
FROM
    stage.InternalNotes AS ino --SOURCE Server table

根据接受的答案让所有内容工作后进行编辑:

出于某种原因,它不喜欢这条线:

$bulkCopy = New-Object -TypeName Data.SqlClient.SqlBulkCopy -ArgumentList $DestSqlConnection, [System.Data.SqlClient.SqlBulkCopyOptions]::KeepIdentity, $DestSqlTransaction;

它给出了错误:

  

无法转换参数“1”,其值为:   “[System.Data.SqlClient.SqlBulkCopyOptions] :: KeepIdentity”,for   “SqlBulkCopy”键入“System.Data.SqlClient.SqlBulkCopyOptions”:   “无法转换价值   键入“[System.Data.SqlClient.SqlBulkCopyOptions] :: KeepIdentity”   “System.Data.SqlClient.SqlBulkCopyOptions”。错误:“无法匹配   标识符名称   [System.Data.SqlClient.SqlBulkCopyOptions] :: KeepIdentity为有效   枚举器名称。指定以下枚举器名称之一并尝试   再次:默认,KeepIdentity,CheckConstraints,TableLock,KeepNulls,   FireTriggers,UseInternalTransaction,   AllowEncryptedValueModifications “”

所以相反我把它改成了这一切,一切正常:

$bulkCopy = New-Object Data.SqlClient.SqlBulkCopy($DestSqlConnection, [System.Data.SqlClient.SqlBulkCopyOptions]::KeepIdentity,$DestSqlTransaction) 

1 个答案:

答案 0 :(得分:1)

进行手动列映射you need to populate SqlBulkCopy.ColumnMappings。如果您没有指定映射,那么据我所知SqlBulkCopy将假设选择列表中的第一列或DataRow进入目标表的第一个序列列。

例如:

$bulkCopy.DestinationTableName = $DestTable;
$bulkCopy.ColumnMappings.Add('sourceColumn1','destinationColumn1');
$bulkCopy.ColumnMappings.Add('sourceColumn2','destinationColumn2');
$bulkCopy.ColumnMappings.Add('sourceColumn3','destinationColumn3');
$bulkCopy.ColumnMappings.Add('sourceColumn4','destinationColumn4');
$bulkCopy.ColumnMappings.Add('sourceColumn5','destinationColumn5');

但是,您的脚本存在许多其他问题。

您的连接字符串身份验证部分是无意义的:

`Integrated Security=True; User ID=$UID; Password=$PWD;`

Integrated Security=True说,"对当前登录的用户使用直通Windows身份验证。" User ID=$UID; Password=$PWD;说,"使用指定用户名和密码的SQL身份验证。"你不能做到这两点。  您应该只指定一个或另一个。

$SqlCommand = New-Object system.Data.SqlClient.SqlCommand($CmdText, $SrcConn)
[...]
$bulkCopy = New-Object Data.SqlClient.SqlBulkCopy($DestConnStr, [System.Data.SqlClient.SqlBulkCopyOptions]::KeepIdentity) 

我可能错了,但我很确定你在这里尝试将两个变量作为一个参数传递。与您的ConnectionString功能一样,我也不认为您不想在这里使用括号。无论如何,它在语法上都令人困惑。这样做:

$SqlCommand = New-Object -TypeName System.Data.SqlClient.SqlCommand -ArgumentList $CmdText, $SrcConn
[...]
$bulkCopy = New-Object -TypeName Data.SqlClient.SqlBulkCopy -ArgumentList $DestConnStr, [System.Data.SqlClient.SqlBulkCopyOptions]::KeepIdentity

说到最后一个,我还有另外一个问题。 SqlBulkCopy是强大的,但你真的必须抓住它。默认情况下,SqlBulkCopy不会运行任何事务优势。这意味着如果它在中间错误,那么太糟糕,您的数据已经部分更新。您可以启用内部事务,但只会回滚最新批量的插入。您确实需要管理自己的事务以获得全有或全无的结果。

所以你最终得到这样的东西:

Try { 
    $DestConnStr = ConnectionString $DestServer $DestDatabase

    # We have to open the connection before we can create the transaction
    $DestSqlConnection = New-Object -TypeName System.Data.SqlClient.SqlConnection -ArgumentList $DestConnStr;
    $DestSqlConnection.Open();
    $DestSqlTransaction = $DestSqlConnection.BeginTransaction();
    $bulkCopy = New-Object -TypeName Data.SqlClient.SqlBulkCopy -ArgumentList $DestSqlConnection, [System.Data.SqlClient.SqlBulkCopyOptions]::KeepIdentity, $DestSqlTransaction;
    $bulkCopy.DestinationTableName = $DestTable
    $bulkCopy.ColumnMappings.Add('sourceColumn1','destinationColumn1');
    $bulkCopy.ColumnMappings.Add('sourceColumn2','destinationColumn2');
    $bulkCopy.ColumnMappings.Add('sourceColumn3','destinationColumn3');
    $bulkCopy.ColumnMappings.Add('sourceColumn4','destinationColumn4');
    $bulkCopy.ColumnMappings.Add('sourceColumn5','destinationColumn5');

    Try {
        $bulkCopy.WriteToServer($sqlReader)
        # Commit on success
        $DestSqlTransaction.Commit();
    }
    Catch {
        # Rollback on error
        $DestSqlTransaction.Rollback();
        # Rethrow the error to the outer catch block
        throw ($_);
    }
} 
Catch [System.Exception] { 
    $ex = $_.Exception 
    Write-Host $ex.Message 
} 
Finally { 
    [...]
}

我可能会重写上面的内容,因为我不喜欢嵌套的try块,但是对于快速而脏的重写,这将起作用。我不认为你会遇到分布式交易问题的任何问题,但我可能错了。当我需要这种数据泵时,我倾向于使用SSIS或链接服务器。