花费更多时间在PowerShell中提取SQL数据。遇到[System.DBNull] :: Value以及PowerShell在比较期间如何处理这些问题。
以下是我看到的行为示例以及变通方法
#DBNull values don't evaluate like Null...
if([System.DBNull]::Value){"I would not expect this to display"}
# The text displays.
if([string][System.DBNull]::Value){"This won't display, but is not intuitive"}
# The text does not display.
#DBNull does not let you use certain comparison operators
10 -gt [System.DBNull]::Value
# Could not compare "10" to "". Error: "Cannot convert value "" to type "System.Int32". Error: "Object cannot be cast from DBNull to other types.""
[System.DBNull]::Value -gt 10
# Cannot compare "" because it is not IComparable.
#No real workaround. Must use test for null workaround in conjunction to avoid comparison altogether:
[string][System.DBNull]::Value -and [System.DBNull]::Value -gt 10
#Example scenario with a function that uses Invoke-Sqlcmd2 to pull data
Get-XXXXServer | Where-Object{$_.VCNumCPUs -gt 8}
#Error for every line where VCNumCPU has DBNull value
#workaround
Get-XXXXServer | Where-Object{[string]$_.VCNumCPUs -and $_.VCNumCPUs -gt 8}
我是否遗漏了任何内容,或者没有“简单”的解决方法可以让那些没有经验的人按预期使用PowerShell比较?
我提交了a suggestion on Connect并有一个临时的workaround from Dave Wyatt,它将数据行转换为psobjects,并将dbnulls转换为null,但是adds a bit of overhead。考虑到PowerShell现有的“松散”行为,似乎应该在幕后处理的东西?
有任何提示,或者我现在已经筋疲力尽了吗?
谢谢!
答案 0 :(得分:18)
我认为你在这里采取了错误的做法。作为documented,DBNull
类代表不存在的值,因此-gt
或-lt
等比较没有任何意义。不存在的值既不大于也不小于任何给定值。 Value
字段有一个Equals()
方法,可让您检查值是否为DBNull
:
PS C:> ([DBNull]::Value).Equals(23)
False
PS C:> ([DBNull]::Value).Equals([DBNull]::Value)
True
答案 1 :(得分:6)
最简单的方法是$var -isnot [DBNull]
。
我已经在自己的脚本中对此进行了测试,并且按预期工作。
答案 2 :(得分:3)
我通常最终会这样做:
[String]::IsNullOrWhiteSpace($Val.ToString())
或者这个:
[String]::IsNullOrEmpty($Val.ToString())
或者这个:
$Val.ToString() -eq [String]::Empty
这通常很好,因为[System.DBNull]::Value.ToString()
返回一个空字符串,因此[String]::IsNullOrWhiteSpace([System.DBNull]::Value)
和[System.DBNull]::Value.ToString() -eq [String]::Empty
都会评估为True。
显然,这些不在逻辑上是等价的,因为您的数据可能合法地具有空字符串,或者可能是一个没有意义的数据类型作为空字符串(例如整数) 。但是,由于您经常希望将DBNulls与空字符串和仅空白字符串完全相同,因此如果您足够了解数据,这将非常有用。
如果您确实想知道该值是否为DBNull,那么请使用[DBNull]::Value.Equals($Value)
。
答案 3 :(得分:1)
if( %youfunctetc%.GetType().Name -eq 'DBNull')
{}
else {}
答案 4 :(得分:0)
在PS中处理SQL数据时,我需要包含此函数并在需要时调用:
function Check-IsNullWithSQLDBNullSupport ($var) {
if ($var -eq [System.DBNull]::Value -or $var -eq $null) {
return $true
} else {
return $false
}
}
可以像这样使用:
if (Check-IsNullWithSQLDBNullSupport -var $VarToBeTested) {
write-output "Is Null"
}
答案 5 :(得分:0)
一些命令|其中FieldOfInterest -is DBNull 似乎为我工作。 DBNull是“类型”,-is运算符正在检查左侧的值是否为给定的“类型”。
您也可以使用oppsite 命令其中FieldOfInterest -isnot DBNull
答案 6 :(得分:0)
似乎我只对旧帖子发表评论,但是我认为与Dave Wyatt的讨论链接在上面已经断开,通过重新搜索我发现它here。
我目前正在处理的代码对性能不敏感,但是我确实需要比较返回数据以重置另一个类型不同的目标对象的属性。
通常方便的PowerShell,例如:
If( $SrcObject.Property ) { $TargObject.Property = $SrcObject.Property }
这不适用于[DBNull]
通常,我会花点时间查找/开发,然后使用最快的代码,而不管其需求或复杂性如何,但我必须尽快获得rev1。在我什至没有意识到[DBNull]问题之前,我已经使用简单的| Select $Props
$ Props是列名称的键入数组。但这并不会改变子属性的类型,因此比较仍然失败!
鉴于我已经走在了戴夫建议的道路上,所以我更加犹豫了。
$Props = ( $SQLData.Tables[0].Rows[0] | Get-Member -MemberType Properties ).Name
$Rows = $SQLData.Tables[0].Rows | Select $Props
ForEach( $RowObject in $Rows )
{
ForEach($Prop in $Props )
{
# Maybe: [String]::Empty below?
If( $RowObject.$Prop -is [DBNull] ) { $RowObject.$Prop = "" }
} #End Inner Loop.
} #End Outer Loop.
注意:这有点伪,因为产品代码中的行被埋在字典中,但是足以传达这种方法。另外,上述内容未经全面测试,因为它是从工作代码中翻译过来的。
我不知道为什么Get-Member不返回其他属性,如RowError,RowState等...,但是只要您不介意将[DBNull]的内容转换为空字符串,此方法就可以工作。而且,Get-Member更具可重用性,无需键入道具...
显然,这与前面提到的某些强制转换并没有太大区别,但是我可能并不孤单地希望在辅助函数中保留一些复杂性,因此“ main”看上去更加简洁。而且,一个空字符串应在以后满足大多数比较,尤其是考虑到背景中进行类型转换的情况。
我知道这是一个评论,不是问题,但是如果我有任何问题,请告诉我。在进行一个活跃的项目时,我确实对此有所迷惑。谢谢!