LINQ query to find Date difference? - c#

I am having date details which is in format (dd/mm/yyyy) like,
Table #1
id Name date User
--------------------------------
1 xxx 01/01/2016 user1
2 yyy 01/02/2016 user2
3 aaa 02/03/2016 user1
Table #2
id Name date
--------------------
1 xxx 07/01/2016
2 xxx 09/01/2016
3 yyy 05/02/2016
4. aaa 04/03/2016
EDIT 1:
The logic is, For the First time the xxx occured in Table2 means, it gets the date from the Table1 to compute the day difference. For the next time xxx value occured means, it get thes the date from Table2 (not table1) Because the Table2 already have the value in that table.So the day difference will compute by using Table2 alone. Same for the other values too.
EDIT 2:
Actually, I need to get the date difference for the value. I have to get the date difference by using Table1 if the Name is occurred First time. And to get the Dtae difference from Table2 , If the Name is occurred before.
I am having 'Name' field as UNIQUE
Result should be like,
user Name DateDifference
------------------------------------
user1 xxx 6 (07/01/2016 - 01/01/2016)
user1 xxx 2 (07/01/2016 - 09/01/2016)
user2 yyy 4 (01/02/2016 - 05/02/2016)
user1 aaa 2 (02/03/2016 - 04/03/2016)
LINQ query I have tried is,
var query = (from item1 in Table1
join item2 in Table1 on item1.Name equals item2.Name
where item2.Date > item1.Date
join origin in Table2 on item1.Name equals origin.Name
where // condition
group new { item1,item2 } by new { item1.Name,item1.Date } into grp
select new
{
DayDiff = DbFunctions.DiffDays(grp.Key.Date, grp.Min(d => d.item2.Date)),
}
).ToList();
But I could not get the result expected. I am also aware of this question, How to get the date difference by joining two tables? , But finally they did not end it in proper LINQ query option.
How my question differ from How to get the date difference by joining two tables? ?
The answer is in SQL. I need to code it in LINQ.

I hope at least you have navigation property over those two tables.Try this
class Program
{
static void Main(string[] args)
{
using (var db = new aaContext2())
{
IList<DTO> dto = new List<DTO>();
dto = db.Table2.Select(a => new DTO
{
Name = a.Name,
User = a.Namea.User,
DateDifference = (DbFunctions.DiffDays(a.Date, db.Table2.Where(aa => aa.Name.Equals(a.Name) && a.Date < aa.Date).Min(dd => dd.Date)
) ?? (DbFunctions.DiffDays(db.Table1.Where(aa => aa.Name.Equals(a.Name)).Min(aaa => aaa.Date), a.Date)))
}).ToList();
}
}
}
public class DTO
{
public string User { get; set; }
public string Name { get; set; }
public int? DateDifference { get; set; }
}
public class aaContext2 : DbContext
{
public DbSet<Table1> Table1 { get; set; }
public DbSet<Table2> Table2 { get; set; }
}
public class Table1
{
public int Id { get; set; }
public string Name { get; set; }
public virtual IList<Table2> NameList { get; set; }
public DateTime Date { get; set; }
public string User { get; set; }
}
public class Table2
{
public int Id { get; set; }
public virtual Table1 Namea { get; set; }
public string Name { get; set; }
public DateTime Date { get; set; }
}
}

Here is one possible way:
var query =
(from t1 in db.Table1
join t2 in db.Table2 on t1.Name equals t2.Name
let prevDate = (from t3 in db.Table2
where t3.Name == t2.Name && t3.Date < t2.Date
select (DateTime?)t3.Date).Max() ?? t1.Date
select new
{
t1.User,
t1.Name,
DayDiff = DbFunctions.DiffDays(prevDate, t2.Date).Value,
}).ToList();

