Linq entity look ahead to value in next row - c#

When querying my entity framework DB, I am trying to match a condition based on the values from 2 rows.
My current query looks like this:
var query = context.table.where(x => x.row > 2);
This all works fine.
What I now want to achieve is to query the table based on the value in current and next row eg:
var query = context.table.where(x => x.row > 2 && x.row[next row up in DB] < 2);
Can this be done.
I know I can achieve this in code, but can it be done in a single query using LINQ and entity?
Here is an example of how I would do this with SQL:
SELECT *
FROM t_Table p
INNER JOIN t_Table f
ON (p.id + 1) = f.id
WHERE p.column = whatever
AND f.column = whatever2

Translating your sample SQL into LINQ to SQL:
var ans = from p in t_table
from f in t_table
where (p.id+1) == f.id && p.column == whatever && f.column == whatever2
select new { p, f };
This does not appear to generate an inner join in SQL but rather a cross-join, but I assume the SQL engine will handle it appropriately. Note that LINQ can only do equi-joins.
I didn't realize you can do (some) expressions in LINQ joins as long as equal is the primary operator, and this generates a sub-select and an inner join, which seems quite a bit faster:
var ans = from p in t_table
where p.column == whatever
let pidplus1 = p.id+1
join f in t_table on pidplus1 equals f.id
where f.column == whatever2
select new { p, f };

Related

LINQ Select newest records that have distinct ForeignKeyId column

I have the following SQL query:
SELECT
table1.Id AS EinAusgangId,
table1.Ausgabedatum,
table1.Rueckgabedatum,
table1.WerkzeugId,
cpmWerkzeug.Name
FROM cpmEinAusgang AS table1
INNER JOIN cpmWerkzeug ON table1.WerkzeugId = cpmWerkzeug.Id
WHERE table1.Id = (SELECT MAX(Id) AS Expr1
FROM dbo.cpmEinAusgang
WHERE table1.WerkzeugId = WerkzeugId)
My aim is to convert the whole query into a LINQ statement for further use in a .Net Application. I already converted joined tables to LINQ but is it also possible to use a select in the where clause?
This is what I got so far, which gives me almost the same result as the SQL statement above, but has major errors when the table cpmEinAusgang contains more then one record for one cpmWerkzeug
using (var dbContext = new cpmEntities())
{
var werkzeuge = from w in dbContext.cpmWerkzeug
join e in dbContext.cpmEinAusgang
on w.Id equals e.WerkzeugId
where e.Rueckgabedatum == null
orderby w.Name
select w;
return werkzeuge.ToList();
}
Has anyone an idea how to achieve the above sql in linq?
Thanks for your help. :)
EDIT:solved (see below)
var werkzeugeImUmlauf = from w in dbContext.cpmWerkzeug
join e in dbContext.cpmEinAusgang
on w.Id equals e.WerkzeugId
where e.Id == dbContext.cpmEinAusgang.Where(x => x.WerkzeugId == e.WerkzeugId).Max(x => x.Id) select w;
This is the final solution. As mentioned by Mittal in his answer, it is possible to write a sub-query in LINQ.
Yes, you can write Sub Query in LINQ as well.
var werkzeuge = from w in dbContext.cpmWerkzeug
join e in dbContext.cpmEinAusgang
on w.Id equals e.WerkzeugId
where w.id = (dbContext.cpmEinAusgang.Max(x => x.id)) AND w.WerkzeugId = WerkzeugId

C# SQL/Linq/Entity Framework calculating column totals for multiple columns from large data source

