2017-02-19 8 views
0

Я написал очень простой код для передачи моего экспорта XML в базу данных SQL Server. Все работает, и данные отображаются в таблице так, как должны.PowerShell XML to SQL Server

Однако, когда я тестировал одну запись из списка (около 120 тыс. Из них), мне потребовалось около 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-файла? Вы говорите, что у вас есть 120K отдельных записей в одном XML-файле? Возможно, поможет xml-фрагмент. –

+0

Загрузка файла занимает 5 секунд, selectsinglenodes - это несколько секунд, когда INSERT довольно быстро –

+0

Можете ли вы добавить фрагмент xml? Возможно, будет уменьшено количество разборок xml и улучшена производительность. –

ответ

1

Вы доступ к свойствам и выполнение SelectSingleNode() на каждом 120K узлов каждый раз, когда вы переменный, а затем вы просто держать первый результат для каждого. Что вы должны сделать, это выбрать первый event-узел и запустить на нем 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> 
"@ 

Для дальнейшего ускорения его при обработке всех событий, вы можете попробовать что-то вроде этого, чтобы пакетный импорт с использованием одного оператора INSERT. Я не уверен, что такое ограничение размера партии. Если запрос становится большим, вы можете разделить его на каждый ex. каждая строка 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 по сути параметризуется и оптимизируется для загрузки больших партий строк. Я ожидал бы, что 120K строк вставлены, чтобы занять секунды с SqlBulkCopy, а не минуты с отдельными вставками. –