2017-02-19 2 views
0

Ich habe einen sehr einfachen Code erstellt, um meinen XML-Export an eine SQL Server-Datenbank zu übergeben. Alles funktioniert und die Daten erscheinen in der Tabelle wie es sollte.PowerShell XML zu SQL Server

Jedoch, wenn ich mit einem einzelnen Datensatz aus der Liste (etwa 120k von ihnen) getestet und es hat mich um 20sec. Es muss ein besserer Weg sein, um Informationen weiterzugeben. Ich habe den Code unten für Leute angebracht, die wissen, wie es geht. Ich denke, mein Code ist sehr einfach und funktioniert mit den Prinzipien und nicht notwendig, den richtigen Weg.

Da ich eine XML jeden Tag produzieren, ist es wirklich wichtig, dass ich dieses Verfahren bis zu beschleunigen. Wie ich es noch nie zuvor getan habe und gerne irgendwelche Kommentare/Links erfahren möchte sind sehr willkommen. Danke für die Zeit im Voraus.

$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

Ist es die 'INSERT', die 20 Sekunden dauert oder die XML-Datei analysiert? Willst du sagen, dass du 120K separate Datensätze in der einzelnen XML-Datei hast? Vielleicht hilft ein XML-Snippet. –

+0

Die Datei lädt dauert 5 Sekunden, die selectsinglenodes sind eacha einige Sekunden die INSERT ist ziemlich schnell –

+0

Können Sie ein Snippet der XML hinzufügen? Es ist möglicherweise möglich, die Menge der XML-Analyse zu reduzieren und die Leistung zu verbessern. –

Antwort

1

Sie den Zugriff auf die Eigenschaften und die Ausführung SelectSingleNode() auf jedem 120k Knoten jedes Mal, wenn Sie eine Variable gesetzt, und dann halten Sie nur das erste Ergebnis für jeden. Was Sie tun sollten, ist den ersten Event-Knoten zu wählen und SelectSingleNode() usw. darauf laufen zu lassen.

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... 

Ich bin nicht sicher, was Ihre xml-aussieht, aber ich diese verwendet zu testen:

$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> 
"@ 

weiter um ihn zu beschleunigen, wenn alle Ereignisse verarbeiten, könnten Sie so etwas wie dies versuchen, Batch-Import mit einer einzigen INSERT-Anweisung. Ich bin mir nicht sicher, wie groß Ihre Chargengröße ist. Wenn die Abfrage zu groß wird, können Sie sie für jede ex aufteilen. jede 10k Reihe.

$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 würde oben mit der Probe xml so aussehen:

"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');" 

Achtung: Betrachten Sie parametrisierte Abfragen verwenden, wenn Sie wirklich die Eingangsdaten vertrauen. Diese Abfrage ist anfällig für SQL-Injektion.

+1

Es wird schneller sein, die Daten in eine DataTable zu laden und SqlBulkCopy anstelle einzelner Einfügungen zu verwenden. SqlBulkCopy ist inhärent parametrisiert und optimiert für das Laden großer Reihen von Zeilen. Ich würde erwarten, dass 120.000 Zeilen mit SqlBulkCopy in Sekunden statt mit Minuten mit separaten Einfügungen eingefügt werden. –