Powershell脚本中的ExecuteReader()

时间:2015-05-21 13:42:37

标签: powershell executereader

我试图在PowerShell脚本中读取SQL表中的数据。我可以在reader对象中看到数据,但是当使用While (readerobject.read()){}读取它时,它不会进入循环内。

Powershell的

 $cmd = $sqlConn.CreateCommand()
 $cmd.CommandText ="SELECT * from user"
 $movedUserDetails = $cmd.ExecuteReader()
 while ($movedUserDetails.Read())
   {
      "[0] : " + $movedUserDetails.GetValue(0)
   }
 $movedUserDetails.Close() 

4 个答案:

答案 0 :(得分:7)

语法是正确的,但是你在循环中没有对值进行任何操作。你会想以某种方式坚持下去。下面是在PowerShell中运行一些基本SQL的示例,它有两种不同类型的命令(Text / SP)和两种不同的执行方法(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()

答案 1 :(得分:1)

我尝试了你的代码,但它确实有效。也许你可以试试SqlDataAdapter。我制作了这个Powershell模块来用SQL获取记录。它从未让我失望

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

答案 2 :(得分:1)

我有完全相同的问题,我相信原因如下(为我工作):

数据库连接并不总是正确关闭,例如在错误情况下。如果它没有关闭,它将跳过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语句,并确保连接正确关闭。

答案 3 :(得分:1)

首先,如果您只是使用SQL Server进行一些快速而肮脏的工作,或者正在运行基于文件的脚本,那么可以省去很多麻烦,只需使用Invoke-Sqlcmd。它是由非常聪明的人编写和维护的,因此很可能会为您服务。

如果您需要在短时间内运行大量查询,并且可以从重用连接中受益。还是希望参数化查询SqlConnectionSqlCommandSqlDataReader的安全性/完整性更有意义。

考虑到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只是为了向我们提供一致的字段顺序,而不必依赖有序哈希表,并且在运行类似{{1 }}。