2017-02-19 17 views
0

XMLエクスポートをSQL Serverデータベースに渡す非常に簡単なコードを作成しました。すべてが機能し、データは必要に応じてテーブルに表示されます。PowerShell XML to SQL Server

しかし、私はリスト(約120k)から1つのレコードでテストしたところ、20秒ほどかかった。情報を渡すより良い方法でなければなりません。私はそれを行う方法を知っている人のために、以下のコードを添付しました。私のコードは非常にシンプルで、原則と必要な正しい方法で動作すると思います。

私は毎日XMLを生成するので、この手順をスピードアップすることが本当に重要です。私は前にそれをやったことがないし、コメント/リンクを学ぶことを熱望しているので、大歓迎です。誰の時間もありがとうございます。

$sqlserver="test" 
$db="test" 
$table="dbo.test" 
$conn = New-Object System.Data.SqlClient.SqlConnection 
$conn.ConnectionString = "Server=$sqlserver; Database=$db; Integrated Security = True;" 
$conn.Open() 


$cmd=$conn.CreateCommand() 
[xml]$xmllog="<logroot>$(get-content("I:\somefile.xml"))</logroot>" 
$idevent=$xmllog.logroot.Event.system.eventid[0] 
$levelcode=$xmllog.logroot.Event.system.level[0] 
$times=$xmllog.logroot.Event.system.timecreated.systemtime[0] 
$computers=$xmllog.logroot.Event.system.computer[0] 
$subUser=$xmllog.logroot.Event.selectsinglenode("//*[@Name='SubjectUserName']")[0].'#text' 
$subdomain=$xmllog.logroot.Event.selectsinglenode("//*[@Name='SubjectDomainName']")[0].'#text' 
$targUser=$xmllog.logroot.Event.selectsinglenode("//*[@Name='TargetUserName']")[0].'#text' 
$targetDom=$xmllog.logroot.Event.selectsinglenode("//*[@Name='TargetDomainName']")[0].'#text' 
$logontypes=$xmllog.logroot.Event.selectsinglenode("//*[@Name='LogonType']")[0].'#text' 
$logonproc=$xmllog.logroot.Event.selectsinglenode("//*[@Name='LogonProcessName']")[0].'#text' 
$workstation=$xmllog.logroot.Event.selectsinglenode("//*[@Name='WorkstationName']")[0].'#text' 
$ipaddress=$xmllog.logroot.Event.selectsinglenode("//*[@Name='IpAddress']")[0].'#text' 

$cmd.CommandText="insert dbo.test values (1, '$idevent', '$levelcode', '$times','$computers', '$subUser', '$subdomain','$targUser', '$targetDom', '$logontypes', '$logonproc', '$workstation','$ipaddress')" 

$cmd.ExecuteNonQuery() 
$conn.Close() 
+2

「INSERT」は20秒かかりますか、またはXMLファイルを解析していますか?あなたは、単一のxmlファイルに120K個のレコードがあると言っていますか?おそらくxmlスニペットが役に立ちます。 –

+0

ファイルの読み込みには5秒かかります。選択範囲はそれぞれ数秒です。INSERTはかなり高速です –

+0

xmlのスニペットを追加できますか? xml解析の量を減らし、パフォーマンスを向上させることは可能かもしれません。 –

答えて

1

あなたはプロパティにアクセスし、すべての120Kのノードに変数を設定するたびにSelectSingleNode()を実行して、あなただけのそれぞれの最初の結果を保持しています。あなたがすべきことは、最初のイベントノードを選び、その上でSelectSingleNode()などを実行することです。

Ex。

$event = $xmllog.logroot.Event[0] 

    $idevent=$event.system.eventid 
    $levelcode=$event.system.level 
    $times=$event.system.timecreated.systemtime 
    $computers=$event.system.computer 
    $subUser=$event.selectsinglenode("//*[@Name='SubjectUserName']").'#text' 
    #etc... 

私はあなたのようなXMLが-に見えるかわからないんだけど、私はこれを使用しテストするために:

