【问题标题】:Global variable loses value outside loop全局变量在循环外失去价值
【发布时间】:2015-09-12 01:59:13
【问题描述】:

我正在编写一个脚本来从 SQL 数据库中的 BLOB 中提取数据。提取过程效果很好。我想在脚本中添加某种进度指示。我有一个来自 SQL 查询的总记录数,以及一个随着导出的每个文件而增加的增量计数器。增量计数器有效,但总记录数——我试图分配给一个全局变量——似乎并没有保持它的值。我声明不正确吗?

## Export of "larger" Sql Server Blob to file            
## with GetBytes-Stream.         
# Configuration data     
$StartTime = Get-Date
$Server = "server";
$UserID = "user";
$Password = "password";
$Database = "db";
$Dest = "C:\Users\me\Desktop\Test\";
$bufferSize = 8192; 

# Counts total rows
$CountSql = "SELECT Count(extension) as countall from 
                (
                    SELECT  p.[people_id], right(pi.[file_name],4) as extension
                    FROM dbo.pictures as pi 
                    INNER JOIN dbo.people AS p ON p.person_picture = pi.pictures_id
                    where left([image_type], 5) = 'image'
                ) as countall"

# Selects Data
$Sql = "SELECT p.[people_id], pi.[image_file], right(pi.[file_name],4), ROW_NUMBER() OVER (ORDER BY people_id) as count
        FROM dbo.pictures as pi 
        INNER JOIN dbo.people AS p ON p.person_picture = pi.pictures_id
        where left([image_type], 5) = 'image'";    

# Open ADO.NET Connection            
$con = New-Object Data.SqlClient.SqlConnection;            
$con.ConnectionString = "Data Source=$Server;" +             
                        "Integrated Security=False;" + 
                        "User ID=$UserID;" +
                        "Password=$Password;" +           
                        "Initial Catalog=$Database";            
$con.Open();            

# New Command and Reader for total row count
$CountCmd = New-Object Data.SqlClient.SqlCommand $CountSql, $con;
$crd = $CountCmd.ExecuteReader();
While ($crd.Read())
{
    $crd.GetValue($global:1)
}
$crd.Close();

# New Command and Reader for rest of data     
$cmd = New-Object Data.SqlClient.SqlCommand $Sql, $con;            
$rd = $cmd.ExecuteReader();            

# Create a byte array for the stream.            
$out = [array]::CreateInstance('Byte', $bufferSize)            

# Looping through records
While ($rd.Read())            
{            
    $total = $global:1
    $counter = ($rd.GetValue(3));
    Write-Output ("Exporting $counter of $total`: {0}" -f $rd.GetGUID(0));

    # New BinaryWriter            
    $fs = New-Object System.IO.FileStream ($Dest + $rd.GetGUID(0) + $rd.GetString(2)), Create, Write;            
    $bw = New-Object System.IO.BinaryWriter $fs;            

    $start = 0;            
    # Read first byte stream            
    $received = $rd.GetBytes(1, $start, $out, 0, $bufferSize - 1);            
    While ($received -gt 0)            
    {            
       $bw.Write($out, 0, $received);            
       $bw.Flush();            
       $start += $received;            
       # Read next byte stream            
       $received = $rd.GetBytes(1, $start, $out, 0, $bufferSize - 1);            
    }            

    $bw.Close();            
    $fs.Close();            
}            

# Closing & Disposing all objects            
$fs.Dispose();            
$rd.Close();            
$cmd.Dispose();            
$con.Close();            
$EndTime = Get-Date
$TotalTime = $EndTime - $StartTime    
Write-Host ("Finished in {0:g}" -f $TotalTime)

输出

PS C:\Users\me> C:\Scripts\ExportImagesFromNTST.ps1
21380
Exporting 1 of : 3089b464-e667-4bf4-80b3-0002d582d4fa
Exporting 2 of : 04cf7738-ae19-4771-92b8-0003c5f27947
Exporting 3 of : 94485b5d-fe71-438d-a097-000ad185c915

等等。 21380 应该是 $1,也应该是 $total。

【问题讨论】:

  • 在你的脚本中没有任何地方给$global:1赋值,而$total只从那个未知值的变量中赋值。

标签: powershell


【解决方案1】:

我认为 PetSerAl 在这里一针见血。您创建一个SqlCommand 对象($CountCmd),然后创建一个SqlDataReader$crd),然后告诉$crd 使用接受整数作为参数的GetValue() 方法,这样它知道要返回哪一列的值,但是您引用了一个名为“1”的全局变量,该变量从未定义过,因此您有效地将$null 传递给该方法,因此它没有得到任何值。老实说,我很惊讶它不会在那里向你抛出错误。您可能只想传递整数 1 作为该方法的参数,然后在 While 循环中将其分配给 $Total。老实说,我在这里猜测,但据我所见,我认为应该是:

$crd = $CountCmd.ExecuteReader();
While ($crd.Read())
{
    $Total = $crd.GetValue(0)
}
$crd.Close();

我很确定这将分配第一列的值(对于该 sql 命令,它应该只是 1 行 1 列,对吗?只是总数?),无论如何,将第一列的值分配给当前行到$Total。稍后您可以参考$Total 来更新您的进度。

先生,如果您想跟踪进度,需要查看 write-progress cmdlet,它非常适合您的脚本。

【讨论】:

  • 第一列的索引为 0,而不是 1。但我使用了它,它工作得很好!我的脚本工作还在继续 - 谢谢你的帮助。
  • 啊,我的 SQL-fu 很弱,抱歉。很高兴你让它工作,我会更新答案以显示正确的代码。
  • 我采纳了您的建议并使用了 write-progress!这很棒! i.imgur.com/T9V0IkA.png
猜你喜欢
  • 2019-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-14
相关资源
最近更新 更多