查询中可为空的运算符

可为空运算符是二进制算术或比较运算符,可在一侧或两侧使用可为空算术类型。 当使用来自允许 NULL 代替实际值的数据库等来源的数据时,会出现可为空类型。 可为空运算符用于查询表达式。 除了用于算术和比较的可为空运算符外,转换运算符还可用于可为空类型之间的转换。 某些查询运算符也有可为空的版本。

注意

可为空运算符通常仅用于查询表达式。 如果不使用查询表达式,则无需了解或使用这些运算符。

可为空运算符表

下表列出了 F# 中支持的可为空运算符。

左侧可为空 右侧可为空 两侧均可为空
?>= >=? ?>=?
?> >? ?>?
?<= <=? ?<=?
?< <? ?<?
?= =? ?=?
?<> <>? ?<>?
?+ +? ?+?
?- -? ?-?
?* *? ?*?
?/ /? ?/?
?% %? ?%?

注解

可为空运算符包含在命名空间 FSharp.Linq 中的 NullableOperators 模块中。 可为空数据的类型是 System.Nullable<'T>

在查询表达式中,当从允许 NULL 而不是值的数据源中选择数据时,会出现可为空类型。 在 SQL Server 数据库中,表中的每个数据列都有一个属性,该属性指示是否允许使用 NULL。 如果允许 NULL,则从数据库返回的数据可以包含不能用原始数据类型(例如 intfloat 等)表示的 NULL。 因此,数据以 System.Nullable<int> 而不是 int,以 System.Nullable<float> 而不是 float 的形式返回。 实际值可以通过使用 Value 属性从 System.Nullable<'T> 对象中获取,你可以通过调用 HasValue 方法来确定 System.Nullable<'T> 对象是否有值。 另一个有用的方法是 System.Nullable<'T>.GetValueOrDefault 方法,可用于获取相应类型的值或默认值。 默认值是某种形式的“零”值,例如 0、0.0 或 false

可以使用常用的转换运算符(例如 intfloat)将可为空的类型转换为不可为空的原始类型。 还可以通过使用可为空类型的转换运算符将一种可为空类型转换为另一种可为空类型。 相应的转换运算符与标准运算符同名,但它们位于一个单独的模块中,即 FSharp.Linq 命名空间中的 Nullable 模块。 通常,在使用查询表达式时会打开此命名空间。 在这种情况下,可以通过将前缀 Nullable. 添加到适当的转换运算符来使用可为空的转换运算符,如以下代码所示。

open Microsoft.FSharp.Linq

let nullableInt = new System.Nullable<int>(10)

// Use the Nullable.float conversion operator to convert from one nullable type to another nullable type.
let nullableFloat = Nullable.float nullableInt

// Use the regular non-nullable float operator to convert to a non-nullable float.
printfn $"%f{float nullableFloat}"

输出为 10.000000

可为空数据字段上的查询运算符(例如 sumByNullable)也可用于查询表达式。 不可为空类型的查询运算符与可为空类型的类型不兼容,因此在处理可为空数据值时必须使用相应查询运算符的可为空版本。 有关详细信息,请参阅查询表达式

以下示例显示了在 F# 查询表达式中使用可为空的运算符。 第一个查询显示了如何编写没有可为空运算符的查询;第二个查询显示了一个使用可为空运算符的等效查询。 有关完整上下文,包括如何设置数据库以使用此示例代码,请参阅演练:使用类型提供程序访问 SQL 数据库

open System
open System.Data
open System.Data.Linq
open Microsoft.FSharp.Data.TypeProviders
open Microsoft.FSharp.Linq

[<Generate>]
type dbSchema = SqlDataConnection<"Data Source=MYSERVER\INSTANCE;Initial Catalog=MyDatabase;Integrated Security=SSPI;">

let db = dbSchema.GetDataContext()

query {
    for row in db.Table2 do
    where (row.TestData1.HasValue && row.TestData1.Value > 2)
    select row
} |> Seq.iter (fun row -> printfn $"%d{row.TestData1.Value} %s{row.Name}")

query {
    for row in db.Table2 do
    // Use a nullable operator ?>
    where (row.TestData1 ?> 2)
    select row
} |> Seq.iter (fun row -> printfn "%d{row.TestData1.GetValueOrDefault()} %s{row.Name}")

另请参阅