web-dev-qa-db-ja.com

PowerShellでSystem.DBNullを処理する

編集:PowerShell 7プレビュー2の時点で、 Joel Sallow via pull request 9794 のおかげで、-not [System.DBNull]::Value$trueと評価されます。

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の比較を期待どおりに使用できるようにするための「簡単な」回避策はありませんか?

私は提出しました Connectに関する提案 そして一時的な Dave Wyattからの回避策 データ行をpsobjectsに変換し、dbnullsをnullに変換しますが、これは 少しオーバーヘッドが追加されます 。 PowerShellの既存の「緩い」動作を考えると、内部で処理する必要があるもののように見えますか?

ヒントはありますか、それとも今のところオプションを使い果たしましたか?

ありがとう!

11
Cookie Monster

ここでは間違ったアプローチを取っていると思います。 documented のように、DBNullクラスは存在しない値を表すため、_-gt_や_-lt_のような比較は意味がありません。存在しない値は、指定された値より大きくも小さくもありません。 Value フィールドにはEquals()メソッドがありますが、これを使用すると、値がDBNullであるかどうかを確認できます。

_PS C:> ([DBNull]::Value).Equals(23)
False
PS C:> ([DBNull]::Value).Equals([DBNull]::Value)
True_
19
Ansgar Wiechers

最も簡単な方法は$var -isnot [DBNull]

私はこれを自分のスクリプトでテストしましたが、期待どおりに機能します。

9
Chrissy LeMaire

私が通常やることになるのはこれです:

_[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と評価されます。

明らかに、これらはnot論理的に同等です。これは、データに合法的に空の文字列が含まれている場合や、空の文字列(整数など)として意味をなさないデータ型である場合があるためです。ただし、DBNullを空の文字列や空白のみの文字列とまったく同じように扱いたい場合が多いため、データを十分に理解していると便利です。

もちろん、値がDBNullであるかどうかを実際に知りたい場合は、[DBNull]::Value.Equals($Value)を使用してください。

4
Bacon Bits
if( %youfunctetc%.GetType().Name -eq 'DBNull')
{}
else {}
1
Vladimir Winer

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"
}
0
Eric Weintraub

いくつかのコマンド|ここで、FieldOfInterest -isDBNullは私にとってはうまくいくようです。 DBNullは「タイプ」であり、-is演算子は、左側の値が指定された「タイプ」であるかどうかをチェックしています。

Oppsitesome-commandを使用することもできます|ここで、FieldOfInterest -isnot DBNull

0
Sudhi

私は古い投稿にコメントするだけのようですが、デイブワイアットとの議論へのリンクは上で壊れていると思います、再グーグルを通して私はそれを見つけました ここ

現在作業しているコードはパフォーマンスに依存しませんが、戻りデータを比較して、別の異なる型のターゲットオブジェクトのプロパティをリセットする必要があります。

したがって、通常は次のような便利なPowerShellです。

If( $SrcObject.Property ) { $TargObject.Property = $SrcObject.Property }

これは[DBNull]では機能しません

通常、私は時間をかけて検索/開発し、必要性や複雑さに関係なく最速のコードを使用しますが、できるだけ早くrev1を入手する必要があります。 [DBNull]の問題に気付く前に、簡単な| Select $Propsを使用してオブジェクトを[PSCustomObject]に切り替えていました。

$ 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.

注:prodコードには行が辞書に埋め込まれているため、これは少し疑似的ですが、アプローチを伝えるには十分なはずです。また、上記は動作中のコードから変換されたため、完全にはテストされていません。

Get-MemberがRowError、RowStateなどの他のプロパティを返さない理由はわかりませんが、[DBNull]を空の文字列に変換してもかまわない限り、これは機能します。そして、Get-Memberはもう少し再利用可能で、小道具を入力する必要はありません...

明らかに、これは前述のキャストの一部と大差ありませんが、ヘルパー関数に複雑さを残したいと思っているのは私だけではないので、「メイン」は少しきれいに見えます。さらに、空の文字列は、特にバックグラウンドで行われる型変換を考慮すると、後でほとんどの比較を満たす必要があります。

これは質問ではなくコメントですが、何か問題があれば教えてください。私は活発なプロジェクトに取り組んでいるときにこれにつまずきました。ありがとう!

0
Steven