C# Linq Weighted Average Based on Date - c#

I've found several posts detailing how to perform a weighted average based on a foreign key, but I have yet to find a solution that deals with my situation. Here it is:
I have two tables, table A and a table B many-to-many table linking them; nothing complicated:
TableA
{
A_ID,
Other stuff
}
TableB
{
B_ID,
Date
Other stuff
}
LinkAtoB
{
A_ID,
B_ID
}
Now here comes the math part. I'm more or less trying to weight result from TableA based on the number of recent associations in Table B.
So if TableA has 4 associations in table with the following dates:
{10/23/2010, //3 days ago
10/19/2010, //5 days ago
10/18/2010, //6 days ago
9/13/2010} //40ish days ago
So here is how I'd like to rank them:
I'd like to provide a recency threshold in days, I'll use 7 days as an example:
So using the above data I would assign the following values:
{10/23/2010, //7-3 = 4
10/19/2010, //7-5 = 2
10/18/2010, //7-6 = 1
9/13/2010} //40ish days ago
So the value of the weighted average for that particular TableA entry is then 7 / 3 = 2.33333.
Here is more or less what I have so far:
var k = from a in TableA
group a by a.Select(x=>x.LinkAtoB.TableB)
.Where(x=>x.Date.CompareTo(DateTime.Now.AddDays(-7)) >= 0)
into g
select g.Sum(x => DateTime.Now.Subtract(x.Date).Days) /
g.Sum(x => x.Length);
I think I'm close but I know I have the group part wrong. I think the other stuff should work. How do I fix my code to accomplish what I want?

Here you go! :)
var k = (from b in TableB
join bb in LinkAtoB on b.B_ID equals bb.B_ID into b_join
from ab in b_join.DefaultIfEmpty()
where b.B_DATE.CompareTo(DateTime.Now.AddDays(-7)) > 0
select new {ab.A_ID, DaysAgo = (DateTime.Now - b.B_DATE).Days} into xx
group xx by xx.A_ID into yy
select new {yy.Key, Weighted = yy.Sum(x=> 7 - x.DaysAgo) / yy.Count()} into zz
join a in TableA on zz.Key equals a.A_ID
select new {a.A_ID, a.A_Other_Stuff, zz.Weighted}).ToList();

Related

group by linq to entity query to get one record having latest timestamp by joining tables

There are two tables and using linq query to get records. From second table, there can be multiple rows corresponding to first table with date timestamp... based on below query, I am getting all records, but is there a way we can get the row from second table which has latest timestamp ?
Table Parent
ID Name
1 M
2 N
3 O
4 P
5 Q
Table Child
Id fkID DateTime
1 2 01/12/2021 09:12:20
2 2 01/12/2021 09:13:20
3 2 01/12/2021 09:14:20
4 2 01/12/2021 09:15:20
5 2 01/12/2021 **09:16:20**
Linq query:
from p in Parent
join c in Child on p.id equals c.fkId into cJoin
from cJoin in cJoin.DefaultIfEmpty()
select new TempResponse
{
Id = p.Id,
Name = p.Name,
Date = c.Date
}
I am getting 10 records using above query but just need 5 records i.e. from child table instead of all 5 records, we need a record that has latest time stamp
**expected output**
1 M
2 N 01/12/2021 09:16:20
this record is 5'th record from child table because this one has latest date time stamp
( latest record )
3 O
4 P
5 Q
Is there any way we can use group by and get the record that has latest time stamp from second table ?
Assuming you have defined navigation properties for the FK, I would use a query like;
dbContext.Child.Where(c => c.Date == c.Parent.Child.Max(c2 => c2.Date))
I believe you can use:
var ans = from p in Parent
join cmax in (
from c in Child
group c by c.fkId into cg
select cg.OrderByDescending(c => c.Date).FirstOrDefault())
on p.Id equals cmax.fkId into cJoin
from c in cJoin.DefaultIfEmpty()
select new TempResponse {
Id = p.Id,
Name = p.Name,
Date = c != null ? c.Date : null
};
Note that the order of results seems to vary on SQL Server unless you add another orderby clause before the select to force an order.

