Может ли Powershell Receive-Job вернуть набор данных?
Справочная информация:
У меня есть приложение, которое устанавливает несколько SQL-соединений с несколькими базами данных, что в настоящее время занимает очень очень много времени для выполнения.
Powershell (.NET) будет ожидать завершения каждой последующей функции "SQL-GET", прежде чем она сможет запустить следующую. У меня сложилось впечатление, что я могу значительно ускорить это приложение, запустив каждую функцию "SQL-GET" одновременно в их фоновом задании! Затем я получу данные из каждого задания, когда они завершатся. В идеале как системный объект DataSet.
Проблемы:
При извлечении данных из фонового задания я могу ТОЛЬКО вернуть объект System.Array обратно. То, что я на самом деле после, это объект System.DataSet. Это необходимо, потому что вся логика в приложении зависит от объекта DataSet.
Код:
Вот фрагмент кода v.simple, который создаст соединение SQL и заполнит вновь созданный объект набора данных возвращенными результатами. Работает угощение. $ Results - это объект DataSet, и я могу прекрасно с этим справиться.
$query = "SELECT * FROM [database]..[table] WHERE column = '123456'"
$Connection = New-Object System.Data.SqlClient.SQLConnection
$ConnectionString = "Server='SERVER';Database='DATABASE';User ID='SQL_USER';Password='SQL_PASSWORD'"
$Connection.ConnectionString = $ConnectionString
$Connection.Open()
$Command = New-Object system.Data.SqlClient.SqlCommand($Query,$Connection)
$Adapter = New-Object system.Data.SqlClient.SqlDataAdapter
$Adapter.SelectCommand = $Command
$Connection.Close()
[System.Data.SqlClient.SqlConnection]::ClearAllPools()
$results = New-Object system.Data.DataSet
[void]$Adapter.fill($results)
$results.Tables[0]
И вот этот ОЧЕНЬ ЖЕ КОД, включенный в параметр scriptblock нового фонового задания. Только после вызова Receive-Job я получаю массив, а не набор данных.
$test_job = Start-Job -ScriptBlock {
$query = "SELECT * FROM [database]..[table] WHERE column = '123456'"
$Connection = New-Object System.Data.SqlClient.SQLConnection
$ConnectionString = "Server='SERVER';Database='DATABASE';User ID='SQL_USER';Password='SQL_PASSWORD'"
$Connection.ConnectionString = $ConnectionString
$Connection.Open()
$Command = New-Object system.Data.SqlClient.SqlCommand($Query,$Connection)
$Adapter = New-Object system.Data.SqlClient.SqlDataAdapter
$Adapter.SelectCommand = $Command
$Connection.Close()
[System.Data.SqlClient.SqlConnection]::ClearAllPools()
$results = New-Object system.Data.DataSet
[void]$Adapter.fill($results)
return $results.Tables[0]
}
Wait-Job $test_job
$ret_results = Receive-Job $test_job
Любая помощь будет принята с благодарностью!!!
Исследование пока что:
Я работал со старым Google, но все посты, блоги и статьи, с которыми я сталкиваюсь, кажется, проникают в ЧРЕЗВЫЧАЙНУЮ глубину об управлении рабочими местами и всеми прибамбасами вокруг этого. Является ли базовая природа powershell, чтобы ТОЛЬКО возвращать массив через командлет receive-job?
Я прочитал сообщение стека о выражении возврата. Думал, что я был к чему-то. Покушение:
return $results.Tables[0]
return ,$results.Tables[0]
return ,$results
Все еще возвращают массив.
Я видел, как довольно громоздко люди вручную преобразовывали массив обратно в объект набора данных - хотя это кажется очень "грязным" - я педантичен и живу надеждой, что у этого магического объекта набора данных должен быть способ пройти через фоновую работу и в мою текущую сессию!:)
Повторить:
По сути, все, что я хотел бы, - это чтобы объект $ret_results был извлечен из командлета Receive-Job в качестве DataSet... или даже DataTable. Я возьму либо... просто не массив:)
2 ответа
В PowerShell для набора из нескольких объектов произвольного типа характерно возвращение в коллекцию. Рассмотрим этот измененный пример, где я строю свою собственную таблицу:
PS C:\> $job = Start-Job -ScriptBlock {
>>
>> $table = New-Object system.Data.DataTable “MyTable”
>>
>> $col1 = New-Object system.Data.DataColumn MyFirstCol,([string])
>> $col2 = New-Object system.Data.DataColumn MyIntCol,([int])
>>
>> $table.columns.add($col1)
>> $table.columns.add($col2)
>>
>> $row1 = $table.NewRow()
>> $row1.MyFirstCol = "FirstRow"
>> $row1.MyIntCol = 1
>> $row2 = $table.NewRow()
>> $row2.MyFirstCol = "SecondRow"
>> $row2.MyIntCol = 2
>>
>> $table.Rows.Add($row1)
>> $table.Rows.Add($row2)
>>
>> $dataSet = New-Object system.Data.DataSet
>> $dataSet.Tables.Add($table)
>>
>> $dataSet.Tables[0]
>>
>> }
>>
PS C:\> $output = Receive-Job -Job $job
Выход получен. Итак, что мы получили?
PS C:\> $output.GetType()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True True Object[] System.Array
Массив, как вы описали. Но это весь объект. Что если мы проанализируем его членов по отдельности, отправив их Get-Member
?
PS C:\> $output | gm
TypeName: Deserialized.System.Data.DataRow
Name MemberType Definition
---- ---------- ----------
ToString Method string ToString(), string ToString(string format, System.IFormatProvider formatProvi...
PSComputerName NoteProperty System.String PSComputerName=localhost
PSShowComputerName NoteProperty System.Boolean PSShowComputerName=False
RunspaceId NoteProperty System.Guid RunspaceId=186c51c3-d3a5-404c-9a4a-8ff3d3a7f024
MyFirstCol Property System.String {get;set;}
MyIntCol Property System.Int32 {get;set;}
PS C:\> $output
RunspaceId : 186c51c3-d3a5-404c-9a4a-8ff3d3a7f024
MyFirstCol : FirstRow
MyIntCol : 1
RunspaceId : 186c51c3-d3a5-404c-9a4a-8ff3d3a7f024
MyFirstCol : SecondRow
MyIntCol : 2
Учтите следующее:
В вашей работе вы указали, что
$results.Tables[0]
должен быть возвращен. Указывая конкретную итерацию таблиц, вы возвращаете объект, который описывает эту таблицу... возможно, DataTable или, в данном случае, DataRows... вместо DataSet, как вы, похоже, ожидаете?DataTables имеют строки. Если DataTable имеет более одной строки, powershell вернет его в коллекции DataRows, как я продемонстрировал выше. Вы можете быть удивлены, узнав, что это не относится к возврату одной строки - он будет возвращать только один объект DataRow вместо коллекции объектов DataRow.
Если это действительно тот результат, который вы ожидаете, вы можете заставить его всегда возвращаться в коллекцию, указав результат как
@($results.Tables[0])
, Таким образом, вы всегда знаете, что ожидаете коллекцию, и можете соответствующим образом обработать полученное содержимое (перебирая коллекцию для управления отдельными объектами).
Когда вы запускаете Start-Job, он в значительной степени генерирует новую оболочку / область видимости. Я предполагаю, что единственный способ обмена данными между различными оболочками - это использование некоторого текстового формата (например, XML/JSON), поэтому выходные данные дочернего задания будут преобразованы в этот "XML", а родительское задание преобразует его обратно в PSObject или Array. Поэтому вам нужно проделать некоторую работу, чтобы получить исходный объект.Net в родительской работе. Я работал над аналогичным проектом, вытащил таблицы данных из 5 баз данных и затем скопировал их в одну таблицу отчетности. В качестве окончательного решения я решил выполнить массовую копию в дочерних заданиях (потому что SQL-сервер поддерживает параллельную BC для неиндексированных таблиц). Если бы мне нужно было сделать это из родительской работы, я бы преобразовал PSObject в datatable с помощью командлета Out-DataTable