【问题标题】:Powershell foreach SQL Server query randomly collates DataSetPowershell foreach SQL Server 查询随机整理数据集
【发布时间】:2019-11-14 14:14:25
【问题描述】:

我需要编写一个简单的内务脚本来检查 14 个 SQL Server 的表。 我最终使用了来自this 问题的Adam's 脚本。

但是,在运行时,它会跳过随机数量的服务器的发布结果,然后将它们的查询转储到一个表中,然后单独执行任何剩余的服务器。我做错了什么? (因为我无法评论亚当的回答)

我不介意整理表格中的所有数据并在最后转储,但如果这样更容易完成,那么可以使用服务器名称和结果列。

这是我的脚本,下面的示例输出;

$year = Get-Date -Format yyyy
$servers = gc 'ServerList.txt'

foreach ($server in $servers) 
{
    try
    {
        $ServerName = "$server"
        $DatabaseName = "DatabaseName"
        $Query = "select count(*) from Alarm_Activations where Activation_Date not like '%$year%'"
        #$ds = $null -Tried adding this to fix the issue

        #Timeout parameters
        $QueryTimeout = 120
        $ConnectionTimeout = 30

        Write-Host ""
        Write-Host ""
        Write-Host "  Checking $server "

        #Action of connecting to the Database and executing the query and returning results if there were any.
        $conn = New-Object System.Data.SqlClient.SQLConnection
        $ConnectionString = "Data Source=$server\sqlexpress;Initial Catalog=DatabaseName;Connect Timeout=30; Integrated security=true;"
        $conn.ConnectionString = $ConnectionString

        $conn.Open()

        $cmd = New-Object system.Data.SqlClient.SqlCommand($Query,$conn)
        $cmd.CommandTimeout = $QueryTimeout

        $ds = New-Object system.Data.DataSet
        $da = New-Object system.Data.SqlClient.SqlDataAdapter($cmd)
        [void]$da.fill($ds)
        $conn.Close()

        Write-Host "Number of activations older than $year :"
        $ds.Tables
    } 
    catch 
    {
        Write-Error "Something went wrong. Seems you have to do it manually!"
        return
    }
}

示例输出

转储表的点似乎是随机的,但通常超过一半的服务器。

我见过的最早的是 Server-5,最新的;服务器 14;

& CheckAlarmActivation.ps1


  Checking Server-1
Number of activations older than 2019 :



  Checking Server-2
Number of activations older than 2019 :


  Checking Server-3
Number of activations older than 2019 :


  Checking Server-4
Number of activations older than 2019 :


  Checking Server-5
Number of activations older than 2019 :


  Checking Server-6
Number of activations older than 2019 :


  Checking Server-7
Number of activations older than 2019 :


  Checking Server-8
Number of activations older than 2019 :


  Checking Server-9
Number of activations older than 2019 :


  Checking Server-10
Number of activations older than 2019 :
Column1
-------
      0
      0
      0
   3318
   1069
   4375
      8
   9357
     74
   1735


  Checking Server-11
Number of activations older than 2019 :
   7917


  Checking Server-12
Number of activations older than 2019 :
   3583


  Checking Server-13
Number of activations older than 2019 :
   5622


  Checking Server-14
Number of activations older than 2019 :
   4166

【问题讨论】:

  • 我会先用Write-Output 替换Write-Host。据我所知,与主机的交互不是同步的。
  • 无关提示:将 conn.Close 放入 finally 块中,如下所示:finally { $conn.Close() } 原因? PowerShell 不支持 using 语句来确保向连接池发送信号以处理连接。
  • 我将所有写主机移到 $ds.Tables 上方,并为 $conn.Close 设置了 finally 块。该脚本现在按预期执行,为每个服务器写入一个结果,然后执行下一个。但是,第一台服务器将其输出为表格,以 Column1 作为标题,并在一行中显示结果。其余的只是打印结果。我认为这是由于我对数据集的处理不当造成的?我也会看看 Brandon McLure 的建议!

标签: sql powershell dataset powershell-5.0


