Complex Linq Query Update as DateTime - c#

There are A and B tables that are related to each other. I want to create a linq query that will update the Status value in the A table if the entire row of relationship lines with the AID column in the B table is equal to or smaller than today's date in the Date field.
For example, according to the table below, the Status values of the rows with ID value 1 (AAA) and 2 (BBB) in Table A will be 1. Its Status value will not change because the line with ID value 3 (CCC) is not smaller than the current date of all the related rows in the B table.
How can I write the most stable and performance linq query?
Today : 2018-7-10
A Table
ID Name Status
1 AAA 0
2 BBB 0
3 CCC 0
B Table
ID AID Date
6 1 2018-5-3
7 2 2018-6-2
8 2 2018-6-4
9 3 2018-10-12
10 3 2018-7-7

Grouping TableB on AID
Selecting the "Max" date in each group.(Each unique AID)
Compares the selected dates with the corresponding Id in Table A.
Sets the Status value to true if the date is less or equal to the current date.
TableB.GroupBy(x => x.AId).Select(group => new { identifier = group.Key, MaxDate = group.Max(m => m.Date) }).ToList().ForEach(y =>
{
if (y.MaxDate <= DateTime.Now.Date)
{
TableA.Where(g => g.Id == y.identifier).First().Status = true;
}
});

This will select AIDs from Table B where Date is samller than now.
we select records from table A where its ID is in List from
previous step
Then we update Status value
A.Where ( a => B.Where( b => b.Date <= DateTime.Now).Select(b => b.AID).Contains(a.ID)).ForEach( a => a.Status = 1 )

/*Fetching those aS Who meet the condition. */
var aList1=(from b in dbset.Bs.Where(x=>x.Date<DateTime.Now)//TimeZone may vary
join a in dbSet.As
on b.AID equals a.ID
select a);
/*Fetching those aS Who don't meet the condition. */
var aList2=(from b in dbset.Bs.Where(x=>x.Date>=DateTime.Now)//TimeZone may vary
join a in dbSet.As
on b.AID equals a.ID
select a);
/*Removing those aS from list1 which occured in list2 */
var aFinalList=(aList1.Except(aList2)).ToList();
/*Updating status */
aFinalList.ForEach(x=>x.Status=1);
aFinalList.SaveChanges();

You can use GroupJoin extension in Lambda to Join the A and B tables then use All extension with your condition (date <= Today or any condition) then update the Status. Something like,
var lstResult = lstA.GroupJoin(lstB, a => new { a.Id }, b => new { Id = b.AId }, (a, b) => new { a, b })
.Select(x =>
{
if (x.b.All(y => y.Date <= DateTime.Now)) //Actual condition here.
{
x.a.Status = true;
return x.a;
}
else return x.a;
});
C# fiddle with sample data.

Related

Linq join two tables and count column

I have two tables.
TableA
TableAId value value2
1 a b
2 aa bb
TableB
TableBId TableAId Status
1 1 success
2 1 success
3 2 failed
4 2 failed
I am trying to create list like
TableAId value value2 successCount errorCount
1 a b 2 0
2 aa bb 0 2
This is the query which I am trying to create but I am getting wrong results.
var query = (from a in db.TableA.AsEnumerable()
join b in db.TableB.AsEnumerable()
on a.TableAId equals b.TableAId
select
new
{
TableAId = a.TableAId,
value = a.value,
value2 = a.value2,
successCount = b.Status.Count(t => t.ToString() == "success"),
errorCount = b.Status.Count(t => t.ToString() == "failed")
}).ToList();
Any help how I can correct the query would be appreciated.
Thanks in advance
You need to group your rows by something first, otherwise the count doesn't make sense.
from a in db.TableA
join b in db.TableB on a.TableAId equals b.TableAId
group b by a into g
select new
{
TableAId = g.Key.TableAId,
value = g.Key.value,
value2 = g.Key.value2,
successCount = g.Count(t => t.Status == "success"),
errorCount = g.Count(t => t.Status == "failed")
}
I also removed AsEnumerable calls to move the join, grouping and counting to the database - your query brings all the rows into application memory before performing the query using LINQ to Objects, which is in 99.9% of cases not what you want.

Order condition in linq

