【问题标题】:F# Query Expression using join & groupBy in same query在同一查询中使用联接和分组依据的 F# 查询表达式
【发布时间】:2018-01-01 02:53:53
【问题描述】:

我决定学习如何在 F# 中使用查询表达式并找到了the official Microsoft documentation。警告似乎没有详细说明,所以我确定我已经通过简单的修复遇到了问题,但不知道为什么我会收到它显示的错误。

我的想法是编写一个同时执行连接和分组的查询。例如,使用他们的示例“MyDatabase”,我想我会尝试查找每个学生注册的课程数量。编写以下查询后,编译器不会标记任何警告,但是当我运行它时会出现错误.

我的查询表达式:

query {
    for student in db.Student do
    join course in db.CourseSelection
        on (student.StudentID = course.StudentID)

    groupBy student into group

    select (group.Key, group.Count())

}

|> Seq.iter (fun (student, classCount) -> printfn "Student %s has %i classes" student.Name classCount)

错误:

System.InvalidOperationException: Could not format node 'New' for execution as SQL.
   at System.Data.Linq.SqlClient.SqlFormatter.Visitor.VisitNew(SqlNew sox)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
>    at System.Data.Linq.SqlClient.SqlFormatter.Visitor.VisitAlias(SqlAlias alias)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlFormatter.Visitor.VisitSelect(SqlSelect ss)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlFormatter.Visitor.VisitScalarSubSelect(SqlSubSelect ss)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlFormatter.Visitor.VisitRow(SqlRow row)
   at System.Data.Linq.SqlClient.SqlFormatter.Visitor.VisitSelect(SqlSelect ss)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlFormatter.Visitor.Format(SqlNode node, Boolean isDebug)
   at System.Data.Linq.SqlClient.SqlFormatter.Format(SqlNode node)
   at System.Data.Linq.SqlClient.SqlProvider.BuildQuery(ResultShape resultShape, Type resultType, SqlNode node, ReadOnlyCollection`1 parentParameters, SqlNodeAnnotations annotations)
   at System.Data.Linq.SqlClient.SqlProvider.BuildQuery(Expression query, SqlNodeAnnotations annotations)
   at System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query)
   at System.Data.Linq.DataQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at Microsoft.FSharp.Collections.SeqModule.Iterate[T](FSharpFunc`2 action, IEnumerable`1 source)
   at <StartupCode$FSI_0008>.$FSI_0008.main@() in C:\Users\JDKS\Library\query expressions.fsx:line 129
Stopped due to error

我什至认为我可以通过使用子查询来摆脱这个问题:

query {
    for student in db.Student do
    join course in db.CourseSelection
        on (student.StudentID = course.StudentID)

    let count = query {
        for s in student.Name do
        select course.CourseID
        count
    }

    select (student.Name, count)

}

|> Seq.iter (fun (student, classCount) -> printfn "Student %s has %i classes" student classCount)

但这给出了更大的错误:

> System.NotSupportedException: Sequence operators not supported for type 'System.String'.
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.ConvertToFetchedSequence(SqlNode node)
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitAlias(SqlAlias a)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlVisitor.VisitSource(SqlSource source)
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitSelect(SqlSelect select)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitAlias(SqlAlias a)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlVisitor.VisitSource(SqlSource source)
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitSelect(SqlSelect select)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitAlias(SqlAlias a)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlVisitor.VisitSource(SqlSource source)
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitSelect(SqlSelect select)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlVisitor.VisitSequence(SqlSelect sel)
   at System.Data.Linq.SqlClient.SqlVisitor.VisitScalarSubSelect(SqlSubSelect ss)
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitSubSelect(SqlSubSelect ss)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitExpression(SqlExpression expr)
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.FetchExpression(SqlExpression expr)
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitNew(SqlNew sox)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitExpression(SqlExpression expr)
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitSelect(SqlSelect select)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitAlias(SqlAlias a)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlVisitor.VisitSource(SqlSource source)
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitSelect(SqlSelect select)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlBinder.Visitor.VisitIncludeScope(SqlIncludeScope scope)
   at System.Data.Linq.SqlClient.SqlVisitor.Visit(SqlNode node)
   at System.Data.Linq.SqlClient.SqlBinder.Bind(SqlNode node)
   at System.Data.Linq.SqlClient.SqlProvider.BuildQuery(ResultShape resultShape, Type resultType, SqlNode node, ReadOnlyCollection`1 parentParameters, SqlNodeAnnotations annotations)
   at System.Data.Linq.SqlClient.SqlProvider.BuildQuery(Expression query, SqlNodeAnnotations annotations)
   at System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq.Provider.IProvider.Execute(Expression query)
   at System.Data.Linq.DataQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
   at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
   at Microsoft.FSharp.Collections.SeqModule.Iterate[T](FSharpFunc`2 action, IEnumerable`1 source)
   at <StartupCode$FSI_0015>.$FSI_0015.main@() in C:\Users\JDKS\Library\query expressions.fsx:line 144
