1

Webサービス呼び出しを介してアプリケーションからすべての「リクエスト履歴」ログを収集するオンデマンドで実行されるPowerShellプロセスがあります。結果のリクエストはオブジェクトにキャストされ、NoteProperty値(属性と値のペア)が1週間あたり大きなリスト配列(通常は1400アイテム)になります。

私がやりたかったのは、アプリケーションが自分でリクエストを削除しないように、これらのリクエストをすべて履歴目的で保存することです。したがって、新しく作成したデータベースにまだ存在していない各リクエストのすべての属性値ペアを格納する単純なテーブルをデータベースに作成しました。

次に、PowerShellスクリプトでMSSQLサーバーへのOleDB接続をセットアップし、テーブルからすべてのレコードを選択してDataTableに入力します(OleDBまたはDataTablesは苦手です)。その後、リスト配列の各項目をループして、DataTableにまだ存在していないことを検証します。存在しないレコードごとに、属性と値のペアを使用してDataTableに新しい行を追加します。そこから、コマンドビルダーがInsertステートメントを支援すると想定しているので、各属性値がnullまたは空白であるかどうかを確認したり、クエリを記述したりする必要はありません。次に、最後に、新しく追加されたDataTableを使用してOleDBAdapterを「更新」します。

このプロセスが機能している間、データベースからすべてのデータを取得し、リスト配列と比較して、新しく追加されたレコードを再コミットすることを実行していることに気付きました。データベースが大きいほど、これには時間がかかります。SQLステートメントを記述せずにこれを迅速かつ効率的に行う方法はありますか?CommandBuilderがDataTablesでどのように機能するかが気に入っています。

以下は、すべての「リクエスト履歴」オブジェクトがフェッチされた後にデータベースを更新するために呼び出される関数です。

function UpdateDatabase([Parameter(Mandatory=$true)] $allRequests)
{
    $objOleDbConnection = New-Object "System.Data.OleDb.OleDbConnection"
    $objOleDbCommand = New-Object "System.Data.OleDb.OleDbCommand"
    $objOleDbAdapter = New-Object "System.Data.OleDb.OleDbDataAdapter"
    $objDataTable = New-Object "System.Data.DataTable"

    $objOleDbConnection.ConnectionString = "Provider=SQLNCLI10;Server=SERVER;Database=DB1;Trusted_Connection=yes;"
    $objOleDbConnection.Open()

    $objOleDbCommand.Connection = $objOleDbConnection
    $objOleDbCommand.CommandText = "SELECT * FROM dbo.RequestLog"

    ##set the Adapter object and command builder
    $objOleDbAdapter.SelectCommand = $objOleDbCommand
    $objOleDbCommandBuilder = New-Object "System.Data.OleDb.OleDbCommandBuilder"
    $objOleDbCommandBuilder.DataAdapter = $objOleDbAdapter

    ##fill the objDataTable object with the results
    [void] $objOleDbAdapter.Fill($objDataTable)
    [void] $objOleDbAdapter.FillSchema($objDataTable,[System.Data.SchemaType]::Source)

    #store all the primary keys in a list for kicking out dups
    $sql_id = @()
    $objDataTable.Rows | foreach { $sql_id += $_.PKID}

    #####

    #loop through all the requests
    trap {
    "Error: $($i)"
    }
    $i = 0
    $total = $allRequests.count
    foreach ($request in $allRequests)
    {       
        $i++
        write-progress -activity "Filling DataTable" -status "% Complete: $($i/$total*100)" -PercentComplete ($i/$total*100)
        #check to see if entry already exists in our table (by primary key)
        if (!($sql_id -contains $request.PKID.Value))
        {
            #shouldn't have to do this but i noticed sometimes requests are duplicate in the list? (probably restarted the script and caught some old requests
            $sql_id += $request.PKID.Value

            $row = $objDataTable.Rows.Add($request.PKID.Value)
            #go through all the attributes from the request and add them to the table
            $list = get-member -in $request | Where-Object { $_.MemberType -eq "NoteProperty" }
            foreach ($attr in $list)
            {
                if ($request.($attr.name) -ne $null)
                {
                    $row.($attr.name) = $request.($attr.name)
                }
            }

        } else { 
            #PKID already in DB
        }

    }
    #update the database with our new records
    $objOleDbAdapter.Update($objDataTable)

    ## close the connection 
    $objOleDbConnection.Close()
}
4

1 に答える 1

1

プロセスをより効率的にするために、小さなT-SQLコードを作成する必要があります。SQL Serverで処理が行われるように、新しい行をSQL​​Serverに送信する必要があります。1つの解決策は、DataTableをSQLServerに渡すことができるテーブル値パラメーターを使用することです。私はここに例をブログに書きました:

http://sev17.com/2012/04/appending-new-rows-only/

于 2012-04-27T16:13:50.837 に答える