How can I get F# to do the equivalent of
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
in a query expression? I've come up with
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, ???) }
but I can't figure out what to insert into "???". To make things worse, the "score" column can be null, which complicates the math.
Alternatively, is there an easier way to do this? I'm not very familiar with .NET database access. Ideally, I'd just give it a block of SQL, it would parse it, and spit back some typed data. As it is, trying to figure out the not-SQL syntax for straightforward SQL is pretty frustrating.
The translation to SQL can generally deal better with C#-style LINQ operations than with native F# functions. So it is easier to go with Select and Average extension methods than with standard F# functions like Seq.map and Seq.average.
I tried writing a similar grouping using a sample Northwind database - I did not find nice tables to join, but the following does basic aggregation with CASE and works fine:
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
It generates a query that is a bit more complicated, but looks right (and uses CASE on the SQL side). You can see that by setting db.DataContext.Log <- System.Console.Out.
Another thing that should generally work would be to use nested query, but I have not tried that.
Some years now, but never too late? Is this of any help? In particular, note the use of FirstOrDefault. Sorry for the Norwegian names, but that's not important. The "x" demonstrates access to the first table.
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) }
}
I first ended up here when googling. Since it didn't help, I googled equivalent solutions in C#, got a hit on SO, got that to work for my case in C#, then in F#. This is the link:
LINQ: combining join and group by
Related
I am trying to use the F# Dapper wrapper at https://github.com/Dzoukr/Dapper.FSharp
It says , on the page:
Dapper.FSharp will map each joined table into separate record and return it as list of 'a * 'b tuples
But it doesn't. For the following code, I get a record for each order and each row of the order detail table ( i.e. no "Master detail"). am I doing something wrong or am I not understanding this correctly. I am really struggling with ANY of the F# data access libraries to find one that works well in F#
let orders =
select {
table "Orders"
leftJoin "OrderDetails" "OrderId" "Orders.Id"
where ( eq "Orders.ReviewSent" false +
gt "Orders.OrderDate" startDate +
lt "Orders.OrderDate" endDate
)
}
|> conn.SelectAsync<Order, OrderDetail>
I need to process the query at the bottom of this post in C#. The query works, but I don't know how to use it in EF6. I used a method and a viewmodel for it (variable query = the query below). But when it encounters null values in the OUTER JOIN, int32 cant accept this value when calling .toList(). What's the best way to deal with it?
var result = context.Database.SqlQuery<TourQueryViewModel>(query);
var reslist = result.ToList();
I tried my first steps with LINQ, but I dont get it how to translate into LINQ itself or the query-methods, that are equivalent to it. So I am hoping for your help.
SELECT toursdata.TourId AS TourId, toursdata.Tourname AS Tourname,toursdata.Tourdate Tourdate,
toursdata.VehicleId AS VehicleId, toursdata.VehicleName AS VehicleName, toursdata.LicenseNumber AS LicenseNumber,
Employees.EmployeeId AS EmployeeId, Employees.Gender AS Gender, Employees.Forename AS Forename, Employees.Surname AS Surname
FROM (
SELECT te.TourId, te.Tourname, te.Tourdate,
Vehicles.VehicleId, Vehicles.VehicleName, Vehicles.LicenseNumber,
TourEmployees.EmployeeId
FROM TourEmployees RIGHT OUTER JOIN Tours AS te ON TourEmployees.TourId = te.TourId,
Tours AS tv INNER JOIN Vehicles ON tv.VehicleId = Vehicles.VehicleId
WHERE tv.TourId = te.TourId
) toursdata
LEFT OUTER JOIN Employees ON toursdata.EmployeeId = Employees.EmployeeId
To eliminate the null problem, I changed the data type of the corresponding entity-attribute to a nullable-type
int turned to int?.
Didnt know about that language feature.
I'm wondering if anyone is able to shed light on why this won't work for me. I am trying to run a standard sql query in my ASP MVC application and mostly this is fine. But for some reason when I try to use the 'like' predicate with wildcards it doesn't return any results, it's as if it is using the ? parameter as a value instead of populating with the search value. For example :-
This query returns results without the wildcard included.
SELECT * FROM LOCATIONS L INNER JOIN ITEMDETAILS IT ON IT.LOCNUMBER = L.LOCNUMBER WHERE L.CLIENTNUMBER = ? AND IT.[DESC] LIKE ?;
This query doesn't return any results when I add the wildcard around the parameter value, but no idea why?
SELECT * FROM LOCATIONS L INNER JOIN ITEMDETAILS IT ON IT.LOCNUMBER = L.LOCNUMBER WHERE L.CLIENTNUMBER = ? AND IT.[DESC] LIKE '%?%';
Thanks for your help as always.
I am trying to use the Entity Framework in my ASP MVC 3 site to bind a Linq query to a GridView datasource. However since I need to pull information from a secondary table for two of the fields I am getting the error
LINQ to Entities does not recognize the method 'System.String Join(System.String, System.Collections.Generic.IEnumerable'1[System.String])' method, and this method cannot be translated into a store expression.
I would like to be able to do this without creating a dedicated view model. Is there an alternative to using String.Join inside a Linq query?
var grid = new System.Web.UI.WebControls.GridView();
//join a in db.BankListAgentId on b.ID equals a.BankID
var banks = from b in db.BankListMaster
where b.Status.Equals("A")
select new
{
BankName = b.BankName,
EPURL = b.EPURL.Trim(),
AssociatedTPMBD = b.AssociatedTPMBD,
FixedStats = String.Join("|", from a in db.BankListAgentId
where a.BankID == b.ID &&
a.FixedOrVariable.Equals("F")
select a.AgentId.ToString()),
VariableStats = String.Join("|", from a in db.BankListAgentId
where a.BankID == b.ID &&
a.FixedOrVariable.Equals("V")
select a.AgentId.ToString()),
SpecialNotes = b.SpecialNotes,
};
grid.DataSource = banks.ToList();
grid.DataBind();
If you're not overly worried about performance (since it has subqueries, it may generate n+1 queries to the database, and if the database rows are large, you may fetch un-necessary data), the simplest fix is to add an AsEnumerable() to do the String.Join on the web/application side;
var banks = (from b in db.BankListMaster
where b.Status.Equals("A") select b)
.AsEnumerable()
.Select(x => new {...})
At the point of the call to AsEnumerable(), the rest of the Linq query will be done on the application side instead of the database side, so you're free to use any operators you need to get the job done. Of course, before that you'll want to filter the result as much as possible.
HI,
I have 3 tables: Clips, Books and relationships between ClipBook
Problem is:
i need get book that has bookID=4 with some clips i mean many-to-many
in simple text sql it will be something like this:
select * from Clips where clipID in (select clipID from ClipBook where bookID=4)
Question is:
How can i do this with Linq without operator Join of course
this could be a solution;
from cb in ClipBooks
where cb.BookID == 4
select cb.Clip;
or
ClipBooks.Where(cb => cb.BookId == 4).Select(cb => cb.Clip);
the Contains method is in Linq converted into the IN operator, example:
String[] cities = {"London", "Paris", "Madrid"};
from p in context.Person
where cities.Contains(p.Adress.City)
select p
is translated into a sql clause like: .. WHERE a.City in ('London', 'Paris', 'Madrid') where a is the Address table alias after the join with the Person table.
edit:
you could write a single query without a subquery, but this will be converted to a join most probably:
var clips = (from c in context.ClipBooks
where c.BookID == 4
select c.Clip).Distinct();
or
var clips = from c in context.Clip
where c.ClicBooks.Any( cb => cb.BookID == 4)
select c
Any is translated in Exists()