Which of these LINQ queries performs better? - performance

In the context of Entity Framework, which of these queries performs better?
var result = Items.OrderByDescending(t => t.Created)
.FirstOrDefault(a => a.ID == ItemID)?.ItemName;
or
var result = Items.Where(a => a.ID == ItemID)
.OrderByDescending(t => t.Created)
.FirstOrDefault()?.ItemName;
Is there anywhere I can read about how these LINQ queries are converted to SQL in this case.

Related

Where in clause using linq

trying to convert a query which has 2 levels of where in clauses to linq and getting some errors. Can anybody help me on this?
Original Query:
select id
from student
where suId
in (select suId
from subjects
where cid
in (select id
from chapters
where chapter='C203'))
LINQ query:
var query = (from s in dc.students
let subs = (from su in dc.subjects
where su.cid == Convert.ToInt32(from c in dc.Chapters
where c.chapter == 'Ç203'
select c.id) //Single chapter id will be returned
select su.suid)
where subs.Contains(s.sid)
select s.id).ToArray();
Am getting below 2 errors while compiling app
'System.Linq.IQueryable' does not contain a definition for 'Contains' and the best extension method overload 'System.Linq.ParallelEnumerable.Contains(System.Linq.ParallelQuery, TSource)' has some invalid arguments
Instance argument: cannot convert from 'System.Linq.IQueryable' to 'System.Linq.ParallelQuery'
Since Linq is lazy-loading everything you don't need to cram everything into a single statement; you can do something like this:
var chapterIds = dc.Chapters
.Where(c => c.Chapter == "C023")
.Select(c => c.Id);
var subjectIds = dc.Subjects
.Where(s => chapterIds.Contains(s.Cid))
.Select(s => s.Suid);
var students = dc.Students
.Where(s => subjectIds.Contains(s.Suid))
.Select(s => s.Sid)
.ToArray();
This way you can debug each subquery by looking at what it returns.
However, looking at your original select you can rewrite the whole thing as a Join and get rid of the bugging issue:
var students = dc.Chapters.Where(c => c.Chapter == "C023")
.Join(dc.Subjects,
c => c.Id,
s => s.Cid,
(chapter, subject) => subject)
.Join(dc.Students,
subj => subj.Suid,
student => student.Suid,
(subj, st) => st.Sid)
.ToArray();

Errors with .Any and .Contains for Linq query for Oracle

I am trying to create the following code in LINQ. I am using Linqpad to test my code but keep getting the error:
"NotSupportedException: Constructing or initializing instances of the type <>f__AnonymousType02[LINQPad.User.RNIS_MAINTBOUNDARY_V,System.Linq.IQueryable1[System.Int32]] with the expression http://localhost:14722/WCFDataservice.svc/RNIS_DRE_AREA_V is not supported."
My code is as below:
var roads =
from d in RNIS_MAINTBOUNDARY_V
let ts2 =
from dretab in RNIS_DRE_AREA_V
select dretab.DRE_CODE
where ts2.Contains(d.AUTH_ID)
select d;
roads.Dump();
For reference, the 2 fields for the join, namely auth_id and dre_code are both Oracle number fields. Why do I get this error?
Your LINQ query gets translated into the chain of method calls
var roads = RNIS_MAINTBOUNDARY_V
.Select(d => new { d, ts2 = RNIS_DRE_AREA_V.Select(dretab => dretab.DRE_CODE) })
.Where(τ0 => τ0.ts2.Contains(τ0.d.AUTH_ID))
.Select(τ0 => τ0.d);
any by the exception it looks like LINQ to Entities doesn't like the { d, ts2 = ... } anonymous type.
Try just moving the inner query into the lambda:
var roads = RNIS_MAINTBOUNDARY_V
.Where(d => RNIS_DRE_AREA_V.Select(dretab => dretab.DRE_CODE).Contains(d.AUTH_ID));
LINQ to Entities might not like this either, so you could try a real join:
var roads = RNIS_MAINTBOUNDARY_V
.GroupJoin(
RNIS_DRE_AREA_V,
d => d.AUTH_ID,
dretab => dretab.DRE_CODE
(d, dretabs) => new { d, count = dretabs.Count() })
.Where(p => p.count > 0)
.Select(p => p.d);

Entity Framework LINQ Multiple Column Count(Distinct)