Simplifying LINQ query in C#

Table1
Table1ID Name Graduation Version Hobbies
1 A Degree 1 B
2 A Degree 2 C
3 A Degree 3 D
Table2
Table2ID Table1ID Name Graduation Version Address Surname Date
1 1 A Degree 1 A A 08-10-2019
2 2 A Degree 2 A A 08-10-2019
3 3 A Degree 3 A A
//I want to check if any version greater than highest version exists in Table1 .where Date column is not null in Table2
Suppose for the combination of Name and Degree , the highest version is 2 in Table2 since Date is null for Table2, I want to check if any record greater than 2 exists in Table1, if yes add it to a new List
Here is what I am doing.
List<Table2> groupByTable2 = //Operations on Table2 and get highest Version record from db
List<Table1> check = new List<Table1>();
List<Table1> check2 = await _table1.GetAll().ToListAsync();
Foreach(var a in groupByTable2)
{
List<Table1> check4 = check2.Where(x => x.Name == a.Name && x.Graduation == a.Graduation).ToList();
If(check4.Any(x=>x.Version > a.Version))
{
check.Add(check2.Where(x=>x.Table1ID == a.Table1ID).First());
}
}
Now my check contains a record where ID is 3. But is there any simpler way to achieve this in simpler way with readability and performance?
I hope I understood what you are trying to achieve. You could try the following.
var result = table2.Where(x=>x.Date!=null)
.GroupBy(x=> new {x.Name, x.Graduation})
.SelectMany(x=> x.OrderByDescending(c=>c.Version).Take(1))
.Join(table1,t2=>t2.Table1ID,t1=>t1.Table1ID,(t2,t1)=>t1)
.ToList();
result.AddRange(table1.Where(x=> result.Any(c=>c.Name.Equals(x.Name)
&& c.Graduation.Equals(x.Graduation)
&& c.Version < x.Version)));
The idea is to first use GroupBy and Join to get the List of Items with highest Version number in Table1 that has a valid date in Table2. Then, use List.AddRange to add remaining higher versions from Table1.

LINQ to SQL Join orderby

i am new to LINQ and joins, so Please forgive me if I am asking it wrong.
I have two tables
Table1
id name date
1 Mike 20-10-15
2 John 21-10-15
3 Sam 23-10-15
Table2
id name date
1 Ashle 19-10-15
2 Lily 21-10-15
3 Jeni 22-10-15
4 April 23-10-15
I need 5 records using Joins and should be orderby Date, most recent records.
Can you guys help me, I really need to figure out how Joins works with orderby.
Thanks
EDIT:
They are two different tables so no foreign key, so I think I can't use Join, so so far what I have done is like this
var combinddata = (from t1 in db.Table1
select t1.id)
.Concat(from t2 in db.Table2
select t2.id);
I don't know how to get only 5 records how to compare records from both tables on DateTime base.
Output should be
Sam
April
Jeni
John
Lily
You can concatenate equal anonymous types from different tables. If you also select the dates, you can sort by them, in descending order, and take the first 5 records:
Table1.Select (t1 =>
new
{
Id = t1.Id,
Name = t1.Name,
Date = t1.Date
}
).Concat(
Table2.Select (t2 =>
new
{
Id = t2.Id,
Name = t2.Name,
Date = t2.Date
}
))
.OrderByDescending (x => x.Date).Take(5)
Note that this gives precedence to items in Table1. If item 5 and 6 in the concatenated result are on the same date, but from Table1 and Table2, respectively, you only get the item from Table1.
If you want, you can select only the names from this result, but I assume that your output only shows the intended order of record, not the exact expected result.
var query =
from Table1 in table1
join Table2 in table2 on table1.id equals table2.id
orderby table1.date ascending
select table1.date;
Try this way
var combinddata = (from t1 in db.Table1
select t1.Name)
.Concat(from t2 in db.Table2
select t2.Name).OrderByDescending(x => x.date).Take(5);

How to determine which SQL entries are missing after querying?

