Linq query using Group by and Joins - c#

I have two tables:
Banner
--------------------------------------------------------|
BId | Name | Link | Image | AltText| Target | BActive|
--------------------------------------------------------
|1 | hello| http| a.jpg |helloimg| | 1 |
--------------------------------------------------------
Tracking
------------------------------------------------------
|TId | BId| ICount| CCount| CreateDate |
------------------------------------------------------
|1 | 1 | 102 | 300 | 2015-11-17 00:00:000 |
|2 | 1 | 182 | 100 | 2015-11-14 00:00:000 |
|3 | 1 | 192 | 200 | 2015-11-12 00:00:000 |
------------------------------------------------------
I want to find the Sum of ICount and CCound for each BId between 2015-11-12 and 2015-11-15.
Here is the code I have tried:
from p in Bannertables
join q in BannerTrackings
on p.BannerId equals q.BannerId
group p by p.BannerId into myTab
select new {
BannerId = myTab.Key,
ImpressionCount = myTab.Sum( x=> x.ImpressionCount),
ClickCount = myTab.Sum(x => x.ClickCount)
};
How do I sort the result according to the date range I specified?

Add the followingwhere to your query:
var d1 = new DateTime(2015, 11, 12);
var d2 = new DateTime(2015, 11, 15);
var query=from p in Bannertables
join q in BannerTrackings
on p.BannerId equals q.BannerId
where q.CreateDate>=d1 && q.CreateDate<=d2
group q by p.BannerId into myTab
select new {
BannerId = myTab.Key,
ImpressionCount = myTab.Sum( x=> x.ImpressionCount),
ClickCount = myTab.Sum(x => x.ClickCount)
};
Update
If you want to get access to the Banner's properties, include it in the group as I show below:
var d1 = new DateTime(2015, 11, 12);
var d2 = new DateTime(2015, 11, 15);
var query=from p in Bannertables
join q in BannerTrackings
on p.BannerId equals q.BannerId
where q.CreateDate>=d1 && q.CreateDate<=d2
group new{Banner=p,Tracking=q} by p.BannerId into myTab
select new {
BannerId = myTab.Key,
Bannername=myTab.Select(e=>e.Banner.Name).FirstOrDefault(),
ImpressionCount = myTab.Sum( x=> x.Tracking.ImpressionCount),
ClickCount = myTab.Sum(x => x.Tracking.ClickCount)
};
But if you only want the banner name this is even better:
var d1 = new DateTime(2015, 11, 12);
var d2 = new DateTime(2015, 11, 15);
var query=from p in Bannertables
join q in BannerTrackings
on p.BannerId equals q.BannerId
where q.CreateDate>=d1 && q.CreateDate<=d2
group q by new {p.BannerId,p.Name} into myTab
select new {
BannerId = myTab.Key.BannerId,
BannerName=myTab.Key.Name,
ImpressionCount = myTab.Sum( x=> x.ImpressionCount),
ClickCount = myTab.Sum(x => x.ClickCount)
};

Related

Query with join, group by and count to linq

im doing this query on sql and it works, but how do i make it to Linq ?
select b.Name,a.idempresa,count(1) as cuenta from Descarga_XML_recepcion_V2 as a
inner join EnterpriseSGE as b on a.idempresa = b.EnterpriseSGEId group by idempresa,b.name
this is what it should bring
name1 | 5041 | 583
name2 | 4031 | 1730
name3 | 5042 | 640
name4 | 4034 | 300
name5 | 6047 | 861
name6 | 5043 | 187
name7 | 4033 | 318
A straight forward translation of the SQL into LINQ yields:
var ans = from a in Descarga_XML_recepcion_V2
join b in EnterpriseSGE on a.idempresa equals b.EnterpriseSGEId
group 1 by new { a.idempresa, b.name } into ingroup
select new {
ingroup.Key.idempresa,
ingroup.Key.name,
cuenta = ingroup.Count()
};
Try :
var results = (from a in Descarga_XML_recepcion_V2
join b in EnterpriseSGE on a.idempresa equal b.EnterpriseSGEId
select new { a = a, b = b})
.GroupBy(x => new { idempresa = x.a.idempresa, name = x.b.name})
.Select(x => new {name = x.Key.name, idempresa = x.Key.idempressa, count = x.Count()})
.ToList();

