Distinct Count x and Grouping by Date y - c#

I'm trying to get from the following data in an SQL database
date | user |
(DateTime) | (string)|
--------------------------------------------------
2013-06-03 13:24:54.013 | 3 |
2013-06-04 13:25:54.013 | 5 |
2013-06-04 13:26:54.013 | 3 |
2013-06-04 13:27:54.013 | 3 |
a list in the form
date | DistinctCountUser
---------------------------------
2013-06-03 | 1
2013-06-04 | 2
I've tried several ways to do this with linq but always end up with a) not the result I expected or b) a linq exception.

var result = input.GroupBy(x=>x.date.Date,(key,x)=> new {
date = key,
DistinctCountUser = x.Select(e=>e.user).Distinct().Count()
});

If you are using Entity Framework, then you should use EntityFunctions.TruncateTime to get date part of date time field:
from x in context.TableName
group x by EntityFunctions.TruncateTime(x.date) into g
select new {
date = g.Key,
DistinctCountUser = g.Select(x => x.user).Distinct().Count()
}
Otherwise use #KingKong answer

Here is how to use query expression when grouping in Linq. Query Expressions may be easier to read in some cases and I find grouping to be one of them.
from thing in things
group thing by thing.date.Date into g
select new {
Date = g.Key,
DistinctCountUser = g.Select(x => x.user).Distinct().Count()
}

Related

Insert values in col1 of list 1 from col 2 of list 2 based on a common column

List 1:
| User Id | Latest |
+---------------------+------------------+
| 1 | 1 |
| 2 | 3 |
| 3 | 3 |
| 4 | 0 |
List 2:
| User Id | Latest | Rating |
+---------------------+------------------+------------------+
| 1 | null | 10 |
| 2 | null | 12 |
| 3 | null | 11 |
| 4 | null | 16 |
I want to insert the values of the Latest column of List1 into the Latest column of List2 based on joining/comparing values of the User Id column in both lists.
I can use a foreach loop but that would run n*m I guess and look ugly. Is there a way to do it with LINQ or efficiently?
Regards.
Junaid
var result = from i1 in List1
join i2 in List2
on i1.UserId equals i2.UserId
select new
{
i2.UserId,
i1.Latest,
i2.Rating
};
you can do it with LINQ :
Try this code :
List2.ForEach(item1 =>
{
item1.Latest = List1.FirstOrDefault(item2 => item2.UserId == item1.UserId)?.Latest;
});
Note That, Latest must be Nullable.
LINQ will never change any of the source sequences, it can only extract data from it.
You will have to enumerate over the extracted data to update your original tables.
var recordsToUpdate = List2.Join(List1, // join List2 and List1
list2Row => list2Row.UserId, // from every row in List2 take UserId
list1Row => list1Row.UserId, // from every row in List1 take UserId
(list2Row, list1Row) => new // when they match make one new object
{
Id = list2Row.UserId, // take UserId from list2
Latest = list1Row.Latest, // take Latest from list1
Rating = list2Row.Rating, // take Rating from list2
})
.ToList(); // execute the query
I don't know how you update your records. Entity framework? SQL? it will be something like this:
foreach (var recordToUpdate in recordsToUpdate)
{
UpdateRecord(recordToUpdate.UserId, recordToUpdate.Latest, recordToUpdate.Rating)
// TODO: implement this function
}
Try something like this. this may fix your issue with adding the Latest value from List1 to List2.
List2.AddRange(List1.Select(user => new List1{
Latest = user.Latest,
UserID = user.UserID
}));

how to group by in LINQ and not select all columns

