I have a snippet of Stored Procedure:
...
SELECT B.BinID, AverageCost, SUM(Qty) AS Qty
FROM #CurrentReturn R INNER JOIN Bins B ON R.BinCode = B.BinCode AND B.StoreroomID = #StoreroomID
...
#StorerroomID is one of the SP parameters.
Now I am trying to translate it into LINQ to Entities,
var AverageCostList = from r in CurrentReturn
join b in BinQuery on new {r.BinCode, b.StoreroomID} equals new {b.BinCode, storeroomID}
It does not work, as the type on the L.H.S. of equals cannot contains fields in b.
So is there any way to translate such an inner join SQL into LINQ?
i would put the B.StoreroomID = #StoreroomID comparison into ther where clause
from r in CurrentReturn
join b in BinQuery
on r.BinCode equals b.BinCode
where b.StoreroomID == storeroomID
Related
I'm trying to replicate the following SQL query in LINQ:
SELECT *
FROM Table1 AS D INNER JOIN Table2 AS DV ON D.Table1Id = DV.Table1Id
INNER JOIN Table3 AS VT ON DV.Table3Id = VT.Table3Id
INNER JOIN Table4 AS C ON DV.CurrencyId = C.CurrencyId
INNER JOIN Table5 AS FP ON DV.DVDate BETWEEN FP.StartDate AND FP.EndDate
INNER JOIN Table6 AS FX ON DV.CurrencyId = FX.FromCurrencyId AND FX.ToCurrencyId = 'USD' AND FX.FiscalPeriodId = FP.FiscalPeriodId
This is what I have in LINQ:
from d in db.Table1
join dv in db.Table2 on d.Table1Id equals dv.Table1Id
join vt in db.Table3 on dv.Table3Id equals vt.Table3Id
join c in db.Table4 on dv.CurrencyId equals c.CurrencyId
join fp in db.Table5 on dv.DVDate >= fp.StartDate && dv.DVDate <= fp.EndDate //error on this line
join fx in db.Table6 on dv.CurrencyId equals fx.FromCurrencyId && fx.ToCurrencyId equals "USD" && fx.FiscalPeriodId equals fp.FiscalPeriodId //error also on this line
The last two joins to fp and fx are the problem but it's not clear to me what's wrong, it doesn't seem to like && but there's no and keyword like there is an equals that replaces =.
I've removed the select portion from LINQ as it's not relevant to the problem and I'd like to avoid spending more time obfuscating table and field names.
"A join clause performs an equijoin. In other words, you can only base matches on the equality of two keys. Other types of comparisons such as "greater than" or "not equals" are not supported. To make clear that all joins are equijoins, the join clause uses the equals keyword instead of the == operator. "
reference: https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/join-clause
you need to do this in the where clause. Like here:
https://stackoverflow.com/a/3547706/3058487
To do a join using composite keys, you need to do something like here:
new { dv.CurrencyId, fp.FiscalPeriodId } equals new { CurrencyId = fx.ToCurrencyId, fx.FiscalPeriodId }
Reference:
https://learn.microsoft.com/en-us/dotnet/csharp/linq/join-by-using-composite-keys
So I have a SQL query that I would like to convert to LINQ.
Here is said query:
SELECT *
FROM DatabaseA.SchemaA.TableA ta
LEFT OUTER JOIN DatabaseA.SchemaA.TableB tb
ON tb.ShipId = ta.ShipId
INNER JOIN DatabaseA.SchemaA.TableC tc
ON tc.PostageId= tb.PostageId
WHERE tc.PostageCode = 'Package'
AND ta.MailId = 'Specification'
The problem I am struggling with is I cannot seem to figure out how to do a left join in LINQ before an inner join, since doing a left join in LINQ is not as clear to me at least.
I have found numerous examples of a LINQ inner join and then a left join, but not left join and then inner join.
If it helps, here is the LINQ query I have been playing around with:
var query = from m in tableA
join s in tableB on m.ShipId equals s.ShipId into queryDetails
from qd in queryDetails.DefaultIfEmpty()
join p in tableC on qd.PostageId equals p.PostageId
where m.MailId == "Specification" && p.PostageCode == "Package"
select m.MailId;
I have tried this a few different ways but I keep getting an "Object reference not set to an instance of an object" error on qd.PostageId.
LINQ is very new to me and I love learning it, so any help on this would be much appreciated. Thanks!
From my SQL conversion recipe:
JOIN conditions that aren't all equality tests with AND must be handled using where clauses outside the join, or with cross product (from ... from ...) and then where
JOIN conditions that are multiple ANDed equality tests between the two tables should be translated into anonymous objects
LEFT JOIN is simulated by using into joinvariable and doing another from from the joinvariable followed by .DefaultIfEmpty().
The order of JOIN clauses doesn't change how you translate them:
var ans = from ta in TableA
join tb in TableB on ta.ShipId equals tb.ShipId into tbj
from tb in tbj.DefaultIfEmpty()
join tc in TableC on tb.PostageId equals tc.PostageId
where tc.PostageCode == "Package" && ta.MailId == "Specification"
select new { ta, tb, tc };
However, because the LEFT JOIN is executed before the INNER JOIN and then the NULL PostageIds in TableB for unmatched rows will never match any row in TableC, it becomes equivalent to an INNER JOIN as well, which translates as:
var ans2 = from ta in tableA
join tb in tableB on ta.ShipId equals tb.ShipId
join tc in tableC on tb.PostageId equals tc.PostageId
where tc.PostageCode == "Package" && ta.MailId == "Specification"
select new { ta, tb, tc };
Use:
var query = from m in tableA
join s in tableB on m.ShipId equals s.ShipId
join p in tableC on s.PostageId equals p.PostageId
where m.MailId == "Specification" && p.PostageCode == "Package"
select m.MailId;
Your query uses a LEFT OUTER JOIN but it doesn't need it.
It will, in practice, function as an INNER JOIN due to your tc.PostageCode = 'Package' clause. If you compare to a column value in a table in a WHERE clause (and there are no OR clauses and you aren't comparing to NULL) then effectively all joins to get to that table will be treated as INNER).
That clause will never be true if TableB is null (which is why you use LEFT OUTER JOIN vs INNER JOIN) - so you should just use an INNER JOIN to make the problem simpler.
I have a query that's something like this.
Select a.*
from table1 a
inner join table2 b on a.field1 = b.field1
inner join table3 c on b.field2 = c.field2
where b.field4 = beta and c.field5 = gamma.
On LINQ, I tried to do that this way:
var query = (from a in table1
join b in table2 on a["field1"] equals b["field1"]
join c in table3 on b["field2"] equals c["field2"]
where (b["field4"] == beta && c["field5"] == gamma)
select a).ToList();
But for some reason, when I try to do this I get an error that says that the entity "table2" doesn't have the field Name = "field5", as though as the where clause was all about the last joined table and the other ones were unaccessible. Furthermore, the compiler doesn't seem to notice neither, because it lets me write c["field5"] == gamma with no warning.
Any ideas? Am I writing this wrong?
Thanks
See these links:
How to: Perform Inner Joins (C# Programming Guide)
What is the syntax for an inner join in linq to sql?
Why you don't create View in database, and Select your data from View in LINQ?
I need translate next sql to linq is it possible? That will have approximately the same speed
SELECT Count(tblcollectionimage.lngimageid),
tblcollectiontree.lngcollectionid,
tblcollection.txtname
FROM (tblcollectiontree
LEFT JOIN tblcollectionimage
ON blcollectiontree.lngcollectionid =
tblcollectionimage.lngcollectionid)
JOIN tblcollection
ON tblcollectiontree.lngcollectionid = tblcollection.lngcollectionid
WHERE lngcollectionparentid = 0
GROUP BY tblcollectiontree.lngcollectionid,
tblcollection.txtname
I have currently such linq but it doesn't work.
var results =(from collection in dataBase.tblcollections
join collectionTree in dataBase.tblcollectiontrees on
collection.lngcollectionid equals collectionTree.lngcollectionid
into generalCollections
from generalCollection in generalCollections
join images in dataBase.tblcollectionimages on
collection.lngcollectionid equals images.lngcollectionid
into generalCollectionImages
from generalCollectionImage in
generalCollectionImages.DefaultIfEmpty()
group generalCollectionImage by
generalCollectionImage.lngcollectionid into hello
from hellos in hello.DefaultIfEmpty()
join collection in dataBase.tblcollections on
hello.Key equals collection.lngcollectionid
select new
{
id = hello.Key,
name = hello.Count()
}).ToList();
T-SQL:
declare #postlocations table (locationid int)
insert into #postlocations
select locationid
from dbo.PostLocations
where PostId = 162172
select t.*
from dbo.Themes t
inner join dbo.ThemeLocations tl on t.ThemeId = tl.ThemeId
inner join #postlocations pl on tl.LocationId = pl.locationid
LINQ-Entities i have so far:
var postLocations = e.SomePost.Locations; // pre-fetched, e.g materialized ICollection<Post>
var themes = (from t in db.Themes
join q in postLocations on t.Locations.Select(l => l.LocationId) equals q.LocationId
select t).ToList();
But the compiler is complaining on the join keyword about not being able to infer the type arguments.
Any ideas?
I don't think you can join a SQL table with an in-memory list of objects, even if those objects are originally from the database.
Convert the in-memory list of objects to a list of id's (integer), and use that in the join or in a Contains/sub-select. EF can translate the list of id's to parameters when generating the SQL.
The problem with your join is that you're implying a collection of LocationId (t.Locations.Select(l => l.LocationId) can equal a single LocationId. You're trying to join a Theme which has a collection of Locations onto a single Location.
You should be able to fix this by using Contains
var themes = (from t in db.Themes
join q in postLocations
on t.Locations.Select(l => l.LocationId).Contains(q.LocationId)
select t).ToList();
or if EF complains about passing a postLocations as a parameter, you can try
// I'd materialize this but you may not have to
var postLocationIds = postLocations.Select(p => p.LocationId).ToList();
var themes = db.Themes.Where(t => t.Locations.Any(l =>
postLocationIds.Contains(l.LocationId))).ToList();
Edit
how about this
///your sql query
select t.* from dbo.Themes t
inner join dbo.ThemeLocations tl on t.ThemeId = tl.ThemeId
inner join #postlocations pl on tl.LocationId = pl.locationid
//linq query for that
from t in teams
join from tl in teamlocation on t.themid = tl.ThemeID
join from pl in postlocation on tl.temeid = pl.temeid
select t;
Org
Not sure but you can try out by using let keyword
var themes = (from t in db.Themes
let location = t.Locations
join q in postLocations on location.LocationId equals q.LocationId
select t).ToList();