How to GROUP BY without breaking order?

I have a very simple ordered table with the following data:
=============================
| Timestamp | Count |
=============================
| 12/30/2016 | 322 |
| 12/29/2016 | 322 |
| 12/28/2016 | 322 |
| 12/4/2016 | 541 |
| 12/3/2016 | 541 |
| 12/2/2016 | 541 |
| 12/1/2016 | 322 |
| 11/30/2016 | 322 |
| 11/29/2016 | 322 |
=============================
I would like to SELECT MAX([Timestamp]), Count FROM [dbo].[Table] GROUP BY [Count] without breaking [Timestamp] order, i.e. the output should be:
=============================
| Timestamp | Count |
=============================
| 12/30/2016 | 322 |
| 12/4/2016 | 541 |
| 12/1/2016 | 322 |
=============================
So 12/1/2016 | 322 is not hidden by the GROUP BY.
Does anybody know how to write such query? I need an EntityFramework Linq query and it would be great to know a T-SQL analogue.
I think you need a query like this:
select [Timestamp], [Count]
from (
select *
--//Step 3: Now just find the max value
, row_number() over (partition by seqGroup order by [Timestamp] desc) as seq
from (
select *
--//Step 2: you need to generate a group number for each sequence
, sum(changed) over (order by [Timestamp] desc) as seqGroup
from (
select *
--//Step 1: you need to track changes over [Count]
, case when coalesce(lag([Count]) over (order by [Timestamp] desc), [Count]) = [Count] then 0 else 1 end as changed
from yourTable ) t1
) t2
) t3
where seq = 1;
I see previous answer, and on the his basis written LINQ request.
1) Create you initTable:
var initTable = new List<Tuple<DateTime, int>>();
initTable.Add(Tuple.Create(new DateTime(2016, 12, 30), 322));
initTable.Add(Tuple.Create(new DateTime(2016, 12, 29), 322));
initTable.Add(Tuple.Create(new DateTime(2016, 12, 28), 322));
initTable.Add(Tuple.Create(new DateTime(2016, 12, 4), 541));
initTable.Add(Tuple.Create(new DateTime(2016, 12, 3), 541));
initTable.Add(Tuple.Create(new DateTime(2016, 12, 2), 541));
initTable.Add(Tuple.Create(new DateTime(2016, 12, 1), 322));
initTable.Add(Tuple.Create(new DateTime(2016, 11, 30), 322));
initTable.Add(Tuple.Create(new DateTime(2016, 11, 29), 322));
2) Add row index in initTable and order by Timestamp:
//get row index
var table = initTable
.Where(x => true)
.Select((x, i) => new
{
Timestamp = x.Item1.ToString("MM-dd-yyyy"),
Count = x.Item2,
//row index
Index = i
})
.OrderByDescending(x => x.Timestamp);
3) Query
var query = table
//#1 get prev row count
.Select(x => new
{
x.Timestamp,
x.Count,
x.Index,
//previous row count (-1 for first row, not default null)
Prev = table.LastOrDefault(y => y.Index < x.Index) == null ?
-1 :
table.LastOrDefault(y => y.Index < x.Index).Count
})
//#2 set group flag
.Select(x => new
{
x.Timestamp,
x.Count,
x.Index,
x.Prev,
GroupId = x.Count == x.Prev
})
//#3
.Where(x => x.GroupId == false)
.Select(x => new
{
x.Timestamp,
x.Count
});
You can insert it into LinqPad and verify result.
I hope this will be helpfully.