Given the following database structure and using the EntityFramework.
Every five minutes, the 'phasecount' table gets records for each record in 'Phase'.
using (Entities db = new Entities())
{
db.ContextOptions.LazyLoadingEnabled = false;
int numberofcontrollers = (from a in db.Junctions select a).Count();
List<int> controllerids = (from b in db.Junctions select b.Id).ToList();
var configuration = (from c in db.Configurations select c).First();
DateTime laststamp = (from s in db.Stamps select s.Time).Max();
DateTime firststamp = laststamp.AddMinutes(-1 * (CountIntervalsBefore - 1) * TimeSliceLength);
var stamps = from s in db.Stamps.Include("PhaseCounts.Phase") where s.Time >= firststamp && s.Time <= laststamp orderby s.Id select s;
// check consistency; number of stamps should equal timeslices*controllers
if (stamps.Count() != CountIntervalsBefore * numberofcontrollers)
{
//counts are not available for all timeslices and controllers
//do extended consistency check (and use dummy data?)
}
}
I want to select for one hour each phasecount for all phases.
stamps normally equals 72, ie 12 5-minute slices * 6 junctions.
If it doesn't equal 72, how to determine which phases and which timestamps have missing data?
My first thoughts on a solution; this may not be the optimal search method but it should work.
Take the earliest timestamp in the group, and check that you do indeed have the correct number of records with that timestamp (from what you've said, I believe this is 6). From that you can tell if there are any missing from this set. Then look for the closest timestamp to the current one. If it's significantly more than about five minutes, you have a whole set missing. If it's around five minutes, check that you have the correct number of records with that timestamp, like with the first set. Repeat until either (total missing records + total found records = 72) or you run out of records. If you get to the end of the records and you still have some missing, then the earliest timestamp was not the first one, and you have a complete set missing there as well. At this point, either (total missing records + total found records = 72) or something has gone very wrong.

Column is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause

This may be a very silly mistake but I just can't fix it. I have 2 tables, Questions and Questions_Rating.
Questions:
question_id question user_id
------------------------------------------
1 'How to blablabla' 1
2 'bla bla bla' 1
Questions_Rating
In this table, users will rate questions either by +1 or -1
question_rating_id question_id user_id rate
------------------------------------------------------
1 1 2 (+1)
2 1 3 (+1)
3 1 4 ( 1)
Now I would simply like to fetch the question from the questions table, and the SUM of the rate in the questions_rating table for this question, which has an ID = 1.
String QUERY = "SELECT q.question, SUM(r.rate) FROM questions q, questions_rating r WHERE r.question_id = q.question_id AND q.question_id = 1";
And this is what I'm getting.
Column 'questions.question' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
I know that the SUM of the rate will return 1 row, and the question is supposedly 1 row, I can't figure out what's wrong with this query.
I am using SQL Server 2008.
You will have to Group the values as per the question so you need a
GROUP BY q.question
at the end of your query
Good Explanation here
You need the group by q.question in your SQL statement.
Your Sum is suppose to return only a single row against the table, since you are selecting a column along with the Sum you need to specify a group on the selected column. In your case it should be q.question, and sum will be applied to each group of questions.
SELECT q.question,SUM(r.rate) AS RateSum
FROM questions q,questions_rating r
WHERE r.question_id = q.question_id AND q.question_id=1
GROUP BY q.question
or (using ANSI 92 style joins)
SELECT q.question,SUM(r.rate) AS RateSum
FROM questions q INNER JOIN questions_rating r ON r.question_id = q.question_id
WHERE q.question_id=1
GROUP BY q.question
or (using subqueries)
SELECT q.question, (SELECT SUM(r.rate) FROM questions_rating r WHERE r.question_id = q.question_id) AS RateSum
FROM questions q
WHERE q.question_id=1
You are missing group by clause in your select statement.
As you have used sum() function which is an aggregate function
So you need to write group by for all the select columns.
String QUERY = "SELECT
q.question,
SUM(r.rate)
FROM
questions q,
questions_rating r
where
r.question_id = q.question_id AND
q.question_id=1
group by
q.question";

Categories

Resources