I wanted the date diff to be inside where condition not outside the query so this form helped me:
Found it on forums.asp.net
Here's a sample showing one way to get all employees with a DOB between now and 14 days from now...
var employeesWithBirthday =
from emp in dc.Employees
let BirthdayDiff = (new DateTime(DateTime.Now.Year, emp.BirthDate.Month, emp.BirthDate.Day) - DateTime.Now).TotalDays
where BirthdayDiff >= 0 && BirthdayDiff <= 14
select emp;
...although, be aware that a query like that will do a table scan (can't use any indexes)...

Related

LINQ - Grouping by left join queries

I have three tables as follows:
public class Employee
{
public Guid Id { get; set; }
public string FirstName { get; set; } = null!;
public string LastName { get; set; } = null!;
}
public class Answer
{
public Guid Id { get; set; }
public Guid QuestionId { get; set; }
public Guid AppraiserId { get; set; }//Employee who appraises
public Guid AppraisedId { get; set; }//Employee who has been appraised
}
public class FinalizedEmployee
{
public long Id { get; set; }
public Guid AppraiserId { get; set; }
public Guid AppraisedId { get; set; }
}
Every employee has an entry in the answers table by default. and when they answer all the questions we add a record in FinalizedEmployees.
Now I want to write a query to show all employees whether they have answered all the questions or not.
The output will be something like this:
Employee
Has answered all the questions
Employee 1
True
Employee 2
False
Employee 3
True
This is what I have tried so far:
var attendees = from answers in dbContext.Answers
join employees in dbContext.Employees
on answers.AppraiserId equals employees.Id
join finalize in dbContext.FinalizedEmployees
on answers.AppraiserId equals finalize.AppraisedId into finalized
from completed in finalized.DefaultIfEmpty()
where answers.AppraisalId == request.appraisalId
group employees by new { appraiser=answers.AppraiserId,hasComplete=completed!=null} into attendee
select new {attendee.Key.appraiser,attendee.Key.hasComplete };
The output is correct but only gives me employee Ids instead of the employee itself.
How can I write this query?
Is there any better way?
Every employee has an entry in answers table. and if they have answered all the questions there is a record in FinalizedEmployee table.
To check whether that the Employee has answered all the questions, you need a LEFT JOIN query for Employee to FinalizedEmployee tables.
With LEFT JOIN:
Guarantee all the records from Employee (LEFT) table will be queried.
If the employee's Id exists in FinalizedEmployee, the HasAnsweredAllQuestion will be true.
(from a in dbContext.Employees
join b in dbContext.FinalizedEmployees on a.Id equals b.AppraisedId into ab
from b in ab.DefaultIfEmpty()
select new
{
Id = a.Id,
Name = a.FirstName + " " + a.LastName,
HasAnsweredAllQuestion = b != null
}).ToList();
Another approach suggested by #Corey which was achieved with EXISTS will be:
(from a in dbContext.Employees
select new
{
Id = a.Id,
Name = a.FirstName + " " + a.LastName,
HasAnsweredAllQuestion = dbContext.FinalizedEmployees.Any(x => x.AppraisedId == a.Id)
}).ToList();

Comparing two table IDs. ASP.NET MVC

I am currently loading two Orders and Colors tables, I wanted the Colors table to list the items that have the ID equal to Orders. For this, what occurred to me was to assign the IdOrders values ​​to a variable and compare it with my IdOrders (in my table Colors), but it is not possible to assign the database's balance to my variable
My tables:
public partial class Orders
{
public int ID_Orders { get; set; }
public Nullable<System.DateTime> Data_Registo { get; set; }
public string Num_Encomenda { get; set; }
public string Ref_Cliente { get; set; }
}
public partial class Colors
{
public int ID_Orders { get; set; }
public int ID_Programa_Malha { get; set; }
public int ID_Linha_Cor { get; set; }
public string Cor { get; set; }
}
I am working with a database already in operation and possible these tables are already used in a sql join but not how to process that information.
As I said the first thing I remembered was to do this:
My Controller:
var id = from d in db.Orders
select d.ID_Orders;
var color = db.Colors.Where(x => x.ID_Orders = id).ToList();
var tables = new EncomendaViewModel
{
Orders= db.Orders.ToList(),
Colors= color.ToList(),
};
return View(tables);
Error in id: CS0029 C# Cannot implicitly convert type to 'int'
Is it possible to process the data in this way?
Thanks for anyone who can help!
-------------------(Update)------------------------------------------------
Using == cs0019 operator '==' cannot be applied to operands of type
My view in Broswer
dbEntities sd = new dbEntities();
List<Orders> orders= sd.Orders.ToList();
List<Colors> colers= sd.Colors.ToList();
var multipletable = from c in orders
join st in colers on c.ID_Programa equals st.ID_Programa into table1
from st in table1.DefaultIfEmpty()
select new MultipleClass { orders= c, colers= st };
There could be one or more values returned from the below query.
var id = from d in db.Orders
select d.ID_Orders;
That is the reason why it was throwing an error.
So lets try it this way
var color = db.Colors.Where(x => id.Contains(x.ID_Orders)).ToList();
public class OrderWithColorsViewModel
{
public Order order { get; set; }
public List<Colors> colers{ get; set; }
}
Public class TestOrderController : Controller
{
public DailyMVCDemoContext db = new DailyMVCDemoContext();
public ActionResult Index()
{
var orders= db.Orders.ToList();
var colers = db.Colors.ToList();
var result = (from c in orders
join st in colers on c.ID_Orders equals st.id into table1
select new OrderWithColorsViewModel { order =c, colers =
table1.ToList() }).ToList();
return View(result);
}
}
credits: YihuiSun

Convert simple Left Outer Join and group by SQL statement into Linq

2 tables: User and Alarm
Table:User
UserID(int),
FullName(varchar)
Table:Alarm
AssignedTo(int),
Resolved(bool)
Query:
SELECT u.Fullname, COUNT(resolved) as Assigned, SUM(CONVERT(int,Resolved)) as Resolved, COUNT(resolved) - SUM(CONVERT(int,Resolved)) as Unresolved
FROM Alarm i LEFT OUTER JOIN Users u on i.AssignedTo = u.UserID
GROUP BY u.Fullname
Results:
Fullname Assigned Resolved Unresolved
User1 204 4 200
User2 39 9 30
User3 235 200 35
User4 1 0 1
User5 469 69 400
For the life of me I can't figure out how to make this into a Linq query. I am having trouble with the grouping function.
I've looked a countless examples and none have my combination of Left Outer join with grouping or they are so complicated that I can't figure out how to make it work with mine. Any help here would be Greatly appreciated!!!
Update:
I may not have been clear in what I'm looking for. I am looking for the alarms grouped by the AssignedTo Column which is a userid... Except, I want to replace that userid with the FullName that is located in the users table. Someone had posted and deleted something close except it gave me all users in the user table which is not what I'm looking for..
Update 2: See my answer below
Assuming that you have the following models:
This is the model for Alarm:
public class Alarm
{
public int id { get; set; }
public int AssignedTo { get; set; }
[ForeignKey("AssignedTo")]
public virtual User User { get; set; }
public bool Resolved { get; set; }
}
This is the model for User:
public class User
{
public int UserID { get; set; }
public string FullName { get; set; }
public virtual ICollection<Alarm> Alarms { get; set; }
public User()
{
Alarms = new HashSet<Alarm>();
}
}
This is the model that will hold the alarm statistics for each user:
public class UserStatistics
{
public string FullName { get; set; }
public int Assigned { get; set; }
public int Resolved { get; set; }
public int Unresolved { get; set; }
}
You can then do the following:
var query = context.Users.Select(
user =>
new UserStatistics
{
FullName = user.FullName,
Assigned = user.Alarms.Count,
Resolved = user.Alarms.Count(alarm => alarm.Resolved),
Unresolved = user.Alarms.Count(alarm => !alarm.Resolved)
});
var result = query.ToList();
By the way, you can also modify the query and remove Unresolved = user.Alarms.Count(alarm => !alarm.Resolved), and then make the Unresolved property a calculated property like this:
public class UserStatistics
{
public string FullName { get; set; }
public int Assigned { get; set; }
public int Resolved { get; set; }
public int Unresolved
{
get { return Assigned - Resolved; }
}
}
This will make the generated SQL query simpler.
I finally figured it out.
This:
var results = alarms.GroupBy(x => x.AssignedTo)
.Join(users, alm => alm.Key , usr => usr.UserID, (alm, usr) => new {
Fullname = usr.FullName,AssignedNum = alm.Count(),
Resolved = alm.Where(t=>t.resolved == true).Select(y => y.resolved).Count(),
Unresolved = alm.Where(t=>t.resolved == false).Select(y => y.resolved).Count() });
Reproduces This:
SELECT u.Fullname, COUNT(resolved) as Assigned, SUM(CONVERT(int,Resolved)) as Resolved,
COUNT(resolved) - SUM(CONVERT(int,Resolved)) as Unresolved
FROM Alarm i LEFT OUTER JOIN Users u on i.AssignedTo = u.UserID
GROUP BY u.Fullname
The result is grouped by the AssignedTo (int) but AssignedTo is not selected. Instead FullName is selected from the joined user table.
Many thanks to everyone that tried to help! I learned a lot from your answers.
For bonus points, how would I write my lamdbda answer in a SQL like syntax?
Try this :
from u in context.User
join a in context.Alarm on u.UserID equals a.AssignedTo into g1
from g2 in g1.DefaultIfEmpty()
group g2 by u.Fullname into grouped
select new { Fullname = grouped.Key, Assigned = grouped.Count(t=>t.Resolved != null), Resolved = grouped.Sum
(t => int.Parse(t.Resolved)), Unresolved = (grouped.Count(t=>t.Resolved != null) - grouped.Sum
(t => int.Parse(t.Resolved)))}
I guess it is not necessarily to use "Grouping" for this query in Linq because the combination of "LEFT JOIN" + "GROUP BY" changed them over to "INNER JOIN".
var results =
from u in users
join a in alarms on u.UserID equals a.AssignedTo into ua
select new
{
Fullname = u.FullName,
Assigned = ua.Count(),
Resolved = ua.Count(a => a.Resolved),
Unresolved = ua.Count(a => !a.Resolved)
};
foreach (var r in results)
{
Console.WriteLine(r.Fullname + ", " + r.Assigned + ", " + r.Resolved + ", " + r.Unresolved);
}

Create Poll in C# (windows form)

I want create a poll. i have a table for questions(Fields: ID,Question) and another for answers(Fields: ID,QuestionID,Answer). There is a table for the results(fields: QuestionID,AnswerID,UserID).i want to show the Percent of responding to any item in datagridview.
for example when i enter question id, datagridview show:
Choose question ID:1
Option...........Percent
1 ------------------------- 30
2 -------------------------- 20
3---------------------------50
4-------------------------- 10
this is my code But the result does not show it:
int a = Convert.ToInt32(textBox1.Text);
var q = (from s in Session.DB.PoolUsers
where s.PoolQID == a
select s);
var qq = (from c in q
group c by c.PoolAID into agroups
select agroups.Key);
var qqq = (from c in qq
select c).Count();
MessageBox.Show(qqq.ToString());
And there is my classes:
public partial class PoolA //For answers
{
public int Id { get; set; }
public string Answer { get; set; }
public string QuestionID { get; set; }
}
public partial class PoolQ //Questions
{
public int Id { get; set; }
public string Question { get; set; }
public string AnswerID { get; set; }
public string Status { get; set; }
public string StartDate { get; set; }
public string EndDate { get; set; }
}
public partial class PoolUser //resaults
{
public int Id { get; set; }
public int PoolQID { get; set; }
public int PoolAID { get; set; }
public int UserID { get; set; }
}
This is not the optimal way I would do it, I would highly consider you, to change your entities.
Here is the solution:
int questionId = 1;
var questionAnswers = list.Where(elem => elem.PoolQID == questionId);
int questionAnswersCount = questionAnswers.Count();
var answersPrecentage = questionAnswers
.GroupBy(elem => elem.PoolAID)
.ToDictionary(grp => grp.Key, grp => (grp.Count() * 100.0 / questionAnswersCount));
.NET Fiddle Link
I don't know your intentions but I think you should redesign the schema (not that it not doable as it is) for maximum efficiency. You call your PoolUser table your results table but it is your answers log at best. you need a table that has the results processed already such as would be done in a DWH kind of situation.
If you have a results summary table you could work out vertical sums groups all day without the extra performance of joining tables etc. for ad hoc data. I reckon you'd be running this a lot to go over the results, so it sounds like it is a better idea to store the summarized information for reporting, performance etc.
as for the current state of the problem:
var qq = (from c in q
group c by c.PoolAID into agroups
select agroups.Key);
agroups has the answerIds grouped up for the question number you typed in.
then you are just showing the row count of the grouped up answers which shows you 2:
var qqq = (from c in qq
select c).Count();
you are telling me that your messagebox shows the number "2"
this means that people only selected 2 options to answer whatever question id you put in. you don't know whether the answer is correct or not. the last query "qqq" you have, you have the count outside so it only returns the row count. you don't want the row count. you want the answerID count after the grouping.
from c in qq
select count(c)
this is the total answers for the questionID you typed in.
now you need the correct answers in order to calculate the percentage. this is the query missing. you have gone 75% of the way. just finish it off. hopefully this is sufficient detail and not too much text to read.
The problem was solved and I got the following code. I also draw a graph based on
private void button1_Click(object sender, EventArgs e)
{
var list = (from c in Session.DB.PoolUsers
select c).ToList();
int questionId = Convert.ToInt32(textBox1.Text);
var questionAnswers = list.Where(elem => elem.PoolQID == questionId);
int questionAnswersCount = questionAnswers.Count();
var answersPrecentage = questionAnswers
.GroupBy(elem => elem.PoolAID)
.ToDictionary(grp => grp.Key, grp => (grp.Count() * 100.0 / questionAnswersCount));
dataGridView1.DataSource = answersPrecentage.ToArray();
//Draw Chart From DataGridview:
chart1.DataBindTable(answersPrecentage);
}
And works properly
Thanks all :)