【解决方案1】:

就像 AdminOfThings 评论的那样,使用 Write-Output 而不是 Write-Host。因为您正在使用Write-Host 流并且只是输出一个变量(它进入输出流)($ds.Tables),所以您不会同时在控制台上看到这两个流。

除了使用Write-Output 之外,我还会通过将数据库调用的结果(以及您想要从它们返回的任何结果)存储为您放入集合中的 PSObjects 来解决该问题,然后在您遍历之后您可以一次性格式化所有服务器(到控制台、生成 csv/html 报告等)。这种方法的好处是,如果其中一台服务器停机,您的脚本不会失败。

编辑我更新了 catch 块以正确的方式获取异常。我还更新了您从数据集中获取值以展开的位置,因此您没有数据行对象。如果您有多个列,或者您想要基于某些列创建计算属性,则可以使用更高级的 Select-Object 逻辑。 SO post with more info。在您的情况下,您只想获取记录数。

$serverResults = @()
$servers = "srv1","srv2"

foreach ($server in $servers) 
{
  # Create an instance of a custom object that will hold the results of your query. Add properties as needed, they can be other complex objects, or simple types. 
   $serverResultInstance = New-Object PSObject -Property @{
   ServerName = $server
   DatabaseName = $DatabaseName 
   Tables = $null
   Exception = $null
   }
 try{
    #Connect to DB like you do (Don't use Write-Host)
    #Add your dataset for this instance 
    $ServerName = "$server"
        $DatabaseName = "dbName"
        $Query = "select count(*) from sys.columns"
        #$ds = $null -Tried adding this to fix the issue

        #Timeout parameters
        $QueryTimeout = 120
        $ConnectionTimeout = 30

        Write-Host ""
        Write-Host ""
        Write-Host "Checking $server"

        #Action of connecting to the Database and executing the query and returning results if there were any.
        $conn = New-Object System.Data.SqlClient.SQLConnection
        $ConnectionString = "Data Source=$server;Initial Catalog=$DatabaseName;Connect Timeout=$ConnectionTimeout; Integrated security=true;"
        $conn.ConnectionString = $ConnectionString

        $conn.Open()

        $cmd = New-Object system.Data.SqlClient.SqlCommand($Query,$conn)
        $cmd.CommandTimeout = $QueryTimeout

        $ds = New-Object system.Data.DataSet
        $da = New-Object system.Data.SqlClient.SqlDataAdapter($cmd)
        [void]$da.fill($ds)

        # Instead of keeping the whole table/row object, expand out the column you want. If you have multiple columns or tables in your dataset you would need to change these values. 
    $serverResultInstance.Tables = $ds.Tables[0] | select -ExpandProperty Column1
 }
 catch{
    #This should have been $_.Exception
    $serverResultInstance.Exception = $_.Exception
 }
 finally{
    $conn.Close()
    $serverResults  += $serverResultInstance
 }
}
foreach($result in $serverResultInstance){
# Format your output here. Make a csv, send an email, etc. Or just:
 Write-Output "Number of activations older than $year :"
 Write-Output $result.Tables

}

【讨论】:

  • 好的,首先我没有为catch{ serverResultInstance.Exception = $_ } 找到对象。将其更改为catch { $serverResultInstance.Exceptions = $_ },现在它说找不到对象的 Exceptions 属性,即使我们之前清楚地做了它,拼写检查 ofc。也试过catch { $serverResultInstance.Exception = $_ },但同样的问题。
  • 注释掉我可以继续的捕获,但是我正在努力从 system.data 对象输出数据,使用write-output $serverResults | format-table ServerName, Alarm_Activations 这提供了一个带有服务器名称的表,并且 {System .Data.DataRow}
  • @Hargaut,我修复了 catch 中的错误,并添加了代码以显示如何从数据表(而不是 DataRow)中获取值本身。还将我的注释语法从 C# 更新为 Powershell :-) 让我知道这是否会给您带来任何问题/如果像这样格式化数据行将适用于您的情况。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多