Sorry to bother you, however I'm having issues converting my SQL Query into C# Entity Framework.
My SQL query is as follows:
SELECT CAST(ROUND(sum(size/rate), 0) AS INT) s,
CAST(ROUND(sum(PL/rate), 0) AS INT) PL
FROM [bs].[b] b
join [bs].[s] s on b.id = s.b_id
join [bs].[o] o on s.o_id = o.id
join [bs].[a] a on o.a_id = a.id
join [fs].[f] f on b.f_id = f.id
where f.r_date
between '2013-05-01 00:00:00.000'
and '2013-05-31 00:00:00.000'
and s.deleted_at is NULL
and b.group_id = '0'
and (o.a_id = 50 or o.a_id = 52)
I have in turn managed to get all the joins done and where statement in place (a.k.a. 'The Easy Bit') however I just cannot find a way to get those sums for the column totals to work.
This is what I have in place so far:
var GroupSk = (from Bs in sb.b
join S in sb.s on Bs.id equals S.b_id
join O in sb.o on S.o_id equals O.id
join A in sb.a on O.a_id equals A.id
join Fs in sb.vw_f on Bs.f_id equals Fs.f_id
where Fs.r_date >= t_FromDate && Fs.r_date <= t_ToDate
where S.deleted_at == null
where Bs.group_id == 0
where O.a_id == 50 || O.a_id == 52
select new {
As you can see, it's everything up until the SUM part of the query.
This query can return anywhere from 1-150000 rows, and I need a way to ensure that the column totals I get back are returned in a timely manner.
I had originally planned on using a ForEach loop but had trouble implementing it (along with the fact that it'll probably take a LONG time if a larger number of rows are returned).
I'm aware there are a few 'sum column total' questions out there, however they don't deal with multiple tables and multiple column outputs. They also appear to be limited to 2 or 3 columns total, whereas my tables far exceed that.
Any & all help would be greatly appreciated.
It's a bit of a hack, but it works. The trick is to make one group containing all items and then do the sums over the group:
var GroupSk = (from Bs in sb.b
join S in sb.s on Bs.id equals S.b_id
join O in sb.o on S.o_id equals O.id
join A in sb.a on O.a_id equals A.id
join Fs in sb.vw_f on Bs.f_id equals Fs.f_id
where Fs.r_date >= t_FromDate && Fs.r_date <= t_ToDate
where S.deleted_at == null
where Bs.group_id == 0
where O.a_id == 50 || O.a_id == 52
select new { r1 = ??.size / ??.rate, r2 = ??.PL / ??.rate })
.GroupBy(x => 0)
.Select(g => new {
R1 = g.Sum(x => x.r1),
R2 = g.Sum(x => x.r2)
});
I put ?? marks where I didn't know the origin of the properties, so you'll have to substitute the right variable names there. (Bs, S, O, A, Fs).
This will translate into one SQL query, so all the processing is done by the database engine and only the small result object is transferred over the wire.

Write sql query to linq

I am having following query in sql :
SELECT [definition],[pos]
FROM [WordNet].[dbo].[synsets]
where synsetid in(SELECT [synsetid] FROM [WordNet].[dbo].[senses]
where wordid = (select [wordid]FROM [WordNet].[dbo].[words]
where lemma = 'searchString'))
I had tried this for sql to linq :
long x = 0;
if (!String.IsNullOrEmpty(searchString))
{
var word = from w in db.words
where w.lemma == searchString
select w.wordId;
x = word.First();
var sence = from s in db.senses
where (s.senseId == x)
select s;
var synset = from syn in db.synsets
where sence.Contains(syn.synsetId)
select syn;
But I am getting following error at sence.Contains()
Error1:Instance argument: cannot convert from
'System.Linq.IQueryable<WordNetFinal.Models.sense>' to
'System.Linq.ParallelQuery<int>'
Below code:
var sence = from s in db.senses
where (s.senseId == x)
select s;
Returns object of type: WordNetFinal.Models.sense, but in where sence.Contains(syn.synsetId) you are trying to search in it syn.synsetId which is an integer.
So you should change above code to:
var sence = from s in db.senses
where (s.senseId == x)
select s.senseId;
x seems to be of Word type, which is not the type of Id (probably int or long).
You're comparing an entire sense row with a synsetId, which is not correct. You're also splitting the original query into two separate queries by using First() which triggers an evaluation of the expression so far. If you can live with not returning an SQL error if there are duplicates in words, you can write the query as something like this;
if (!String.IsNullOrEmpty(searchString))
{
var wordIds = from word in db.words
where word.lemma == searchString
select word.wordId;
var synsetIds = from sense in db.senses
where wordIds.Contains(sense.wordId)
select sense.synsetId;
var result = (from synset in db.synsets
where synsetIds.Contains(synset.synsetId)
select new {synset.definition, synset.pos}).ToList();
}
The ToList() triggering the evaluation once for the entire query.
You could also just do it using a simpler join;
var result = (from synset in db.synsets
join sense in db.senses on synset.synsetId equals sense.synsetId
join word in db.words on sense.wordId equals word.wordId
select new {synset.definition, synset.pos}).ToList();

linq to entities, a where in where clause? (inner where)

I have a table with a one to many mapping to a table that has a many to many mapping to another table. I'd like to do the following:
var results = context.main_link_table
.Where(l => l.some_table.RandomProperty == "myValue" &&
l.some_table.many_to_many_table
.Where(m => m.RandomProperty == "myValue"));
How can I achieve this? The first part will work but when trying it without the 'inner WHERE', I can't access the many_to_many_table's properties, but the "inner where" obviously won't compile. I basically want to achieve something like the following SQL query:
SELECT * from main_link_table
INNER JOIN some_table AS t1 ON t1.association = main_link_table.association
INNER JOIN many_to_many_table AS t2 ON t2.association = some_table.association
WHERE t1.RandomProperty = 'MyValue' AND t2.RandomProperty = 'MyValue'
It's seemingly simple but I can't find a way to achieve it in one single line of linq - using multiple lines to achieve the desired effect returns too much results and I end up having to loop through them. I also tried stuff like:
var results = main_link_tbl.Include("some_table.many_to_many_table")
.Where(l => l.some_table.many_to_many_table.<property>
== "MyValue")
But at this point I can't select a property of many_to_many_table unless I add a FirstOrDefault(), which nullifies the effect since it won't search through all the records.
What did work, but requires multiple lines of code and in the background returns too many results in the SQL query built by the linq-to-entities framework:
var results = db.main_link_table.Include("some_table")
.Include("some_table.many_to_many_table")
.Where(s => s.some_table.RandomProperty
== "myValue")
.Select(s => s.some_table);
foreach(var result in results) {
var match_data = result.Where(s => s.many_to_many_table.RandomProperty
== "myValue");
}
This piece of code will return all rows inside some_table that match the first Where condition and then applies the next Where condition, while I obviously only need a single row where the many_to_many_table.RandomProperty equals myValue.
It should work if you change the inner Where to Any:
var results = context.main_link_table
.Where(l => l.some_table.RandomProperty == "myValue" &&
l.some_table.many_to_many_table
.Any(m => m.RandomProperty == "myValue"));
If you want to do a join, why don't you just do a join?
var query = from main in context.MainLinks
join t1 in context.Some on main.Association equals t1.Association
where t1.RandomProperty == "MyValue"
join t2 in context.ManyToMany on t1.Association equals t2.Association
where t2.RandomProperty == "MyValue"
select new { main, t1, t2 };
That should achieve exactly what your SQL does...
from link in db.main_link_table
join s in db.some_table on link.association1 = s.association
join m in db.many_to_many_table on link.association2 = m.association
where s.X = 'MyValue' AND m.Y = 'MyValue'
select m; // or s or link or both 3 as you want

C# LINQ: How to stack LINQ queries correctly

I have a form that allows the user to perform a myriad of searches. The table(s) that need to be joined differ depending on the search criteria entered. (My example below is very simplistic because both tables use the same sub-tables to join on, but the actual problem is not as simple.)
I've been using a technique I call LINQ stacking, like this:
IQueryable<LogENT> results = Context.AssignedLogsENT.Where(l => l.AgencyId);
if(txtFirstName.Text != null)
results = from r in results
join a in Context.LogAssignmentsENT on r.DisplayLogId equals a.LogId
join p in Context.PersonsENT on a.ObjectId equals p.DisplayPersonId
&& !a.Deleted &&
p.FirstName.StartsWith(Object.FirstName)
select r;
if(txtLastName.Text != null)
results = from r in results
join a in Context.LogAssignmentsENT on r.DisplayLogId equals a.LogId
join p in Context.PersonsENT on a.ObjectId equals p.DisplayPersonId
&& !a.Deleted &&
p.LastName.StartsWith(Object.LastName)
select r;
So you see if a certain text field is set, I add to the query as necessary. This actually works fine, except that when I use SQL Profiler to view the generated query, it is INNER JOINing the tables each time I add a new criterion.
i.e. the LogAssignments table is included 3, 4, 5 times. Is there a way I can prevent it from JOINing the same table more than once?
Or, is there a better way I can do this? I've looked at Predicate Builder however it doesn't seem to permit joining tables, which is a requirement in my case.
Thanks!
IQueryable<LogENT> results = Context.AssignedLogsENT.Where(l => l.AgencyId);
results = from r in results
join a in Context.LogAssignmentsENT on r.DisplayLogId equals a.LogId
join p in Context.PersonsENT on a.ObjectId equals p.DisplayPersonId
&& !a.Deleted
select r;
if(txtFirstName.Text != null)
results = from r in results
p.FirstName.StartsWith(Object.LastName)
select r;
if(txtLastName.Text != null)
results = from r in results
p.LastName.StartsWith(Object.LastName)
select r;
If you use just one query, you could modify it something like this:
results = from r in results
join a in Context.LogAssignmentsENT on r.DisplayLogId equals a.LogId
join p in Context.PersonsENT on a.ObjectId equals p.DisplayPersonId
&& !a.Deleted &&
(txtFirstName.Text != null || p.FirstName.StartsWith(Object.FirstName)) &&
(txtLastName.Text != null || p.LastName.StartsWith(Object.LastName))
select r;
You can build your base result and then dynamically add the where clauses.

Categories

Resources