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#. Это ссылка: