I'm new to LINQ to SQL and I would like to know how to achieve something like this in LINQ:
Month Hires Terminations
Jan 5 7
Feb 8 8
Marc 8 5
I've got this so far, and I think there is something wrong with it but I'm not sure:
from term1 in HRSystemDB.Terminations
group term1 by new { term1.TerminationDate.Month, term1.TerminationDate.Year } into grpTerm
select new HiresVsTerminationsQuery
{
Date = Criteria.Period,
TerminationsCount = grpTerm.Count(term => term.TerminationDate.Month == Criteria.Period.Value.Month),
HiresCount = (from emp in HRSystemDB.Persons.OfType<Employee>()
group emp by new { emp.HireDate.Month, emp.HireDate.Year } into grpEmp
select grpEmp).Count(e => e.Key.Month == Criteria.Period.Value.Month)
});
Thanks in advance.
I'm not quite sure where does the Criteria.Period value come from in your sample query.
However I think you're trying to read both hires and terminations for all available months (and then you can easily filter it). Your query could go wrong if the first table (Termination) didn't include any records for some specified month (say May). Then the select clause wouldn't be called with "May" as the parameter at all and even if you had some data in the second table (representing Hires), then you wouldn't be able to find it.
This can be elegantly solved using the Concat method (see MSDN samples). You could select all termniations and all hires (into a data structure of some type) and then group all the data by month:
var terms = from t in HRSystemDB.Terminations
select new { Month = t.TerminationDate.Month,
Year = term1.TerminationDate.Year,
IsHire = false };
var hires = from emp in HRSystemDB.Persons.OfType<Employee>()
select new { Month = emp.HireDate.Month,
Year = emp.HireDate.Year
IsHire = true };
// Now we can merge the two inputs into one
var summary = terms.Concat(hires);
// And group the data using month or year
var res = from s in summary
group s by new { s.Year, s.Month } into g
select new { Period = g.Key,
Hires = g.Count(info => info.IsHire),
Terminations = g.Count(info => !info.IsHire) }
When looking at the code now, I'm pretty sure there is some shorter way to write this. On the other hand, this code should be quite readable, which is a benefit. Also note that it doesn't matter that we split the code into a couple of sub-queries. Thanks to lazy evalutation of LINQ to SQL, this should be executed as a single query.
I don't know if it shorter but you can also try this version to see if it works better with your server. I don't know exactly how these two answers turn into SQL statements. One might be better based on your indexs and such.
var terms =
from t in Terminations
group t by new {t.Month, t.Year} into g
select new {g.Key, Count = g.Count()};
var hires =
from p in Persons
group p by new {p.Month, p.Year} into g
select new {g.Key, Count = g.Count()};
var summary =
from t in terms
join h in hires on t.Key equals h.Key
select new {t.Key.Month, t.Key.Year,
Hires = h.Count, Terms = t.Count};
Related
I'm pretty new to C# and LINQ and I'm trying get a list of emails that holds the sum of emails, attachments and user's (the one's that sent the email).
So my current Problem is the Output of my Query is false. The number of email's is equal to the number of attachment's which obvious is wrong.
My Query:
var monthQuery = from em in dbEdoka.email
join ema in dbEdoka.email_attachment on em.id equals ema.email_id into e
from e2 in e.DefaultIfEmpty()
group e2 by em.erstellt_am.Month into grouped
select new Entities.Month
{
NameOfMonth = grouped.FirstOrDefault().erstellt_am.ToString(),
NumberOfMails = grouped.Distinct().Count(m => m.email_id != null).ToString(),
NumberOfAttachments = grouped.Count(a => a.id != null).ToString(),
NumberOfUsers = grouped.Select(u => u.erstellt_von).Distinct().Count().ToString()
};
months = monthQuery.ToList();
Months = CollectionViewSource.GetDefaultView(months);
As you can see I had to take m.email_id from dbEdoka.email_attachment instead of m.id from dbEdoka.email because it wasn't avaliable (don't know why...).
Yet I have to count "NumberOfMails", "NumberOfAttachments" and "NumberOfUsers".
Thank you!
I'm trying to query my MsSQL Express database to find all CompanyID's which have multiple dates associated - when I say multiple dates, I must point out they need to be over different days.
EG
ID UkDate CompanyId
1 01/01/2015 16
2 01/01/2015 16
3 03/01/2015 18
4 05/01/2015 19
5 06/01/2015 20
6 08/01/2015 20
In the example above, only the rows with ComapnyID 20 would be returned because it occurred multiple times and those times were over dates (note that although companyId 16 has multiple entries, but both entries are the same date).
I'm not sure how to write the query for this using Linq. My object is already IQueryable<T> but, I'm not sure how to perform the query without executing the code, and then 'finishing off' the query.
I'm not near Visual Studio but the code would be (please forgive typing errors, this is from memory)
//First, grab unique CompanyIds as this removes those who didn't visit multiple times
var uniqueIds = (from d in this._database.MyTable
select companyId).Distinct();
//This is the problem because on each iteration I'm re-querying the database!
foreach(var id in uniqueIds)
{
var result = (from d in this._database.MyTable.OrderBy(a=>a.UkDate)
where d.CompanyId==id
select d);
//check for nulls
if (result.First(a=>a.UkDate.Day) != result.Last(a => a.UkDate.Day)
{
this.AllResultsList.AddRange(results);
}
}
Whilst it works without error I don't feel the code is correct - it feels like a hack and unefficient but this was my best effort. Is there a way I could reduce the number of database requests I make and achieve the same result
It would be something along the lines of
var results = myTable.GroupBy(x => x.CompanyID)
.Where(g => g.GroupBy(g2 => g2.UkDate).Count()>1)
.Select(g => g.Key);
Live example (albeit with LinqToObjects, but the query should work against a database just fine): http://rextester.com/FPHI53553
var results = (from o in this._database.MyTable
group o by o.CompanyId into grouped
where (grouped.Max(s => s.UKDate) - grouped.Min(s => s.UKDate)).TotalDays > 0
select grouped.Key);
Edit (by OP)
Final result:
var results = (from o in this._database.MyTable
group o by o.CompanyId into grouped
where (Convert.ToDateTime(grouped.Max(s => s.UKDate)) - Convert.ToDateTime(grouped.Min(s => s.UKDate))).TotalDays > 0
from l in myTable
where l.CompanyID == grouped.Key
select l).ToList();
A little different version:
var result = (from o in this._database.MyTable
group o by o.CompanyId into grouped
select new {
grouped.Key,
Count = grouped.Select(c => c.UkDate).Distinct().Count()
} into filter
where filter.Count > 1
join a in this._database.MyTable on filter.Key equals a.CompanyID
select new { a.CompanyID, a.UkDate}
).ToList();
You can also try this if you want the company id and a count of the different dates:
from c in dataTable
group c by c.CompanyId into grouped
let count = grouped.Select(x => x.UkDate).Distinct().Count()
where count > 1
select new { CompanyId = grouped.Key, Count = count }
I retrieve data from two different repositories:
List<F> allFs = fRepository.GetFs().ToList();
List<E> allEs = eRepository.GetEs().ToList();
Now I need to join them so I do the following:
var EFs = from c in allFs.AsQueryable()
join e in allEs on c.SerialNumber equals e.FSerialNumber
where e.Year == Convert.ToInt32(billingYear) &&
e.Month == Convert.ToInt32(billingMonth)
select new EReport
{
FSerialNumber = c.SerialNumber,
FName = c.Name,
IntCustID = Convert.ToInt32(e.IntCustID),
TotalECases = 0,
TotalPrice = "$0"
};
How can I make this LINQ query better so it will run faster? I would appreciate any suggestions.
Thanks
Unless you're able to create one repository that contains both pieces of data, which would be a far preferred solution, I can see the following things which might speed up the process.
Since you'r always filtering all E's by Month and Year, you should do that before calling ToList on the IQueryable, that way you reduce the number of E's in the join (probably considerably)
Since you're only using a subset of fields from E and F, you can use an anonymous type to limit the amount of data to transfer
Depending on how many serialnumbers you're retrieving from F's, you could filter your E's by serials in the database (or vice versa). But if most of the serialnumbers are to be expected in both sets, that doesn't really help you much further
Reasons why you might not be able to combine the repositories into one are probably because the data is coming from two separate databases.
The code, updated with the above mentioned points 1 and 2 would be similar to this:
var allFs = fRepository.GetFs().Select(f => new {f.Name, f.SerialNumber}).ToList();
int year = Convert.ToInt32(billingYear);
int month = Convert.ToInt32(billingMonth);
var allEs = eRepository.GetEs().Where(e.Year == year && e.Month == month).Select(e => new {e.FSerialNumber, e.IntCustID}).ToList();
var EFs = from c in allFs
join e in allEs on c.SerialNumber equals e.FSerialNumber
select new EReport
{
FSerialNumber = c.SerialNumber,
FName = c.Name,
IntCustID = Convert.ToInt32(e.IntCustID),
TotalECases = 0,
TotalPrice = "$0"
};
I'm building an ArchivesController for the open source asp.net MVC-3 blog platform FunnelWeb. We have an model called "Entry" which represents a blog entry which has a DateTime property called "Published" for when this entry was published. The purpose of the proposed ArchivesController is to create a wordpress-like archives link table that shows a descending list of all years and months for which we have posts with links to an archive index like '/archive/2011/9' and a count for the number of posts in the year/month.
ex:
December 2011 (2 posts)
November 2011 (4 posts)
October 2011 (1 post)
I'm not experienced with NHibernate and so wrote the initial query using linq like this:
public class GetArchiveDatesQuery : IQuery<ArchiveDate>
{
public System.Collections.Generic.IEnumerable<ArchiveDate> Execute(ISession session, IDatabaseProvider databaseProvider)
{
var criteria = session.QueryOver<Entry>();
var archiveDates = from entry in criteria.List<Entry>()
group entry by new { entry.Published.Year, entry.Published.Month } into entryGroup
orderby entryGroup.Key.Year descending, entryGroup.Key.Month descending
select new ArchiveDate()
{
Year = entryGroup.Key.Year,
Month = entryGroup.Key.Month,
EntryCount = entryGroup.Count()
};
return archiveDates;
}
}
Where ArchiveDate is a new model I created to encapsulate the year-month-count information from this query.
This works, but I'd prefer to push the work off to SQL instead of doing the grouping and sorting in C#. I imagine on an active blog that has been around for several years with hundreds or thousands of posts would be much better off to do this in SQL so we don't return unnecessary data (like the entry content).
My question is how we can accomplish the above LINQ statement in an NHibernate fashion which results in the grouping/sorting occurring in SQL. I imagine it will involve some Criteria->Projection->Transformation sort of process.
The part I'm stuck on is accessing the month portion and year portion of the DateTime property for grouping and sorting which is currently accessed by the .Net DateTime object.
The blog engine is using NHibernate version 3.2.0.4000.
Update:
var query = from e in session.Query<Entry>()
group e by new { e.Published.Year, e.Published.Month } into entryGroup
select new
{
entryGroup.First().Published,
EntryCount = entryGroup.Count()
};
var archiveDates = from a in query.AsEnumerable()
orderby a.Published.Year descending, a.Published.Month descending
select new
{
Year = a.Published.Year,
Month = a.Published.Month,
EntryCount = a.EntryCount,
};
Original Answer:
could you try NHibernates LINQ-provider first and post the errors?
using NHibernate.Linq;
var archiveDates = from entry in session.Query<Entry>()
group entry by new { entry.Published.Year, entry.Published.Month } into entryGroup
orderby entryGroup.Key.Year descending, entryGroup.Key.Month descending
select new ArchiveDate
{
Year = entryGroup.Key.Year,
Month = entryGroup.Key.Month,
EntryCount = entryGroup.Count()
};
I need some help with LINQ-2-SQL in order to group some blog posts by a year and month.
Basically, I have a collection of blog posts that have the following properties
Id
Title
Date
I want to be able to iterate through each year, then iterate through every month of that year, and finally, iterate through every blog post within that month. Something like
2011
April (show number of posts)
Random Post 1
Random Post 2
May (show number of posts)
Random Post 2
etc...
Is there a way I can do this with a single LINQ query, using the group by clause?
Here's as far as I've got
var groupedBlogPosts = (from p in blogPostsFiltered
group p by new { month = p.Date.Month, year = p.Date.Year } into d
select new { postDate = string.Format("{0}/{1}", d.Key.month, d.Key.year), postCount = d.Count() });
I haven't actually tested this, but it looks like a start, wrote this by looking at this msdn article:
var groupedBlogPosts =
from p in blogPostsFiltered
group p by p.Date.Year into yg
select
new
{
Year = yg.Key,
MonthGroups =
from o in yg
group o by o.Date.Month into mg
select new { Month = mg.Key, Posts = mg }
};
Look under the GroupBy - Nested heading.
Here's an alternative if you don't want to do the projections and leave them as groupings.
var groupedBlogPosts =
from post in blogPostsFiltered
group post by new { post.Date.Year, post.Date.Month } into grouped
group grouped by new { grouped.Key.Year };