$xmllog = [xml]@" 
<logroot> 
    <Event> 
     <System> 
      <Eventid>1</Eventid> 
      <Level>3</Level> 
      <Computer>Computer1</Computer> 
      <Timecreated systemtime="10:24" /> 
      <Attribute Name="SubjectUserName">User1</Attribute> 
      <Attribute Name="SubjectDomainName">DomainA</Attribute> 
      <Attribute Name="TargetUserName">User11</Attribute> 
      <Attribute Name="TargetDomainName">DomainB</Attribute> 
      <Attribute Name="LogonType">Windows</Attribute> 
      <Attribute Name="LogonProcessName">Winlogon.exe</Attribute> 
      <Attribute Name="WorkstationName">Computer1</Attribute> 
      <Attribute Name="IpAddress">192.168.1.10</Attribute> 
     </System> 
    </Event> 
     <Event> 
     <System> 
      <Eventid>1</Eventid> 
      <Level>3</Level> 
      <Computer>Computer2</Computer> 
      <Timecreated systemtime="10:24" /> 
      <Attribute Name="SubjectUserName">User2</Attribute> 
      <Attribute Name="SubjectDomainName">DomainA</Attribute> 
      <Attribute Name="TargetUserName">User21</Attribute> 
      <Attribute Name="TargetDomainName">DomainB</Attribute> 
      <Attribute Name="LogonType">Windows</Attribute> 
      <Attribute Name="LogonProcessName">Winlogon.exe</Attribute> 
      <Attribute Name="WorkstationName">Computer2</Attribute> 
      <Attribute Name="IpAddress">192.168.1.11</Attribute> 
     </System> 
    </Event> 
</logroot> 
"@ 
さらにそれをスピードアップするために、すべてのイベントを処理するとき、あなたはこのような何かを試みることができる

1つのINSERT文を使用して一括インポートします。あなたのバッチサイズの制限は何か分かりません。クエリが大きくなると、各クエリを分割することができます。各10k行。

$sqlserver="test" 
$db="test" 
$table="dbo.test" 
$conn = New-Object System.Data.SqlClient.SqlConnection 
$conn.ConnectionString = "Server=$sqlserver; Database=$db; Integrated Security = True;" 
$conn.Open() 

$cmd=$conn.CreateCommand() 
[xml]$xmllog="<logroot>$(get-content("I:\somefile.xml"))</logroot>" 

$rows = @() 

foreach ($event in $xmllog.logroot.Event) { 

    $idevent=$event.system.eventid 
    $levelcode=$event.system.level 
    $times=$event.system.timecreated.systemtime 
    $computers=$event.system.computer 
    $subUser=$event.selectsinglenode("//*[@Name='SubjectUserName']").'#text' 
    $subdomain=$event.selectsinglenode("//*[@Name='SubjectDomainName']").'#text' 
    $targUser=$event.selectsinglenode("//*[@Name='TargetUserName']").'#text' 
    $targetDom=$event.selectsinglenode("//*[@Name='TargetDomainName']").'#text' 
    $logontypes=$event.selectsinglenode("//*[@Name='LogonType']").'#text' 
    $logonproc=$event.selectsinglenode("//*[@Name='LogonProcessName']").'#text' 
    $workstation=$event.selectsinglenode("//*[@Name='WorkstationName']").'#text' 
    $ipaddress=$event.selectsinglenode("//*[@Name='IpAddress']").'#text' 

    $rows += "(1, '$idevent', '$levelcode', '$times','$computers', '$subUser', '$subdomain','$targUser', '$targetDom', '$logontypes', '$logonproc', '$workstation','$ipaddress')" 
} 

#Combine value-strings 
$values = $rows -join ", " 
$cmd.CommandText = "insert into $table values $values;" 

$cmd.ExecuteNonQuery() 
$conn.Close() 

CommandText

は、上記のサンプルXMLでこのようになります:

"insert into dbo.test values (1, '1', '3', '10:24','Computer1', 'User1', 'DomainA','User11', 'DomainB', 'Windows', 'Winlogon.exe', 'Computer1','192.168.1.10'), (1, '1', '3', '10:24','Computer2', 'User1', 'DomainA','User11', 'DomainB', 'Windows', 'Winlogon.exe', 'Computer1','192.168.1.10');" 

警告:あなたは本当に、入力データを信頼していない限り、パラメータ化クエリを使用することを検討してください。このクエリは、SQLインジェクションが発生しやすくなります。

+1

データをDataTableにロードし、個別の挿入ではなくSqlBulkCopyを使用する方が高速になります。 SqlBulkCopyは本質的にパラメータ化されており、大きなバッチの行をロードするために最適化されています。私は、SqlBulkCopyでは分単位で挿入するのではなく、120K行を秒単位で挿入することを期待しています。 –