I am trying to write a LINQ query which is complex for me now since I am a newbie to LINQ.
I have a table like below...
UserId | CompanyId | ProblemDescription | CreatedTime | TimeSpentMins
-----------------------------------------------------------------------
1 | 95 | Sysmtem is crashed | 2016-01-01 15:23 | 25
1 | 95 | Total is incorrect | 2016-01-01 15:45 | 45
I want to write a LINQ query that will do the job below. CreatedTime has date and time but I want to group it by only date.
SELECT UserId, CompanyId,CreateTime Sum(TimeSpentMins)
FROM TransactionLogs
GROUP BY UserId, CompanyId, Convert(DATE,CreatedTime)
How can I write this LINQ? I wanted to put my code below but I got nothing :(
Simply use the GroupBy extension method and use EntityFunctions.TruncateTime method to get only the date part:-
var result = db.TransactionLogs
.GroupBy(x => new
{
CreateTime = EntityFunctions.TruncateTime(x.CreatedTime),
UserId,
CompanyId
})
.Select(x => new
{
UserId = x.Key.UserId,
CompanyId = x.Key.CompanyId,
CreateTime = x.Key.CreateTime,
TotalTimeSpentMins = x.Sum(z => z.TimeSpentMins)
});
try this one:
var result = db.TransactionLogs
.GroupBy(_ => new {
_.UserId, _.CompanyId, DbFunctions.TruncateTime(_.CreatedTime)})
.Select(_ => new {
_.UserId, _.CompanyId, DbFunctions.TruncateTime(_.CreatedTime),
Total = _.Sum(t => t.TimeSpentMins)});

How do I get a value adjacent to a Max(value) using Linq?

I have a table:
Group | BasalArea | SpeciesName
1 | 3.6 | Palustris
1 | 45.0 | MSO
2 | 4.2 | Oak
2 | 2.0 | MSO
...
From this table, I would like to get the species name with the highest basal area grouped by the Group field, which would look like this:
Group | BasalArea | SpeciesName
1 | 45.0 | MSO
2 | 4.2 | Oak
Using SQL, I can get the highest basal area:
SELECT Group, Max(BasalArea)
FROM TABLE
GROUP BY Group
I can't figure out how to also get the species name without doing some looping. Is this possible? What are the strategies for handling ties?
This is simpler in LINQ2SQL than in SQL:
var res = source.MyTable
.GroupBy(item => item.Group)
.Select(g => g.OrderByDescending(item => item.BasalArea).First())
.ToList();
This will return the list of items with largest values of BasalArea in its Group, together with SpeciesName.
In SQL you would need to join back to the original table, like this:
SELECT * FROM TABLE b
JOIN (
SELECT Group, Max(BasalArea) as BasalArea
FROM TABLE
GROUP BY Group
) t on t.Group = b.Group AND t.BasalArea = b.BasalArea
Try this:
var froup = categories.GroupBy(g => new {g.CategoryType})
.Select(g => g.OrderByDescending(i => i.CategoryID).First())
.ToArray();
What sasblinkenlight said would be the LINQ. Out of curiosity, here is a potential SQL solution.
SELECT grouped.Group, raw.SpeciesName, grouped.MaBasalArea
FROM (
SELECT Group, MAX(BasalArea) as MaxBasalArea
FROM TABLE
GROUP BY Group
) grouped
INNER JOIN TABLE raw ON grouped.MaxBasalArea = raw.BasalArea AND grouped.Group = raw.Group

Linq - Group by multiple tables

Using Linq to Sql how do i group the following 2 tables.
Orders Table:
CustomerID | Name |Date
1 | order1 | 2010-01-01
2 | order2 | 2010-01-01
2 | order3 | 2010-04-01
Calls Table:
CustomerID | Name |Date
1 | call1 | 2010-01-01
3 | call2 | 2010-06-01
2 | call3 | 2010-05-01
I want to group the two tables by date , Result:
Date | Orders | Calls
2010-01-01 | 2 | 1
2010-04-01 | 1 | 0
2010-05-01 | 0 | 1
2010-06-01 | 0 | 1
i know how to group a single table ,
from o in Orders
group o by o.Date.Date into og
select new {Date = og.Key,Orders= og.Count()};
how do i group both?
thx!
Since both tables seem to have a similar structure I'd recommend projecting both into an equivalent form and then group on the concatenation of those two sets.
var orders = from o in Orders
select new { IsOrder = true, o.Date };
var calls = from c in Calls
select new { IsOrder = false, c.Date };
var result = from x in orders.Concat(calls)
group x by x.Date into og
select new {Date = og.Key, Orders= og.Count(o=>o.IsOrder), Calls = og.Count(c=>!c.IsTrue)};
Due to the lazy nature of Linq2Sql this might actually be reduced to a single query. In the interest of performance I would make sure this is not a query from hell.
You can use the Union method:
var result =
(from c in Calls group c by c.Date into cg select new {Date = cg.Key, Calls = cg.Count(), Orders = 0})
.Union(from o in Orders group o by o.Date into og select new {Date = og.Key, Calls = 0, Orders = og.Count()})
.GroupBy(x => x.Date)
.Select(g => new {Date = g.Key, Calls = g.Max(r => r.Calls), Orders = g.Max(r => r.Orders)});
foreach (var row in result)
{
Trace.WriteLine(row);
}
This is very similar to the SQL you would write (a union of the two tables, and then an outer query to merge the results into a row)

List of unique strings in database table using Linq?

I have a "Tickets" table with somewhat following structure (removed unnecessary columns)
int | string | int |
ID | Window | Count |
------------------------
0 | Internet | 10 |
1 | Phone | 20 |
2 | Fax | 15 |
3 | Fax | 10 |
4 | Internet | 5 |
. | . | . |
. | . | . |
And I have mapped this table to a class "Ticket". So I can get all records like this:
var tickets = from t in db.Tickets
select t;
Now I need to get the list of unique window names in the table. For above table, list would look something like:
Internet
Phone
Fax
Is there anyway to create this list without fetching all records and iterating over them?
I am using SQL Server 2008 express edition.
EDIT:
Thanks for the answers guys it solved the above problem. Just being greedy but is there any way to also get the total of count for each window. For example:
Internet = 15
Phone = 25
Fax = 20
How about:
var tickets = db.Tickets.Select(t => t.Window).Distinct();
I prefer to only use query expressions when I'm doing more than one operation, but if you like them the equivalent is:
var tickets = (from t in db.Tickets
select t.Window).Distinct();
To get the counts, you need to group:
var tickets = from t in db.Tickets
group t by t.Window into grouped
select new { Window=grouped.Key,
Total=grouped.Sum(x => x.Count) };
foreach (var entry in tickets)
{
Console.WriteLine("{0}: {1}", entry.Window, entry.Total);
}
Note that this should all end up being performed at the database side - examine the SQL query to check this.
var query2 = from ticket in db.tickets
group window by ticket.Window into result
select new
{
Name = result.Window,
Sum = result.Sum(i => i.Count)
};
The query will be evaluated inside the store.
var windows = db.Tickets.Select(ticket => ticket.Window).Distinct();
Linq Samples Part 11 by Bill Wagner should help you. Just call the Distinct() function on your Linq result. It's as simple as that.
var tickets = (from t in db.Tickets
select t).Distinct();
[EDIT]
Concering the numbers of the occurences, see this example as a hint.
int[] numbers = { 5, 4, 1, 3, 9, 8, 6, 7, 2, 5 };
var numberGroups =
from n in numbers
group n by 5 into g
select g;
g.Count(); // occurences
You can use the .Distinct() operator - it'll make a SELECT DISTINCT to the database, giving exactly what you ask for.

Categories