12

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

电源外壳

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

4 回答 4

14

语法是正确的,但是一旦进入循环,您就不会对值做任何事情。你会想以某种方式坚持下去。这是在 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()
于 2016-01-15T16:05:49.533 回答
3

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

如果您需要在短时间内运行大量查询并且可以从重用连接中受益。或者想要参数化查询的安全性/完整性SqlConnectionSqlCommand并且SqlDataReader更有意义。

记住 PowerShell 是一个面向管道的结构,我们应该从管道的角度来思考并有效地利用它。也就是说,与其将所有记录转储到 aDataTable中以再次在下游迭代它们,为什么不利用动态特性 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()
  }
}

请不要在没有指定 a 的情况下在生产中使用它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只是为我们提供一致的字段顺序,而不必依赖有序的哈希表,并帮助我们PSObject在运行诸如Get-Member.

于 2019-05-16T19:24:34.717 回答
2

下面的答案是一个但过时的答案。您现在应该使用Invoke-Sqlcmd

Invoke-Sqlcmd -Query $sqlStatement -ConnectionString $ConnectionString;

您可能需要Install-Module -Name SqlServer安装它。


如果您不想安装该SqlServer模块或者您的 Powershell 版本为 2 或更低,您可以尝试使用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 }
}
于 2015-05-21T15:30:22.050 回答
2

我遇到了完全相同的问题,我相信原因如下(对我有用):

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

于 2016-12-13T12:27:45.987 回答