基于属性参数化 F# 查询

Parametrize F# queries based on properties

我正在寻找对几个表的一些常见查询的分解。在一个非常简单的示例中,所有表都有一个 DataDate 列,所以我有这样的查询:

let dtexp1 = query { for x in table1 do maxBy x.Datadate }
let dtexp2 = query { for x in table2 do maxBy x.Datadate }

基于 我可以执行以下操作:

let mkQuery t q  = 
        query { for rows in t do maxBy ((%q) rows) }

let getMaxDt1 = mkQuery table1 (<@ fun q -> q.Datadate @>)   
let getMaxDt2 = mkQuery table2 (<@ fun q -> q.Datadate @>)

如果有任何其他不使用引号的解决方案,我会很感兴趣。原因是对于更复杂的查询,引文和拼接变得难以阅读。

例如,这显然是行不通的,因为我们不知道 x 有 属性 DataDate。

let getMaxDt t = query { for x in t do maxBy x.Datadate }

除非我可以抽象出由SqlProvider生成的table1、table2等的类型。

答案在很大程度上取决于您需要构建什么样的查询以及它们是静态的还是动态的。一般来说:

  • LINQ 很棒,如果它们大部分是静态的,并且如果您可以轻松列出您需要的所有查询的所有模板 - 主要的好处是它静态类型检查查询

  • 当您的查询结构非常动态时,LINQ 就不是那么好,因为那样您最终会编写大量的引用,并且类型检查有时会妨碍。

如果您的查询非常动态(包括动态选择源),但不太复杂(例如,没有花哨的分组,没有花哨的连接),那么编写代码来生成 SQL 查询可能会更容易来自 F# 域模型。

对于您的简单示例,查询实际上只是一个 table 名称和聚合:

type Column = string
type Table = string

type QueryAggregate =
  | MaxBy of Column

type Query = 
  { Table : Table
    Aggregate : QueryAggregate }

然后您可以使用以下方法创建两个查询:

let q1 = { Table = "table1"; Aggregate = MaxBy "Datadate" }
let q2 = { Table = "table2"; Aggregate = MaxBy "Datadate" }

将这些查询翻译成 SQL 非常简单:

let translateAgg = function
  | MaxBy col -> sprintf "MAX(%s)" col

let translateQuery q =
  sprintf "SELECT %s FROM %s" (translateAgg q.Aggregate) q.Table

根据您的查询的丰富程度,转换可能会变得非常复杂,但如果结构相当简单,那么这可能只是一种比使用 LINQ 构建查询更简单的替代方法。正如我所说,如果不知道确切的用例,很难说什么会更好!