LINQ MVC ViewModel: Multiple joins to the same table with optional field

Given the following database structure
Category
ID
CategoryNameResID
ParentCategory(Optional)
Resources
ID
Text
Lang
And given a ViewModel
public class CategoryViewModel
{
public int ID { get; set; }
public int CategoryNameResID { get; set; }
public string CategoryName { get; set; }
public int ParentCategory { get; set; }
public string ParentCategoryName { get; set; }
}
I want to get the list of all Categories with ParentCategoryName included
What I did so far is:
var categories = (from cat in db.Categories
join res in db.Resources on cat.CategoryNameResID equal res.ID
select new CategoryViewModel{
ID = cat.ID,
CategoryNameResID = cat.CategoryNameResID,
CategoryName = res.Text,
ParentCategory = cat.ParentCategory,
ParentCategoryName = (from p in db.Resources
where p.ID == cat.ParentCategory
select p.Text)
}).ToList();
I can't figure out how to get the ParentCategoryName without having to iterate again, which is definitely wrong.
Try this:
(from cat in cats
join res in resources on cat.ResId equals res.Id let categoryName = res.Text
join cat1 in cats on cat.ParentId equals cat1.Id into parentJoin
from pj in parentJoin.DefaultIfEmpty() let parentCatResId =pj==null?0: pj.ResId
join res1 in resources on parentCatResId equals res1.Id into resJoin
from res2 in resJoin.DefaultIfEmpty() let parentName = (res2==null?string.Empty:res2.Text)
select new CategoryVM
{
Id = cat.Id,
ResId = cat.ResId,
CatName = categoryName,
ParentId = cat.ParentId,
ParentName = parentName
}).ToList();
Say you have the following data with your tables
dbo.Categories
ID CategoryNameResID ParentCategory
1 1 NULL
2 2 NULL
3 3 1
4 4 NULL
5 5 4
6 6 4
7 7 4
dbo.Resources
ID Text Lang
1 Standard en-GB
2 Custom en-GB
3 Standard Oversize en-GB
4 Loose en-GB
5 Loose 2F Set en-GB
6 Loose (4” Scale) en-GB
7 Loose (6” Scale) en-GB
The following LINQ statements will output the desired results:
public class CategoryViewModel
{
public int ID { get; set; }
public int CategoryNameResID { get; set; }
public string CategoryName { get; set; }
public int? ParentCategory { get; set; }
public string ParentCategoryName { get; set; }
}
var categories = (from cat in Categories
join res in Resources on cat.CategoryNameResID equals res.ID let categoryName = res.Text
select new CategoryViewModel
{
ID = cat.ID,
CategoryNameResID = cat.CategoryNameResID,
CategoryName = categoryName,
ParentCategory = cat.ParentCategory,
ParentCategoryName = Resources.FirstOrDefault(r => r.ID == cat.ParentCategory).Text
}).ToList();
foreach(var c in categories)
{
Console.WriteLine(c.CategoryName + " - " + c.ParentCategoryName);
}
// Prints
Standard -
Custom -
Standard Oversize - Standard
Loose -
Loose 2F Set - Loose
Loose (4” Scale) - Loose
Loose (6” Scale) - Loose

Categories

Resources