問題が起きる前にNullを見つける
Nullが出現する式では、不良なデータが返される可能性があります。例えば、図1はNullをNullと照合した結果を示しています。ANSI_NULLS
の設定によっては、このステートメントは何もレコードを返さないことがあります。
ANSI_NULLS
オプションは、デフォルトではオフ(False)になっています。つまり、NullとNullの照合ではNullの代わりにTrueが返されます。このオプションをオン(True)にすると、NullとNullの照合では常にNullが返されます。デフォルトの設定はANSI規格に違反していますが、そうすることでエラーの発生を防いでいます。このオプションをどのように設定するかは好みの問題ですが、それによってNullの評価がどのように左右されるかを知っておくことは重要です。
どのような問題が起きる可能性があるかが分かるように、次のステートメントではオプションをオンにしています。
USE AdventureWorks SET ANSI_NULLS ON SELECT EmployeeID FROM HumanResources.Employee WHERE ManagerID = NULL
レコードを調べると、ManagerID
列にNull値を含むレコードが1つあることが分かります。ところが、前のステートメントを実行すると空の結果セットが返されます。つまり、存在しているレコードが返されません。さらに悪いことに、通常のエラーではなく、空の結果セットで体現される「エラー」は見逃される可能性が高いと考えられます。
このオプションの設定にかかわらず、Null値を検索する適切な方法は、次のようにIS NULL
を使うことです。
USE AdventureWorks SELECT EmployeeID FROM HumanResources.Employee WHERE ManagerID IS NULL
図2を見ると分かるように、ANSI_NULLS
オプションの設定を意識する必要はありません。しかも、このオプションがだれかにリセットされても、ステートメントは常に正しく機能します。
IS NULL
を使うと、特定の列のNull値を簡単に検索できますが、複数の列や全部の列を検索するのは少し複雑です。当初は、たくさんのOR
句を使った複雑なステートメントを検討するかもしれませんが、その必要はありません。単に次のような構文を使ってフィールドを連結するだけで済みます。
SELECT list|* FROM table WHERE nulcol1 + nulcol2 + ... IS NULL
複数の列を組み合わせることも、全部の列を連結することもできます。3つのフィールドを連結した結果を図3に示します。
USE AdventureWorks SELECT * FROM Person.Contact WHERE Suffix + EmailAddress + Phone IS NULL
チェックする各列を明示的に参照する必要はあるものの、この方法を使うと入力の手間を多少減らすことができます。Null許容型でない列は連結しないでください。エラーが発生します。この方法は、Null許容型のフィールドでのみ有効です。
この連結の動作は便利ですが、予期しない結果が返されることもあります。例えば、次のステートメントでは名、姓、およびミドルネームを連結しています。
Use AdventureWorks SELECT FirstName + ' ' + MiddleName + ' ' + LastName FROM Person.Contact
図4には思いがけない結果が表示されています。この場合、ANSI_NULLS
の設定は無関係です。いずれかの名前の値がNullの場合、結果の値はNullになります。この問題は次のようにISNULL()
を使って回避できます(図5を参照)。
Use AdventureWorks SELECT FirstName + ' ' + ISNULL(MiddleName, '') + LastName FROM Person.Contact
すべての列をISNULL()
に入れる必要はありません。列のプロパティで値が必須になっている場合は、列がNullになる可能性はありません(FirstName
とLastName
はNull許容型ではありません)。ISNULL()
関数では、次の構文を使い、指定した代替値でNull値を置き換えます。checkexpression
はNull値かどうかをチェックする値または列、replacementvalue
はNullの代わりに使用する値です。
ISNULL(checkexpression, replacementvalue)
この例では、Null許容型の列はMiddleName
だけです。ミドルネームの値を含まないレコードでは、名と姓の間に2つのスペース文字が挿入されます。
SQL ServerのCONCATENATE_NULL_YIELDS_NULL
の設定は、Null値を連結するすべての式の結果に影響を与えます。デフォルトはオン(True)で、Nullを連結するとNullが返されます。オフ(False)にすると、SQL ServerはNullを無視し、Null以外の他の値を連結した結果を返します。前のIS NULL
の例と同様に、ISNULL()
を使うと、このオプションがだれかにリセットされても、予期しない結果を回避できます。
たとえNull値による問題が起きなくても、ユーザーにとってNull値はほとんど意味を持ちません。より意味のあるデータの方がユーザーには役立ちます。データを参照するときに、ユーザーがNULLの意味を推測してくれるかもしれませんが、それを期待すべきではありません。次のステートメントのISNULL()
関数では、図3や図4のようにNULLを表示する代わりに、図6に示すように「None」を表示しています。
USE AdventureWorks SELECT Contact, ISNULL(Suffix, 'None') FROM Person.Contact
こうすればユーザーに分かりやすくなります。