ExecuteReader() in Powershell script ExecuteReader() in Powershell script powershell powershell

ExecuteReader() in Powershell script


The syntax is correct, but you're not doing anything with the value once inside the loop. You'll want to persist it somehow. Here's an example of running some basic SQL inside of powershell, with two different types of commands (Text/SP) and two different methods of execution (DataAdapter/DataReader). Either one of each should work fine.

# 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()


First off, if you're just doing some quick & dirty work with SQL Server or running file-based scripts, save yourself a ton of trouble and just use Invoke-Sqlcmd. It's written and maintained by really smart people, so will likely serve you well.

If you're needing to run a lot of queries in a short period and could benefit from reusing your connection. Or want the safety/integrity of parameterized queries, SqlConnection, SqlCommand and SqlDataReader make more sense.

Bearing in mind that PowerShell is a pipeline oriented construct, it behooves us to think in terms of the pipeline and leverage it effectively. That said, rather than dump all of the records into a DataTable only to iterate them again downstream, why not leverage the dynamic nature PowerShell and pass a "callback" (i.e. [ScriptBlock]) to perform some operation on each IDataRecord as you iterate the IDataReader.

The following function Invoke-SqlCommand requires a: Connection String, Query and Callback which can be used for row projection/analysis etc.

Note: If a persisted SqlConnection is required, simply replace the $ConnectionString parameter with $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()  }}

Please do not use this in production without specifying a catch {...}, omitted here for brevity.

This format affords you the opportunity to perform some operation and projection against each IDataRecord AND yield it into the pipeline for downstream processing.

$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}

The use of New-Object here is simply to provide us with consistent field ordering without having to rely on an ordered hash table and helps us identify our custom PSObject more easily when running things like Get-Member.


I had exactly the same issue and I believe the reason is the following(worked for me):

The database connection is not always closed correct e.g in an error case. If it is not closed it will skip the while loop. Change your code to the following:

     $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()      }

The finally statement is always executed and will secure that the connection is properly closed.