C# linq query from sql server into one table

I have three tables. I would like to used C# linq to turn into one table.
For example:
Schedule:
+------+--------+--------+
| Name | DateId | TaskId |
+------+--------+--------+
| John | 2 | 32 |
| John | 3 | 31 |
| Mary | 1 | 33 |
| Mary | 2 | 31 |
| Tom | 1 | 34 |
| Tom | 2 | 31 |
| Tom | 3 | 33 |
+------+--------+--------+
Date:
+----+------------+
| Id | Date |
+----+------------+
| 1 | Monday |
| 2 | Tuesday |
| 3 | Wednesday |
| 4 | Thursday |
| 5 | Friday |
+----+------------+
Task:
+----+----------+
| Id | Task |
+----+----------+
| 31 | School |
| 32 | Homework |
| 33 | Break |
| 34 | Teaching |
+----+----------+
I would like to have a table like this:
+--------+----------+----------+-----------+----------+
| Person | Monday | Tuesday | Wednesday | Thursday |
+--------+----------+----------+-----------+----------+
| John | | Homework | School | |
| Mary | Break | School | | |
| Tom | Teaching | School | Break | |
+--------+----------+----------+-----------+----------+
I could not think of any good way doing this.
Any suggestion would be helpful
Thanks
Starting from this set of data:
var schedules = new[] { new { Name = "John", DateId = 2, TaskId = 32},
new { Name = "John", DateId = 3, TaskId = 31},
new { Name = "Mary", DateId = 1, TaskId = 33},
new { Name = "Mary", DateId = 2, TaskId = 31},
new { Name = "Tom", DateId = 1, TaskId = 34},
new { Name = "Tom", DateId = 2, TaskId = 31},
new { Name = "Tom", DateId = 3, TaskId = 33}
};
var dates = new[] { new { DateId = 1, Desc = "Monday"},
new { DateId = 2, Desc = "Tuesday"},
new { DateId = 3, Desc = "Wednesday"},
new { DateId = 4, Desc = "Thursday"},
new { DateId = 5, Desc = "Friday"}
};
var tasks = new[] { new { TaskId = 31, Desc = "School"},
new { TaskId = 32, Desc = "Homework"},
new { TaskId = 33, Desc = "Break"},
new { TaskId = 34, Desc = "Teaching"}
};
You can do as follows:
var result = schedules
// First you join the three tables
.Join(dates, s => s.DateId, d => d.DateId, (s, d) => new {s, d})
.Join(tasks, s => s.s.TaskId, t => t.TaskId, (sd, t ) => new { Person = sd.s, Date = sd.d, Task = t })
// Then you Group by the person name
.GroupBy(j => j.Person.Name)
// Finally you compose the final object extracting from the list of task the correct task for the current day
.Select(group => new
{
Person = group.Key,
Monday = group.Where(g => g.Date.DateId == 1).Select(g => g.Task.Desc).FirstOrDefault(),
Tuesday = group.Where(g => g.Date.DateId == 2).Select(g => g.Task.Desc).FirstOrDefault(),
Wednesday = group.Where(g => g.Date.DateId == 3).Select(g => g.Task.Desc).FirstOrDefault(),
Thursday = group.Where(g => g.Date.DateId == 4).Select(g => g.Task.Desc).FirstOrDefault(),
Friday = group.Where(g => g.Date.DateId == 5).Select(g => g.Task.Desc).FirstOrDefault()
})
.ToList();
If you want to select only some days, you can return an object containing a dictionary instead of an object with a property per day.
The dictionary will contain key-value pairs with the key representing the day and the value representing the task.
See the following code:
var filter = new[] {2, 3};
var filteredResult = schedules
.Join(dates, s => s.DateId, d => d.DateId, (s, d) => new{ s, d})
.Join(tasks, s => s.s.TaskId, t => t.TaskId, (sd, t) => new { Person = sd.s, Date = sd.d, Task = t })
.Where(x => filter.Contains(x.Date.DateId))
.GroupBy(x => x.Person.Name)
.Select(group => new
{
Person = group.Key,
TasksByDay = group.ToDictionary(o => o.Date.Desc, o => o.Task.Desc)
})
.ToList();
foreach (var item in filteredResult)
{
System.Console.WriteLine(item.Person);
foreach (var keyvaluepair in item.TasksByDay)
{
System.Console.WriteLine(keyvaluepair.Key + " - " + keyvaluepair.Value);
}
System.Console.WriteLine("---");
}
It is called "transpose".
var persons = new[] { new { name="John", dateId=2,taskId=32},
new { name="John", dateId=3,taskId=31},
new { name="Mary", dateId=1,taskId=33},
new { name="Mary", dateId=2,taskId=31},
new { name="Tom", dateId=1,taskId=34},
new { name="Tom", dateId=2,taskId=31},
new { name="Tom", dateId=3,taskId=33}
};
var dates = new[] { new { dateId=1, desc="Monday"},
new { dateId=2, desc="Tuesday"},
new { dateId=3, desc="Wednesday"},
new { dateId=4, desc="Thursday"},
new { dateId=5, desc="Friday"}
};
var tasks = new[] { new { taskId=31, desc="School"},
new { taskId=32, desc="Homework"},
new { taskId=33, desc="Break"},
new { taskId=34, desc="Teaching"}
};
var qry = from p in (from p in persons
join d in dates on p.dateId equals d.dateId
join t in tasks on (int)p.taskId equals (int)t.taskId
select new { name = p.name, monday = d.dateId == 1 ? t.desc : "", tuesday = d.dateId == 2 ? t.desc : "", wednesday = d.dateId == 3 ? t.desc : "", thursday = d.dateId == 4 ? t.desc : "", friday = d.dateId == 5 ? t.desc : "" })
group p by p.name into q
select new { q.Key, monday=q.Max(a => a.monday),tuesday=q.Max(a => a.tuesday), wednesday = q.Max(a=>a.wednesday), thursday = q.Max(a => a.thursday), friday=q.Max(a => a.friday)};
foreach ( var a in qry.ToList())
{
Console.WriteLine(String.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}",a.Key, a.monday, a.tuesday, a.wednesday, a.thursday, a.friday));
}

