37

F#3.0を試して、複数の列でグループ化することになると、ちょっとした壁に突き当たるだけです。明白なことは試したことでした

query {
    for d in context.table do
    groupBy (d.col1,d.col2) into g
    select (g.Key)
}

しかし、「LINQ to Entitiesでサポートされているのは、パラメータを持たないコンストラクタとイニシャライザのみです」。例外。

msdnに例が見つからないようです

http://msdn.microsoft.com/en-us/library/hh225374(v=vs.110).aspx

http://msdn.microsoft.com/en-us/library/hh361035(v=vs.110).aspx

そして私の質問は 「F#のエンティティフレームワークと匿名型「しかし、それはpowerpack / F#2.xに焦点を当てているようで、F#3.0には優雅な答えがあることを願っています...何かアイデアはありますか?

更新:

私はBrianの投稿を読んでCLIMutable属性に出会いました。

http://blogs.msdn.com/b/fsharpteam/archive/2012/07/19/more-about-fsharp-3.0-language-features.aspx

私はかなり楽観的だったので試しました

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

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

残念ながら私はまったく同じ例外を受けます。


  • あなたがタプルでグループ化しているのか、それともあなたが問題であるのかという問題です。選択する1?エラーは"出力"を参照しているようです。タイプ。たぶん選択g.Count()代わりに、何が起こるか見てください。 3.0を試していません...推測しているだけです。 - Daniel
  • 定数「select(1)」を選択してみました。そして同じことを得た - Jizugu
  • @ildjarn:g.Keyパラメータなしのコンストラクタを持たないタプルです。select 1私の推測が正しければ、しかし、動作するはずです。 - Daniel
  • @ダニエル:select g.Keyではない構築または初期化しかし、新しいタプルです。気が付く、select (g.Key)になりますTuple<Tuple<col1_t, col2_t>>これはのコンストラクタを呼び出していますTuple<'T1>;select g.KeyになりますTuple<col1_t, col2_t>新しい値を初期化するのではなく、既存の値を再利用します。もちろん、私は例外が原因で発生しているという前提で実行していますselectではなくgroupByこれはまったく間違っている可能性があります。 :-P - ildjarn
  • 括弧は単にグループ化するためのものです。それをタプルにするのはコンマです。要素が1つだけのタプルは(1、)として入力されます。 - Markus Jarderot

7 답변


3

以下は、c#でのグループ化に使用され、f#に変換された複数の列の例です(過度に妄想的な管理によってすべての名前が変更されましたが、一貫していたと思います)。

(TheDatabaseはSqlMetalによって生成され、GetSummedValuesResultはF#レコード型です)

c#

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();
    }
}

f#

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>

だから使うものはMicrosoft.FSharp.Linq.RuntimeHelpers.AnonymousObject

集約関数にSeqモジュールを使うべきではないことに注意してください。

SummedValues  = grouping |> Seq.sumBy (fun g -> g.SomeValues)

これは機能しますが、適切なSQLを定式化するのではなく、クライアント側で集計を行います。


1

私はあなたのリンクの最初にこれを見ます、私はそれがあなたが望むものであると思います:

query {
    for student in db.Student do
    groupValBy student.Name student.Age into g
    select (g, g.Key, g.Count())
}


  • groupValByは、「これまでに選択された各要素の値を選択し、指定されたキーで要素をグループ化する」と説明されています。重要なのは、この場合はまだstudent.Nameだけに基づいていることです - Jizugu
  • それで、なぜその学生がいますか。 FSharpで複数の列でグループ化する簡単な方法がないとは思われません。私が今結合で見ている同様のケース - 複合キーを書く方法? - tomasK
  • 実際にはこれだけでグループ化されますstudent.Age。最初の引数groupValBy格納される値です。2番目はグループ化するためのキーです。言葉を想像してみてby1番目と2番目の引数の間 - latkin

0

groupByを使用するときは、集計関数(count、sum、avgなど)を選択する必要があります。


  • この例は実際の説明のためだけのもので、select(g.Key)を使用しても意味がありませんが機能するはずです... "select(g.Count())";同じエラーを出します。 - Jizugu

0

まず、クエリが実際のクエリに変換されることを覚えておく必要があります。SQLある時点で。 linqは複数のグループキーの使用をサポートしていないようです。Tuple<>。したがって、への変換はTuple<>データベース呼び出しが完了した後に実行する必要があります。

第二に、あなたは達成することができるはずです複数キーのグループ化それぞれのキーで互いに背後にある複数のグループ化を実行することによって:

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

F#は私の母国語ではないので構文が100%ではない場合は、私と憐れんでください:)ただし、この概念はうまく機能するはずです。


  • 興味深いことに、LINQ to Entitiesでは「パラメータなしのコンストラクタとイニシャライザのみがサポートされています」と表示されます。 - Jizugu

0

open Microsoft.FSharp.Linq.RuntimeHelpers
open System.Linq

query {
    for d in context.table do
    let t = MutableTuple<_,_>(Item1=d.col1,Item2=d.col2)
    groupValBy d t into g
    select (g.Key,g.Count())
    } 


  • 動作しているようです。しかしselect(g.Key)以上のものを得るのに問題があります。 select(g.Count())でエラーが発生し、他の列で集計を実行する方法がわからない場合があります。まだアイデアで遊んで - Jizugu

0

//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)
    }


-1

query {  
    for d in context.table do  
    groupBy (new {d.col1, d.col2}) into g  
    select (g.Key) 
}


  • 質問者が理解しやすいようにコメントを追加してください。それ以外の場合、これは「魔法」です。 - Eric Fossum
  • new {d.col1, d.col2}C#の無名型を構築します。これは有効なF#ではありません。 - Jay

リンクされた質問


関連する質問

最近の質問