I have two tables and I am joinning them using linq query
TableA with columns ID and Name
TableB with columns ID, TableAID and Order
TableB's column TableAID is a link between TableA and TableB. Please note that TableB my contain rows that do not exist in TableA
My data is as under
TableA
ID Name
1 One
2 Two
3 Three
TableB
ID TableAID Order
1 1 2
2 2 1
3 4 1
4 5 1
In my linq query I want to select all rows from TableB with matching ID in TableA but the list should contain Name from TableA in the order of TableB. So my data should be
Two
One
Please help in building the linq statement
I have tried as below, but dont know where to put the order
var MyList = TableA.Where(x => TableB.Any(y => y.TableAID == x.ID))
.ToList();
Probably your query will be like as given below example,
var data = (from a in context.TableA
from b in context.TableB
where a.ID == b.TableAID
select new
{
a.Name,
b.Order
}).Distinct().OrderByDescending(x => x.Order).ToList();
foreach(var item in data)
{
Console.WriteLine(item.Name);
}
Alternatively, you can rewrite #M. Nasser Javaid answer in:
context.TableA.Join(context.TableB, a => a.ID, b => b.TableAID,
(a, b) => new {a.Name, b.Order}).Distinct()
.OrderByDescending(x => x.Order)
.Select(x => x.Name).ToList()
please try this:
var listresult = (from b1 in TableA
join a1 in TableB on b1.ID equals a1.TableAID
select new
{
b1.Name,
a1.Order
}).ToList().OrderByDescending(x=>x.Order);
var final = (from m in listresult
select m.Name).ToList();

Speed up the linq group by statement

