сгруппировать несколько столбцов в запросе F # 3.0 - PullRequest
37 голосов
/ 28 декабря 2011

Просто попробуйте 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

И я понимаю, что мой вопрос похожна " Entity Framework и анонимные типы в F # ", но, похоже, он сфокусирован на powerpack / F # 2.x, и я надеюсь, что F # 3.0 имеет элегантный ответ ... Есть идеи?

ОБНОВЛЕНИЕ:

Я наткнулся на атрибут 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)
}

К сожалению, я получаю точно такое же исключение.

Ответы [ 8 ]

3 голосов
/ 30 мая 2013

Ниже приведен пример нескольких столбцов, используемых для группировки в c # и преобразования в f # (чрезмерно параноидальное управление заставило меня переименовать все, но я считаю, что я был последовательным):

(была создана база данныхпо 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 голос
/ 30 мая 2012

Я вижу это в первой из ваших ссылок, думаю, это то, что вы хотите:

query {
    for student in db.Student do
    groupValBy student.Name student.Age into g
    select (g, g.Key, g.Count())
}
0 голосов
/ 10 апреля 2019

Очевидно, что может быть решение, указанное в, с использованием комбинации groupValBy и AnonymousObject<_,_> из Linq RuntimeHelpers .

ex.

query {
    for d in context.table do
    let key = AnonymousObject<_,_>(d.col1,d.col2)
    groupValBy d key into g
    select (g.Key)
    } 
0 голосов
/ 06 декабря 2013
//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)
    }
0 голосов
/ 22 марта 2012
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())
    } 
0 голосов
/ 07 марта 2012
query {  
    for d in context.table do  
    groupBy (new {d.col1, d.col2}) into g  
    select (g.Key) 
}
0 голосов
/ 08 февраля 2012

При использовании groupBy вам нужно выбрать функцию агрегирования (например, count, sum, avg ...).

0 голосов
/ 07 февраля 2012

Сначала вы должны помнить, что запрос в какой-то момент переводится в фактический 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
}

Пожалуйста, пощадите меня, если синтаксис не на 100%, поскольку F # не является моим родным языком :) Однако концепция должна работать очень хорошо.

...