Combine tables using row values as column LINQ C# SQL

I have a users table:
Id | Name | Age
--------------------
1 | Steve | 21
2 | Jack | 17
3 | Alice | 25
4 | Harry | 14
I also have a table containing additional user info:
UId | Key | Value
----------------------
1 | Height | 70
2 | Height | 65
2 | Eyes | Blue
4 | Height | 51
3 | Hair | Brown
1 | Eyes | Green
The UId column links to the Id column in the users table. As you can see, not all users have the same additional info present. Alice doesn't have a height value, Jack is the only one with an eye color value etc.
Is there a way to combine this data into one table dynamically using C# and LINQ queries so that the result is something like this:
Id | Name | Age | Height | Eyes | Hair
------------------------------------------
1 | Steve | 21 | 70 | Green |
2 | Jack | 17 | 65 | Blue |
3 | Alice | 25 | | | Brown
4 | Harry | 14 | 51 |
If a user does not have a value for the column, it can remain empty/null. Does this require some sort of data pivoting?
For the case, your user info fields are constant:
var result = users.GroupJoin(details,
user => user.Id,
detail => detail.Id,
(user, detail) => new
{
user.Id,
user.Name,
user.Age,
Height = detail.SingleOrDefault(x => x.Key == "Height").Value,
Eyes = detail.SingleOrDefault(x => x.Key == "Eyes").Value,
Hair = detail.SingleOrDefault(x => x.Key == "Hair").Value,
});
You can do it by utilising GroupJoin, example:
var users = new List<Tuple<int, string, int>> {
Tuple.Create(1, "Steve", 21),
Tuple.Create(2, "Jack", 17),
Tuple.Create(3, "Alice", 25),
Tuple.Create(4, "Harry", 14)
};
var userInfos = new List<Tuple<int, string, string>> {
Tuple.Create(1, "Height", "70"),
Tuple.Create(2, "Height", "65"),
Tuple.Create(2, "Eyes", "Blue"),
Tuple.Create(4, "Height", "51"),
Tuple.Create(3, "Hair", "Brown"),
Tuple.Create(1, "Eyes", "Green"),
};
var query = users.GroupJoin(userInfos,
u => u.Item1,
ui => ui.Item1,
(u, infos) => new { User = u, Infos = infos });
var result = query.Select(qi => new
{
Id = qi.User.Item1,
Name = qi.User.Item2,
Age = qi.User.Item3,
Height = qi.Infos.Where(i => i.Item2 == "Height").Select(i => i.Item3).SingleOrDefault(),
Eyes = qi.Infos.Where(i => i.Item2 == "Eyes").Select(i => i.Item3).SingleOrDefault(),
Hair = qi.Infos.Where(i => i.Item2 == "Hair").Select(i => i.Item3).SingleOrDefault()
});
First of all I have grouped the user details data using Feature (I have renamed the Key property with Feature to avoid confusion) & UId then I have used group join to combine both results using into g. Finally retrieved the result using specified feature.
var result = from user in users
join detail in details.GroupBy(x => new { x.UId, x.Feature })
on user.Id equals detail.Key.UId into g
select new
{
Id = user.Id,
Name = user.Name,
Age = user.Age,
Height = g.FirstOrDefault(z => z.Key.Feature == "Height") != null ?
g.First(z => z.Key.Feature == "Height").First().Value : String.Empty,
Eyes = g.FirstOrDefault(z => z.Key.Feature == "Eyes") != null ?
g.First(z => z.Key.Feature == "Eyes").First().Value : String.Empty,
Hair = g.FirstOrDefault(z => z.Key.Feature == "Hair") != null ?
g.First(z => z.Key.Feature == "Hair").First().Value : String.Empty,
};
I am getting following output:-
Here is the complete Working Fiddle.
Try this
var list = (from u in context.users
join ud in context.UserDetails on u.Id equals ud.UId
select new
{
u.Id,
u.Name,
u.Age,
ud.Key,
ud.Value
});
var finallist = list.GroupBy(x => new { x.Id, x.Name,x.Age}).Select(x => new
{
x.Key.Id,
x.Key.Name,
x.Key.Age,
Height = x.Where(y => y.Key == "Height").Select(y => y.Value).FirstOrDefault(),
Eyes = x.Where(y => y.Key == "Eyes").Select(y => y.Value).FirstOrDefault(),
Hair = x.Where(y => y.Key == "Hair").Select(y => y.Value).FirstOrDefault()
}).ToList();
try this query
var objlist=( form a in contex.user
join b in contex.UserDetails on a.id equals a.Uid into gj
from subpet in gj.DefaultIfEmpty()
select new { Id=a.id, Name=a.name, Age=a.age, Height =subpet.Height,Eyes=subpet.Eyes, Hair=subpet.Hair}).ToList();

