F# F 3.0查询中的多列分组

F# F 3.0查询中的多列分组,f#,f#-3.0,F#,F# 3.0,只是尝试一下F3.0,在按多个列进行分组时遇到了一些麻烦。最明显的尝试是 query { for d in context.table do groupBy (d.col1,d.col2) into g select (g.Key) } 但我得到的结论是,LINQtoEntities中只支持无参数构造函数和初始值设定项。例外 我似乎在msdn上找不到一个例子 我意识到我的问题与 但它似乎是powerpack/F2.x的焦点,我希望F3.0有一个优雅的答案。。。有什么想法

只是尝试一下F3.0,在按多个列进行分组时遇到了一些麻烦。最明显的尝试是

query {
    for d in context.table do
    groupBy (d.col1,d.col2) into g
    select (g.Key)
}
但我得到的结论是,LINQtoEntities中只支持无参数构造函数和初始值设定项。例外

我似乎在msdn上找不到一个例子

我意识到我的问题与 但它似乎是powerpack/F2.x的焦点,我希望F3.0有一个优雅的答案。。。有什么想法吗

更新:

我在阅读Brian的帖子时发现了CLIMutable属性:

我很乐观,所以我试了一下

[<CLIMutable>]
type MyRecord = { Column1 : int; Column2 : int }

query {
    for d in context.table do
    groupBy {Column1 = col1; Column2 = col2} into g
    select (g.Key)
}

不幸的是,我得到了完全相同的异常。

首先,您必须记住,查询在某个时刻被转换为实际的SQL。linq似乎不支持将多个组键用作元组。因此,任何到元组的转换都必须在数据库调用完成后进行

其次,您应该能够通过在各个键上彼此后面执行多个分组来实现多个键分组:

query {
    for d1 in context.table do
    groupBy d1.col1 into g1
    for d2 in g1 do
    groupBy d2.col2 into g2
    select g2
}

如果语法不是100%,请原谅我,因为F不是我的母语:但是这个概念应该很好用。

使用groupBy时,你需要选择一个聚合函数,例如count、sum、avg…

我在你的第一个链接中看到了这一点,我认为这是你想要的:

query {  
    for d in context.table do  
    groupBy (new {d.col1, d.col2}) into g  
    select (g.Key) 
}
query {
    for student in db.Student do
    groupValBy student.Name student.Age into g
    select (g, g.Key, g.Count())
}

下面是一个在c中使用多个列进行分组并转换为f的示例。过度偏执的管理让我重新命名了所有内容,但我相信我是一致的:

数据库是由SqlMetal生成的,GetSummedValuesResult是F记录类型

c


虽然这会起作用,但它会在客户端进行聚合,而不是制定适当的SQL。

显然,可能有一种解决方案,如中所示,可以使用来自的groupValBy和AnonymousObject的组合

//in F# 3.0
open Microsoft.FSharp.Linq.RuntimeHelpers
open Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter
open System.Linq

//[<CLIMutable>]
//type MyRecord = { Column1 : int; Column2 : int }
// require constructor in F#
// groupBy is not valid

type T(column1 : int, column2 : int)
    member val Colum1=colum1 with get,set
    membre val Colum2=colum2 with get,set

query {
    for d in context.table do
    groupValBy d (NewAnonymousObjectHelper(T(d.Colum1,d.Colume2))) into g
    select (g.Key)
    }


问题是您在元组上分组,还是选择元组?错误似乎与输出类型有关。也许选择g.计数,看看会发生什么。我没有试过3.0…只是猜测而已。我试过选择一个常量select 1,得到了相同的结果thing@ildjarn:g.Key是一个元组,它没有无参数构造函数。如果我的猜测是正确的,选择1应该可以工作。@Daniel:select g.Key不会构造或初始化新的元组。AFAICT,select g.Key将是一个元组,它调用元组的构造函数括号只是为了分组。正是逗号使其成为元组。一个只有一个元素的元组被输入为1,这个示例实际上只是为了说明,使用select g。键没有用处,但应该可以工作。。。select g.Count给出了相同的错误。有趣的是,LINQ to Entities中仍然只支持get无参数构造函数和初始值设定项。似乎可以工作。但是,除了选择g键之外,在获取任何内容方面都有困难。select g.Count给出了一个错误,我不知道如何在其他列上进行聚合。仍在使用想法thoughgroupValBy被描述为为为迄今为止选择的每个元素选择一个值,并按给定的键对元素进行分组。关键是这个案例仍然是基于student.NameSo,为什么会有student.Age?我不敢相信在FSharp中没有简单的方法可以通过多个列进行分组。我现在在join中看到了类似的情况-如何编写复合键?实际上,这将仅按student.Age分组。第一个arg groupValBy是将要存储的值,第二个是groupby的键。想象一下第一个和第二个参数之间的单词by。请添加注释以帮助提问者理解。否则这就是magic.new{d.col1,d.col2}构造了一个C匿名类型;这对F无效。
public static class Reports
{
    public static IReadOnlyList<GetSummedValuesResult> GetSummedValues(TheDatabase db, DateTime startDate, DateTime? endDate)
    {
        var query =
            from sv in db.SomeValues

            where (sv.ADate >= startDate && sv.ADate <= (endDate ?? startDate))

            group sv by new { sv.ADate, sv.Owner.Name } into grouping

            select new GetSummedValuesResult(
                grouping.Key.ADate,
                grouping.Key.Name,
                grouping.Sum(g => g.Value)
            );

        return query.ToList();
    }
}
type Reports() =
    static member GetSummedValues (db:TheDatabase) startDate (endDate:Nullable<DateTime>) =
        let endDate = if endDate.HasValue then endDate.Value else startDate

        let q = query {
            for sv in db.SomeValues do
            where (sv.ADate >= startDate && sv.ADate <= endDate)

            let key = AnonymousObject<_,_>(sv.ADate, sv.Owner.Name)
            groupValBy sv key into grouping

            select {
                ADate        = grouping.Key.Item1;
                AName        = grouping.Key.Item2;
                SummedValues = grouping.Sum (fun (g:TheDatabaseSchema.SomeValues) -> g.Value)
            }
        }

        List(q) :> IReadOnlyList<GetSummedValuesResult>
SummedValues  = grouping |> Seq.sumBy (fun g -> g.SomeValues)
//in F# 3.0
open Microsoft.FSharp.Linq.RuntimeHelpers
open Microsoft.FSharp.Linq.RuntimeHelpers.LeafExpressionConverter
open System.Linq

//[<CLIMutable>]
//type MyRecord = { Column1 : int; Column2 : int }
// require constructor in F#
// groupBy is not valid

type T(column1 : int, column2 : int)
    member val Colum1=colum1 with get,set
    membre val Colum2=colum2 with get,set

query {
    for d in context.table do
    groupValBy d (NewAnonymousObjectHelper(T(d.Colum1,d.Colume2))) into g
    select (g.Key)
    }
query {
    for d in context.table do
    let key = AnonymousObject<_,_>(d.col1,d.col2)
    groupValBy d key into g
    select (g.Key)
    }