I have a table like this
UserID Year EffectiveDate Type SpecialExpiryDate
1 2015 7/1/2014 A
1 2016 7/1/2015 B 10/1/2015
there is no ExpriyDate in the table because it is only valid for one year, so the expiry date can be calculated from the effective date by adding a year.
The result I want to get is like this (the current year's effective date and the next year's expiry date)
UserID EffectiveDate ExpiryDate
1 7/1/2014 7/1/2016
And If the user's type is B, then there will be a special expiry date, so for this person, the result will be
UserID EffectiveDate ExpiryDate
1 7/1/2014 10/1/2015
Here is the code I wrote
var result = db.Table1
.Where(x => x.Year>= 2015 && (x.Type == "A" || x.Type == "B"))
.GroupBy(y => y.UserID)
.OrderByDescending(x => x.FirstOrDefault().Year)
.Select(t => new
{
ID = t.Key,
Type = t.FirstOrDefault().Type,
EffectiveDate = t.FirstOrDefault().EffectiveDate,
ExpiryDate = t.FirstOrDefault().SpecialExpiryDate != null ? t.FirstOrDefault().SpecialExpiryDate : (t.Count() >= 2 ? NextExpiryDate : CurrentExpiryDate)
}
);
The code can get the result I need, but the problem is that in the result set there are about 10000 records which took about 5 to 6 seconds. The project is for a web search API, so I want to speed it up, is there a better way to do the query?
Edit
Sorry I made a mistake, in the select clause it should be
EffectiveDate = t.LastOrDefault().EffectiveDate
but in the Linq of C#, it didn't support this LastOrDefault function transfered to sql, and it cause the new problem, what is the easiest way to get the second item of the group?
You could generate the calculated data on the fly, using a View in your database.
Something like this (pseudocode):
Create View vwUsers AS
Select
UserID,
Year,
EffectiveDate,
EffectiveData + 1 as ExpiryDate, // <--
Type,
SpecialExpiryDate
From
tblUsers
And just connect your LINQ query to that.
Try this:
var result =
db
.Table1
.Where(x => x.Year>= 2015 && (x.Type == "A" || x.Type == "B"))
.GroupBy(y => y.UserID)
.SelectMany(y => y.Take(1), (y, z) => new
{
ID = y.Key,
z.Type,
z.EffectiveDate,
ExpiryDate = z.SpecialExpiryDate != null
? z.SpecialExpiryDate
: (t.Count() >= 2 ? NextExpiryDate : CurrentExpiryDate),
z.Year,
})
.OrderByDescending(x => x.Year);
The .SelectMany(y => y.Take(1) effectively does the .FirstOrDefault() part of your code. By doing this once rather than for many properties you may improve the speed immensely.
In a test I performed using a similarly structured query I got these sub-queries being run when using your approach:
SELECT t0.increment_id
FROM sales_flat_order AS t0
GROUP BY t0.increment_id
SELECT t0.hidden_tax_amount
FROM sales_flat_order AS t0
WHERE ((t0.increment_id IS NULL AND #n0 IS NULL) OR (t0.increment_id = #n0))
LIMIT 0, 1
-- n0 = [100000001]
SELECT t0.customer_email
FROM sales_flat_order AS t0
WHERE ((t0.increment_id IS NULL AND #n0 IS NULL) OR (t0.increment_id = #n0))
LIMIT 0, 1
-- n0 = [100000001]
SELECT t0.hidden_tax_amount
FROM sales_flat_order AS t0
WHERE ((t0.increment_id IS NULL AND #n0 IS NULL) OR (t0.increment_id = #n0))
LIMIT 0, 1
-- n0 = [100000002]
SELECT t0.customer_email
FROM sales_flat_order AS t0
WHERE ((t0.increment_id IS NULL AND #n0 IS NULL) OR (t0.increment_id = #n0))
LIMIT 0, 1
-- n0 = [100000002]
(This continued on for two sub-queries per record number.)
If I ran my approach I got this single query:
SELECT t0.increment_id, t1.hidden_tax_amount, t1.customer_email
FROM (
SELECT t2.increment_id
FROM sales_flat_order AS t2
GROUP BY t2.increment_id
) AS t0
CROSS APPLY (
SELECT t3.customer_email, t3.hidden_tax_amount
FROM sales_flat_order AS t3
WHERE ((t3.increment_id IS NULL AND t0.increment_id IS NULL) OR (t3.increment_id = t0.increment_id))
LIMIT 0, 1
) AS t1
My approach should be much faster.

Linq Query for EAV design table

I had a situation to use EAV design tables and I am new to this design,I am struck with a select query. Below is my query structure and data.
TABLE1:
Id KeyName
1 Name
2 Age
TABLE2:
ID TABLE1_ID VALUE
1 1 ABC
2 2 12
3 1 CDF
4 2 14
5 1 XYZ
6 2 13
7 1 CSF
8 2 10
EXPECTED OUTPUT: Get all the values which are greater than 12 AND Value contains "C".
i.e.,
Table2_ID Result Table1_KeyName
1 ABC Name
2 12 Age
3 CDF Name
4 14 Age
Options I tried are:
Var temp = (from c in Table2
where c.Value > 12 && c.Table1.KeyName.Contains("C")
Select new
{
ID = c.ID,
Result = C.Value
});
the above query didn't returned any result, as filters(in where clause) are across rows. I even tried "OR" condition in where clause, it returns me everything. Please do help me.
Your query is wrong, did you type it in here or copy and paste it from your work?
Do you have this table setup in an ORM such as Entity Framework? So that there is a relationship setup between table1 and table2, so you don't have to 'join' them?
To be more 'correct' it should be...
Var temp = (from c in Table2
where c.Value > 12 && c.Table1.Name.Contains("C")
Select new
{
ID = c.ID,
Name = c.Table1.Name,
Value = c.Value
});
Or if the relationship isn't in an ORM your using explicitly specify the join as follows:
Var temp = (from c in Table2
join c1 in Table1 on c.Table1_ID equals c1.Id
where c.Value > 12 && c.Table1.Name.Contains("C")
Select new
{
ID = c.ID,
Name = c.Table1.Name,
Value = c.Value
});
Edit: then it should be an OR, not AND
Var temp = (from c in Table2
where c.Value > 12 || c.Value.Contains("C")
Select new
{
ID = c.ID,
Result = c.Value,
KeyName = c.Table1.Name
});

How to return value from 2 tables in one linq query

please consider this table:
PK_Id Number Year Month Value
-------------------------------------------------------------------------
1 1 2000 5 100000
410 4 2000 6 10000
8888 1 2001 5 100
I Id=8888 and now I want to first select record with Id=8888 and second select previos year of that record*(I mean Id=1)*. How I can do this with linq and one query.
basically we have some queries that first it should find a value from a table (that may be not PK) and find Corresponding records in another tables. How I can do this with linq and one reference to database.
thanks
from a in Record
where a.PK_Id == 8888
from b in Record
where b.Number == a.Number && b.Year == a.Year - 1
select new { Current = a, Previous = b }
or
Record
.Where(a => a.PK_Id == 888)
.SelectMany(a =>
Record
.Where(b => b.Number == a.Number && b.Year == a.Year - 1)
.Select(b => new { Current = a, Previous = b })
If I understand your question right, then you need to filter the data of one table and join two tables.
You can join the tables and filter your data
var query = from c in Table1
join o in Table2 on c.Col1 equals o.Col2
where o.Col3 == "x"
select c;
or you can filter your data from one table and then join the tables (result will be the same)
var query = from c in Table1.Where(item => item.Col3 == "x")
join o in Table2 on c.Col1 equals o.Col2
select c;

Categories