我试图在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()
答案 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
。它是由非常聪明的人编写和维护的,因此很可能会为您服务。
如果您需要在短时间内运行大量查询,并且可以从重用连接中受益。还是希望参数化查询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
只是为了向我们提供一致的字段顺序,而不必依赖有序哈希表,并且在运行类似{{1 }}。