PowerShellスクリプトでSQLテーブルからデータを読み取ろうとしています。リーダーオブジェクトでデータを確認できますが、While (readerobject.read()){}
を使用してデータを読み取ると、ループの内側に移動しません。
Powershell:
$cmd = $sqlConn.CreateCommand()
$cmd.CommandText ="SELECT * from user"
$movedUserDetails = $cmd.ExecuteReader()
while ($movedUserDetails.Read())
{
"[0] : " + $movedUserDetails.GetValue(0)
}
$movedUserDetails.Close()
構文は正しいですが、ループ内で値を使用して何もしていません。どういうわけかそれを永続化したいと思うでしょう。 Powershell内でいくつかの基本的なSQLを実行する例を以下に示します。2種類のコマンド(Text/SP)と2種類の実行方法(DataAdapter/DataReader)があります。どちらでも問題なく機能するはずです。
# config
$svr = "serverName"
$db = "databaseName"
# connection
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection
$sqlConnection.ConnectionString = "Server=$svr;Database=$db;Integrated Security=True"
$sqlConnection.Open()
# command A - text
$sqlCmd = New-Object System.Data.SqlClient.SqlCommand
$sqlCmd.Connection = $sqlConnection
$sqlCmd.CommandText = "SELECT name AS TABLE_NAME FROM sys.tables"
# command B - stored procedure
$sqlCmd = New-Object System.Data.SqlClient.SqlCommand
$sqlCmd.Connection = $sqlConnection
$sqlCmd.CommandText = "sys.sp_tables"
$sqlCmd.CommandType = [System.Data.CommandType]::StoredProcedure
$sqlCmd.Parameters.Add("@table_owner", "dbo")
# execute A - data reader
$reader = $sqlCmd.ExecuteReader()
$tables = @()
while ($reader.Read()) {
$tables += $reader["TABLE_NAME"]
}
$reader.Close()
# execute B - data adapter
$dataTable = New-Object System.Data.DataTable
$sqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$sqlAdapter.SelectCommand = $sqlCmd
$sqlAdapter.Fill($dataTable)
$sqlConnection.Close()
私はまったく同じ問題を抱えていましたが、その理由は次のとおりだと思います(私のために働いた):
エラーの場合など、データベース接続は常に正しく閉じられているとは限りません。閉じていない場合は、whileループをスキップします。コードを次のように変更します。
$sqlConn.Open()
$cmd = $sqlConn.CreateCommand()
$cmd.CommandText ="SELECT * from user"
$movedUserDetails = $cmd.ExecuteReader()
try
{
while ($movedUserDetails.Read())
{
"[0] : " + $movedUserDetails.GetValue(0)
}
}
catch
{
#log error
}
finally
{
$sqlConn.Close()
}
Finallyステートメントは常に実行され、接続が適切に閉じられていることを保証します。
私はあなたのコードを試してみましたが、うまくいきました。おそらく、SqlDataAdapter
で試すことができます。 SQLでレコードをフェッチするためにこのPowershellモジュールを作成しました。それは私を失敗したことはありません
function Invoke-SqlSelect
{
[CmdletBinding()]
Param
(
[ValidateNotNullOrEmpty()]
[Parameter(ValueFromPipeline=$True,Mandatory=$True)]
[string] $SqlServer,
[Parameter(ValueFromPipeline=$True,Mandatory=$False)]
[string] $Database = "master",
[ValidateNotNullOrEmpty()]
[Parameter(ValueFromPipeline=$True,Mandatory=$True)]
[string] $SqlStatement
)
$ErrorActionPreference = "Stop"
$sqlConnection = New-Object System.Data.SqlClient.SqlConnection
$sqlConnection.ConnectionString = "Server=$SqlServer;Database=$Database;Integrated Security=True"
$sqlCmd = New-Object System.Data.SqlClient.SqlCommand
$sqlCmd.CommandText = $SqlStatement
$sqlCmd.Connection = $sqlConnection
$sqlAdapter = New-Object System.Data.SqlClient.SqlDataAdapter
$sqlAdapter.SelectCommand = $sqlCmd
$dataTable = New-Object System.Data.DataTable
try
{
$sqlConnection.Open()
$sqlOutput = $sqlAdapter.Fill($dataTable)
Write-Output -Verbose $sqlOutput
$sqlConnection.Close()
$sqlConnection.Dispose()
}
catch
{
Write-Output -Verbose "Error executing SQL on database [$Database] on server [$SqlServer]. Statement: `r`n$SqlStatement"
return $null
}
if ($dataTable) { return ,$dataTable } else { return $null }
}
まず、SQL Serverですばやく簡単な作業をしている場合や、ファイルベースのスクリプトを実行している場合は、手間を省いて Invoke-Sqlcmd
を使用します。それは本当に賢い人によって書かれ、維持されているので、あなたに役立つでしょう。
短期間に多くのクエリを実行する必要があり、接続を再利用するメリットがある場合。または、パラメータ化されたクエリの安全性/完全性が必要な場合は、SqlConnection
、SqlCommand
およびSqlDataReader
の方が意味があります。
PowerShellはパイプライン指向の構造であることを念頭に置いて、パイプラインの観点から考え、それを効果的に活用する必要があります。つまり、すべてのレコードをDataTable
にダンプして、下流で再び繰り返すだけでなく、動的な性質のPowerShellを利用して「コールバック」(つまり[ScriptBlock]
)を渡して操作を実行してみませんかIDataRecord
を反復するときに、各IDataReader
で。
次の関数Invoke-SqlCommand
には、接続文字列、クエリ、およびコールバックが必要です。これらは、行の投影/分析などに使用できます。
注:永続化された
SqlConnection
が必要な場合は、$ConnectionString
パラメータを$Connection
に置き換えるだけです。
function Invoke-SqlCommand {
param(
[Parameter(Mandatory=$True,
ValueFromPipeline=$True,
ValueFromPipelineByPropertyName=$True,
HelpMessage="The connection string.")]
[string] $ConnectionString,
[Parameter(Mandatory=$True,
HelpMessage="The query to run.")]
[string] $Query,
[Parameter(Mandatory=$True,
HelpMessage="The work to perform against each IDataRecord.")]
[scriptblock] $ScriptBlock
)
$conn = New-Object System.Data.SqlClient.SqlConnection
$conn.ConnectionString = $ConnectionString
$cmd = $conn.CreateCommand()
$cmd.CommandText = $Query
try {
$conn.Open()
$rd = $cmd.ExecuteReader()
while($rd.Read()){
Write-Output (Invoke-Command $ScriptBlock -ArgumentList $rd)
}
}
finally {
$conn.Close()
}
}
catch {...}
を指定せずに本番環境で使用しないでください。簡潔にするためにここでは省略しています。
この形式では、各IDataRecord
に対していくつかの操作と投影を実行し、それをダウンストリーム処理のためにパイプラインに渡すことができます。
$connectionString = "your connection string"
$query = "SELECT * FROM users"
Invoke-SqlCommand $connectionString $query {
param(
[Parameter(Mandatory=$True)]
[System.Data.SqlClient.SqlDataReader]$rd)
$obj = New-Object -TypeName PSObject -Property @{ user_id = $rd.GetValue($rd.GetOrdinal("geoname_id"))}
$obj.psobject.typenames.insert(0,'MyAwesome.Object')
Write-Output $obj
}
ここでのNew-Object
の使用は、順序付けられたハッシュテーブルに依存することなく、一貫したフィールドの順序付けを提供するだけであり、Get-Member
のようなものを実行するときにカスタムPSObject
をより簡単に識別するのに役立ちます。