groupby multiple columns in a F# 3.0 query

前端 未结 8 833
梦如初夏
梦如初夏 2020-12-14 00:02

Just trying out F# 3.0 and hit a bit of a wall when it comes to grouping by multiple columns. The obvious thing to try was

query {
    for d in context.table         


        
相关标签:
8条回答
  • 2020-12-14 00:23

    First you have to remember that a query is translated into actual SQL at some point. It appears that linq does not support the use of multiple group keys as a Tuple<>. Therefore any transformation into Tuple<> has to be done after the database call has completed.

    Second, you should be able to achieve multiple key grouping by performing multiple groupings behind each other on the respective keys:

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

    Please have mercy with me if the syntax is not 100% since F# is not my native tongue :) The concept however should work just fine.

    0 讨论(0)
  • 2020-12-14 00:25
    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 讨论(0)
  • 2020-12-14 00:29

    Apparently there may be a solution as indicated in by using a combination of groupValBy and AnonymousObject<_,_> from the 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 讨论(0)
  • 2020-12-14 00:37
    query {  
        for d in context.table do  
        groupBy (new {d.col1, d.col2}) into g  
        select (g.Key) 
    }
    
    0 讨论(0)
  • 2020-12-14 00:42

    The following is an example of multiple columns being used for grouping in c# and converted to f# (overly paranoid management has made me rename everything, but I believe I have been consistent):

    (TheDatabase was generated by SqlMetal, GetSummedValuesResult is a F# record type)

    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>
    

    So the thing to use is Microsoft.FSharp.Linq.RuntimeHelpers.AnonymousObject

    Note that you should not use the Seq module for aggregation functions!!

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

    Although this WILL WORK, it does the aggregation on the client side, rather than formulating appropriate SQL.

    0 讨论(0)
  • 2020-12-14 00:47

    I see this in first of your links, I think it is what you want:

    query {
        for student in db.Student do
        groupValBy student.Name student.Age into g
        select (g, g.Key, g.Count())
    }
    
    0 讨论(0)
提交回复
热议问题