Complex SQL JOIN Query, min, max and date range

I have the following tables:
Readings:
+----+---------------------+-------+----------+
| Id | TimestampLocal | Value | Meter_Id |
+----+---------------------+-------+----------+
| 1 | 2014-08-22 18:05:03 | 50.5 | 1 |
| 2 | 2013-08-12 14:02:09 | 30.2 | 1 |
+----+---------------------+-------+----------+
Meters:
+----+--------+
| Id | Number |
+----+--------+
| 1 | 32223 |
+----+--------+
I need to select 2 readings for each meter, the reading with max DateTime and the reading with min DateTime, in addition to the difference between values of the two readings, something like this:
+----------+------------+----------------+------------+----------------+------------+
| Meter_Id | MaxReading | MaxReadingTime | MinReading | MinReadingTime | Difference |
+----------+------------+----------------+------------+----------------+------------+
I need a single query to achieve this for all meters within a date range in Entity Framework
i was able to get this far (get max and min readings):
SELECT
tt.*
FROM Readings tt
INNER JOIN
(
SELECT
Meter_Id,
MAX(TimeStampLocal) AS MaxDateTime,
MIN(TimeStampLocal) AS MinDateTime
FROM Readings
where TimeStampLocal > '2014-12-08'
GROUP BY Meter_Id
) AS groupedtt
ON (tt.Meter_Id = groupedtt.Meter_Id) AND
(tt.TimeStampLocal = groupedtt.MaxDateTime or tt.TimeStampLocal = groupedtt.MinDateTime)
order by Meter_Id;
Using this mockup of your actual schema and data:
class Reading
{
public int Id { get; set; }
public DateTime TimestampLocal { get; set; }
public double Value { get; set; }
public int Meter_Id { get; set; }
}
List<Reading> Readings = new List<Reading>()
{
new Reading { Id = 1, TimestampLocal = new DateTime(2014, 8, 22), Value = 50.5, Meter_Id = 1 },
new Reading { Id = 2, TimestampLocal = new DateTime(2013, 8, 12), Value = 30.2, Meter_Id = 1 },
new Reading { Id = 3, TimestampLocal = new DateTime(2013, 9, 12), Value = 35.2, Meter_Id = 1 }
};
using this linq query:
var q = from r in Readings
group r by r.Meter_Id into rGroup
select new
{
Meter_Id = rGroup.Key,
MaxReading = rGroup.OrderByDescending(x => x.TimestampLocal).First().Id,
MaxReadingTime = rGroup.OrderByDescending(x => x.TimestampLocal).First().TimestampLocal,
MinReading = rGroup.OrderBy(x => x.TimestampLocal).First().Id,
MinReadingTime = rGroup.OrderBy(x => x.TimestampLocal).First().TimestampLocal,
Difference = rGroup.OrderByDescending(x => x.TimestampLocal).First().Value -
rGroup.OrderBy(x => x.TimestampLocal).First().Value
};
produces this output:
[0] = { Meter_Id = 1, MaxReading = 1, MaxReadingTime = {22/8/2014 12:00:00 πμ},
MinReading = 2, MinReadingTime = {12/8/2013 12:00:00 πμ}, Difference = 20.3 }
which should be close to expected result.
EDIT:
You can considerably simplify the above linq query by making use of the let clause:
var q = from r in Readings
group r by r.Meter_Id into rGroup
let MaxReading = rGroup.OrderByDescending(x => x.TimestampLocal).First()
let MinReading = rGroup.OrderBy(x => x.TimestampLocal).First()
select new
{
Meter_Id = rGroup.Key,
MaxReading = MaxReading.Id,
MaxReadingTime = MaxReading.TimestampLocal,
MinReading = MinReading.Id,
MinReadingTime = MinReading.TimestampLocal,
Difference = MaxReading.Value - MinReading.Value
};
Probably not the most efficient, I admit, but that's the quickest I could come up with something without counter-verifying it myself.
SELECT m.Id AS Meter_Id, MaxReading, MaxReadingTime, MinReading, MinReadingTime, (MaxReading - MinReading) AS Difference
FROM Meters m
CROSS APPLY (SELECT MIN(Value) MinReading, TimestampLocal AS MinReadingTime FROM Readings WHERE Meter_Id = m.Id) min
CROSS APPLY (SELECT MAX(Value) MaxReading, TimestampLocal AS MaxReadingTime FROM Readings WHERE Meter_Id = m.Id) max
edit: formatting.

Categories

Resources