I'm not entirely sure how to translate the following SQL into LINQ Fluent API. Any help would be great
select count(distinct thread.Id) as ThreadCount, count(distinct segment.Id) as SegmentCount
from Segment
inner join SegmentCommunicator as SegmentCommunicator
on Segment.Id = SegmentCommunicator.SegmentId
inner join Thread
on Thread.Id = Segment.ThreadId
where SegmentCommunicator.CommunicatorId in (94, 3540, 6226, 10767, 20945)
Currently, I know how to do this in 2 queries but for the life of me I can't figure out how to condense down into one. Any help would be much appreciated
var aggregate1 = _threadProvider
.AsQueryable()
.SelectMany(t => t.Segments)
.Where(s => s.SegmentCommunicators.Any(sc => communicatorIds.Contains(sc.CommunicatorId)))
.Select(s => s.ThreadId)
.Distinct()
.Count();
var aggregate2 = _threadProvider
.AsQueryable()
.SelectMany(t => t.Segments)
.Where(s => s.SegmentCommunicators.Any(sc => communicatorIds.Contains(sc.CommunicatorId)))
.Select(s => s.Id)
.Distinct()
.Count();
You can use one "base" query but the two distinct counts will be separate queries, hydrating the query once with as small of a data set as possible:
var query = _threadProvider
.AsQueryable()
.SelectMany(t => t.Segments)
.Where(s => s.SegmentCommunicators.Any(sc => communicatorIds.Contains(sc.CommunicatorId)))
.Select(s => new {s.ThreadId, s.Id})
.Distinct()
.ToList();
var aggregate1 = query.Select(s => s.ThreadId)
.Distinct()
.Count();
var aggregate2 = query.Select(s => s.Id)
.Distinct()
.Count();
You might be able to use GroupBy to do it in one query:
var query = _threadProvider
.AsQueryable()
.SelectMany(t => t.Segments)
.Where(s => s.SegmentCommunicators.Any(sc => communicatorIds.Contains(sc.CommunicatorId)))
.GroupBy(s => 1)
.Select(g => new
{
ThreadCount = g.Select(s => s.ThreadId.Distinct().Count()),
SegmentCount = g.Select(s => s.Id.Distinct().Count()),
});
but I doubt that the underlying query provider will support it (at best it will turn it into two sub-queries).
Note that neither query will likely perform as fast as the raw SQL, since SQL can optimize the query before returning the results to Linq.

Linq expression for left join and filter for the inner table

I want to know how to make Linq expression that has the same effect as these SQL query
SELECT item.*, priceforitem.*
FROM
item
LEFT JOIN priceforitem
ON priceforitem.ItemID = item.ItemID
AND priceforitem.PriceID = ?PriceID
I already make it using the Method query but I don't know if it will produce the same result
db.Items
.GroupJoin(
db.PriceForItems.Where(pi => pi.PriceID == id),
i => i.ItemID,
pi => pi.ItemID,
(i, pi) => new { Item = b, Prices = pi })
.SelectMany(
a => a.Prices.DefaultIfEmpty(),
(i, pi) => new
{
ItemID = i.Item.ItemID,
Code = i.Item.Code,
Name = i.Item.Name,
PriceForItemID = pi.PriceForItemID,
Price = pi.Price
})
and then after thinking for awhile i shorten it like this
db.Items
.SelectMany(
i => db.PriceForItems.Where(
pi => pi.PriceID == id
&& pi.ItemID = i.ItemID).DefaultIfEmpty(),
(i, pi) => new
{
ItemID = i.Item.ItemID,
Code = i.Item.Code,
Name = i.Item.Name,
PriceForItemID = pi.PriceForItemID,
Price = pi.Price
})
I am new to Linq, and I don't know which is better and how to convert it to Linq query statement.
First of all your sql query. It is effectively and inner join because the where condition will filter out all rows where data from priceforitem is null. If you do want to convert same query to linq you can do it like
from i in db.Items
join p in db.PriceforItems on
i.ItemId equals p.ItemId into tempvals
from t in tempvals.DefaultIfEmpty()
where t.PriceId == id
select new{i.ItemId, ..., t.PriceId, t...., t....}
I mostly write linq queries instead of expressions where they are more readable to me. If you still want to get an expression, you can write a valid linq query and paste it into Linqpad and it will give the result as well as lambda expression of your query.

What is the lambda equivalent of this Linq query?

What is the equivalent lambda syntax to this linq query?
Dim query = From t In _rdsqlconn.Tags Where t.TagWord = tag
Join p In _rdsqlconn.Posts On t.PostId Equals p.PostId Order By p.PostDatePublished
Descending Select p Where p.PostIsPublished = True
You can do this with a join, like so:
_rdsqlconn.Tags
.Where(t => t.TagWord == tag)
.Join(_rdsqlconn.Posts, t => t.PostId, p => p.PostId, (t, p) => p)
.Where(p => p.PostIsPublished == true)
.OrderByDescending(p => p.PostDatePublished)
but what you want to do is properly map your tables and relationships in the LINQ-to-SQL designer, and then you can use
_rdsqlconn.Posts.Where(p => p.PostIsPublished && p.Tags.Any(t => t.TagWord == tag))
.OrderByDescending(p => p.PostDatePublished)
If you have a foreign key between Posts and Tags in your database then you will be able to do this. It's much cleaner code, and removes the unnecessarily Join operator.

Resources