Stopped due to error

我的猜测是,我并没有真正了解幕后发生的事情,不足以解决错误或引导自己找到解决方案。可以在这里查询我想要的吗?有解决办法吗?

【问题讨论】:

  • 我认为这是因为查询末尾的元组。尝试不使用它。
  • 我在没有元组的情况下尝试了它,但它不起作用。然后我尝试先分组然后加入,它成功了。但这不是我想要的。你只能groupBy student.StudentID 因为这是加入CourseSelection 的列。我正在尝试恢复名称,但我不敢相信没有办法直接按名称分组和计算课程。
  • for s in student.Name 正在尝试迭代名称中的字符,这就是您在第二个查询中出现此错误的原因

标签: linq f# query-expressions


【解决方案1】:

当我尝试在本地运行它时,我得到了这个异常

System.Exception: Grouping over multiple tables is not supported yet

我不知道为什么你会得到那个错误的不太漂亮的版本。但是,如果您将 GroupBy 从查询中拉出,一切似乎都可以正常工作。

query {
    for student in db.Student do
    join course in db.CourseSelection
        on (student.StudentID = course.StudentID)
    select (student.Name, course.Id)
} 
|> Seq.countBy snd
|> Seq.iter (fun (student, classCount) -> printfn "Student %s has %i classes" student classCount)

【讨论】:

  • 我认为你的 countBy 应该是 fst 而不是 snd
  • 我的错误如此之大真的很奇怪。您运行的是 F# 版本 14.0.23413.0 吗?
  • 但这会在客户端进行分组。巨大的性能损失。
【解决方案2】:

我认为您的第一个查询不起作用的原因是因为groupBy student into group

我想你真正想写的是groupBy student.StudentID into group

【讨论】:

    【解决方案3】:

    我从similar question 找到了答案:

    query {
        for student in db.Student do
    
        join course in db.CourseSelection
            on (student.StudentID = course.StudentID)
    
        let count = query { 
            for c in db.CourseSelection do
            where (c.StudentID = student.StudentID)
            select c
            count
        }
    
    
        select (student.Name, count)
        distinct
    
    }
    
    |> Seq.iter (fun (student, classCount) -> printfn "Student %s has %i classes" student classCount)
    

    这些查询表达式的基本范式仍然让我难以理解。

    编辑:

    我发现你可以隐式加入而无需使用join 运算符

    query {
        for student in db.Student do
        let count = query {
            for course in student.CourseSelection do
            count    
        }
    
        select (student.Name, count)
    }
    |> Seq.iter (fun (name, count) -> printfn "%s has %i courses" name count)
    

    【讨论】:

      猜你喜欢
      • 2018-03-13
      • 1970-01-01
      • 1970-01-01
      • 2018-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-26
      相关资源
      最近更新 更多