F# запрос для объединения / группы / агрегата?

Как я могу заставить F# сделать эквивалент

select a.id, avg(case when a.score = b.score then 1.0 else 0.0 end)
from table1 a join table2 b on a.id = b.id and a.date = b.date
group by a.id

в выражении запроса? Я придумал

query {
    for a in db.table1 do
    join b in db.table2 on ((a.id, a.date) = (b.id, b.date))
    groupBy a.id into g
    select (g.Key, ???) }

но я не могу понять, что вставить в "???". Что еще хуже, столбец "Score" может быть нулевым, что усложняет математику.

В качестве альтернативы, есть ли более простой способ сделать это? Я не очень знаком с доступом к базе данных.NET. В идеале я бы просто дал ему блок SQL, он бы его проанализировал и выплюнул некоторые типизированные данные. На самом деле, попытка выяснить синтаксис не-SQL для простого SQL довольно сложно.

2 ответа

Перевод на SQL обычно лучше справляется с операциями LINQ в стиле C#, чем с собственными функциями F#. Так легче идти с Select а также Average методы расширения, чем со стандартными функциями F#, такими как Seq.map а также Seq.average,

Я попытался написать похожую группировку, используя пример базы данных Northwind - я не нашел хороших таблиц для объединения, но следующее выполняет базовую агрегацию с CASE и отлично работает

open System.Linq

query {
    for p in db.Products do
    groupBy p.CategoryID.Value into g
    select (g.Key, g.Select(fun a -> 
      if a.UnitPrice.Value > 10.0M then 1.0 else 0.0).Average()) }
|> Array.ofSeq

Он генерирует запрос, который немного сложнее, но выглядит правильно (и использует CASE на стороне SQL). Вы можете увидеть это, установив db.DataContext.Log <- System.Console.Out,

Еще одна вещь, которая в целом должна работать, это использовать вложенный запрос, но я этого не пробовал.

Несколько лет назад, но никогда не поздно? Это какая-то помощь? В частности, обратите внимание на использование FirstOrDefault. Извините за норвежские имена, но это не важно. "Х" демонстрирует доступ к первой таблице.

type Result3 = { Aarsak: int; Beskrivelse: string; Antall: int; Varighet: Nullable<int> }
let query3 = query {
    for stopptid in dc.StoppTider do
    where (stopptid.DatoS = datoS && stopptid.SkiftNr = skiftNr)
    groupBy stopptid.Aarsak into g
    join stoppaarsak in dc.StoppAarsak on (g.FirstOrDefault().Aarsak.ToString() = stoppaarsak.Nr)
    select { Aarsak = g.Key; Beskrivelse = stoppaarsak.Norsk; Antall = g.Count(); Varighet = g.Sum(fun x -> x.Varighet) }
    }

Я впервые оказался здесь, когда гуглил. Так как это не помогло, я погуглил эквивалентные решения в C#, получил удар по SO, заставил это работать для моего случая в C#, а затем в F#. Это ссылка:

LINQ: объединение объединения и группировки по

Другие вопросы по тегам