I have a SQL Server table having data as shown below and I need a C# Linq query for SQL Server's Row_Number() over (partition by multiple column)
windowing function. Any help would be appreciated
Table data:
locationid | ContractorID | ResourceID | ST | OT | DT | CostDate | AFEID |
---|---|---|---|---|---|---|---|
15 | 17570 | 37450 | 48.22 | 66.78 | 96.44 | 2022-07-20 | 1093 |
15 | 17570 | 37450 | 35.46 | 49.11 | 70.92 | 2022-07-21 | 1093 |
15 | 17570 | 37450 | 54.60 | 75.62 | 109.20 | 2022-07-19 | 1093 |
15 | 17570 | 37450 | 53.90 | 74.64 | 107.80 | 2022-07-20 | 1093 |
15 | 17571 | 37450 | 25.53 | 35.36 | 51.06 | 2022-07-20 | 1093 |
15 | 17571 | 37625 | 70.92 | 98.21 | 141.84 | 2022-07-20 | 1093 |
15 | 17571 | 37450 | 87.93 | 121.78 | 175.86 | 2022-07-20 | 1093 |
15 | 17571 | 37450 | 51.06 | 70.71 | 102.12 | 2022-07-19 | 1093 |
15 | 17570 | 37680 | 60.99 | 84.46 | 121.98 | 2022-07-20 | 1093 |
15 | 17570 | 37680 | 53.90 | 74.64 | 107.80 | 2022-07-19 | 1093 |
15 | 17570 | 37478 | 53.90 | 74.64 | 107.80 | 2022-07-19 | 1093 |
SQL query which needs to be converted to Linq:
SELECT
LocationID,
AFEID,
ContractorID,
ResourceID,
MAX(ST) AS MaxST,
MAX(OT) AS MaxOT,
MAX(DT) AS MaxDT,
AVG(ST) AS AvgST,
AVG(OT) AS AvgOT,
AVG(DT) AS AvgDT,
MIN(ST) AS MinST,
MIN(OT) AS MinOT,
MIN(DT) AS MinDT,
CostDate,
ROW_NUMBER() OVER (PARTITION BY afeid, contractorid, resourceid
ORDER BY costdate DESC) AS rownum
FROM
tbldata
GROUP BY
LocationID, AFEID, ContractorID,
ResourceID, CostDate
I tried this Linq query, but had no success with it:
tbldata.OrderByDescending(x => x.CostDate)
.AsEnumerable()
.GroupBy(x => new
{
x.LocationId,
x.AfeId,
x.ContractorId,
x.ResourceId,
x.CostDate,
})
.Select(grp => new
{
grp.Key.AfeId,
grp.Key.ContractorId,
grp.Key.ResourceId,
grp.Key.LocationId,
grp.Key.CostDate,
MaxST = grp.Max(x => x.StandardTime),
MaxOT = grp.Max(x => x.Overtime),
MaxDT = grp.Max(x => x.DualTime),
AvgST = grp.Average(x => x.StandardTime),
AvgOT = grp.Average(x => x.Overtime),
AvgDT = grp.Average(x => x.DualTime),
MinST = grp.Min(x => x.StandardTime),
MinOT = grp.Min(x => x.Overtime),
MinDT = grp.Min(x => x.DualTime),
count = grp.Count(),
rownum = grp.Zip(Enumerable.Range(1, grp.Count()), (j, i) => new { rownum = i}).FirstOrDefault()
});
Output Required using the Linq query:
rownum | LocationID | FEID | ContractorID | ResourceID | MaxST | MaxOT | MaxDT | AvgST | AvgOT | AvgDT | MinST | MinOT | MinDT | CostDate |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | 15 | 1093 | 17570 | 37450 | 35.46 | 49.11 | 70.92 | 35.460000 | 49.11 | 70.920 | 35.46 | 49.11 | 70.92 | 2022-07-21 |
2 | 15 | 1093 | 17570 | 37450 | 53.90 | 74.64 | 107.80 | 51.060000 | 70.71 | 102.12 | 48.22 | 66.78 | 96.44 | 2022-07-20 |
3 | 15 | 1093 | 17570 | 37450 | 54.60 | 75.62 | 109.20 | 54.600000 | 75.62 | 109.20 | 54.60 | 75.62 | 109.20 | 2022-07-19 |
1 | 15 | 1093 | 17570 | 37478 | 53.90 | 74.64 | 107.80 | 53.900000 | 74.64 | 107.80 | 53.90 | 74.64 | 107.80 | 2022-07-19 |
1 | 15 | 1093 | 17570 | 37680 | 60.99 | 84.46 | 121.98 | 60.990000 | 84.46 | 121.98 | 60.99 | 84.46 | 121.98 | 2022-07-20 |
2 | 15 | 1093 | 17570 | 37680 | 53.90 | 74.64 | 107.80 | 53.900000 | 74.64 | 107.80 | 53.90 | 74.64 | 107.80 | 2022-07-19 |
1 | 15 | 1093 | 17571 | 37450 | 87.93 | 121.78 | 175.86 | 56.730000 | 78.57 | 113.46 | 25.53 | 35.36 | 51.06 | 2022-07-20 |
2 | 15 | 1093 | 17571 | 37450 | 51.06 | 70.71 | 102.12 | 51.060000 | 70.71 | 102.12 | 51.06 | 70.71 | 102.12 | 2022-07-19 |
1 | 15 | 1093 | 17571 | 37625 | 70.92 | 98.21 | 141.84 | 70.920000 | 98.21 | 141.84 | 70.92 | 98.21 | 141.84 | 2022-07-20 |
Please help me out.
CodePudding user response:
If I correctly understand what needs to be obtained, it has been simplified to illustrate the solution:
using System.Collections.Generic;
using Xunit;
using System.Linq;
namespace TestProject1
{
public class TestGrouping
{
[Fact]
public void Test()
{
var data = new List<Item>{
new Item{Location = "A", Contractor = "John", SomeValue = 10},
new Item{Location = "A", Contractor = "John", SomeValue = 12},
new Item{Location = "B", Contractor = "John", SomeValue = 10},
new Item{Location = "C", Contractor = "Tomm", SomeValue = 13},
};
var res =
data
.GroupBy(
item => new { Location = item.Location },
(key, group) =>
group.Select((x, i) =>
new
{
Row = i 1,
x.Location,
x.Contractor,
SumOfSomeValue = group.Sum(x => x.SomeValue)
}))
.SelectMany(reportItem => reportItem)
.ToList();
}
}
public class Item
{
public string Location;
public string Contractor;
public int SomeValue;
}
}
CodePudding user response:
The q in Linq stands for query. It is not meant modifying data. See my solution below
using System;
using System.Linq;
using System.Text;
using System.Collections;
using System.Collections.Generic;
using System.Data;
namespace ConsoleApp2
{
class Program
{
static void Main(string[] args)
{
DataTable tbldata = new DataTable();
tbldata.Columns.Add("locationid", typeof(int));
tbldata.Columns.Add("ContractorID", typeof(int));
tbldata.Columns.Add("ResourceID", typeof(int));
tbldata.Columns.Add("ST", typeof(decimal));
tbldata.Columns.Add("OT", typeof(decimal));
tbldata.Columns.Add("DT", typeof(decimal));
tbldata.Columns.Add("CostDate", typeof(DateTime));
tbldata.Columns.Add("AFEID", typeof(int));
tbldata.Rows.Add(15, 17570, 37450, 48.22, 66.78, 96.44, DateTime.Parse("2022 - 07 - 20"), 1093);
tbldata.Rows.Add(15, 17570, 37450, 35.46, 49.11, 70.92, DateTime.Parse("2022 - 07 - 21"), 1093);
tbldata.Rows.Add(15, 17570, 37450, 54.60, 75.62, 109.20, DateTime.Parse("2022 - 07 - 19"), 1093);
tbldata.Rows.Add(15, 17570, 37450, 53.90, 74.64, 107.80, DateTime.Parse("2022 - 07 - 20"), 1093);
tbldata.Rows.Add(15, 17571, 37450, 25.53, 35.36, 51.06, DateTime.Parse("2022 - 07 - 20"), 1093);
tbldata.Rows.Add(15, 17571, 37625, 70.92, 98.21, 141.84, DateTime.Parse("2022 - 07 - 20"), 1093);
tbldata.Rows.Add(15, 17571, 37450, 87.93, 121.78, 175.86, DateTime.Parse("2022 - 07 - 20"), 1093);
tbldata.Rows.Add(15, 17571, 37450, 51.06, 70.71, 102.12, DateTime.Parse("2022 - 07 - 19"), 1093);
tbldata.Rows.Add(15, 17570, 37680, 60.99, 84.46, 121.98, DateTime.Parse("2022 - 07 - 20"), 1093);
tbldata.Rows.Add(15, 17570, 37680, 53.90, 74.64, 107.80, DateTime.Parse("2022 - 07 - 19"), 1093);
tbldata.Rows.Add(15, 17570, 37478, 53.90, 74.64, 107.80, DateTime.Parse("2022 - 07 - 19"), 1093);
var groupTable = tbldata.AsEnumerable()
.OrderByDescending(x => x.Field<DateTime>("CostDate"))
.GroupBy(x => new
{
lid = x.Field<int>("LocationId"),
afeid = x.Field<int>("AFEID"),
cid = x.Field<int>("ContractorId"),
rid = x.Field<int>("ResourceId"),
});
DataTable output = tbldata.Clone();
output.Columns.Add("MaxST", typeof(decimal));
output.Columns.Add("MaxOT", typeof(decimal));
output.Columns.Add("MaxDT", typeof(decimal));
output.Columns.Add("AvgST", typeof(decimal));
output.Columns.Add("AvgOT", typeof(decimal));
output.Columns.Add("AvgDT", typeof(decimal));
output.Columns.Add("MinST", typeof(decimal));
output.Columns.Add("MinOT", typeof(decimal));
output.Columns.Add("MinDT", typeof(decimal));
output.Columns.Add("rownum");
output.Columns["rownum"].SetOrdinal(0);
output.Columns.Remove("ST");
output.Columns.Remove("OT");
output.Columns.Remove("DT");
foreach (var row in groupTable)
{
var dates = row.GroupBy(x => x.Field<DateTime>("CostDate")).ToArray();
for(int i = 0; i < dates.Count(); i )
{
DataRow newRow = output.Rows.Add(new object[]
{
i 1,
row.Key.lid,
row.Key.cid,
row.Key.rid,
dates[i].Key,
row.Key.afeid,
dates[i].Max(x => x.Field<decimal>("ST")),
dates[i].Max(x => x.Field<decimal>("OT")),
dates[i].Max(x => x.Field<decimal>("DT")),
dates[i].Average(x => x.Field<decimal>("ST")),
dates[i].Average(x => x.Field<decimal>("OT")),
dates[i].Average(x => x.Field<decimal>("DT")),
dates[i].Min(x => x.Field<decimal>("ST")),
dates[i].Min(x => x.Field<decimal>("OT")),
dates[i].Min(x => x.Field<decimal>("DT"))
});
}
}
}
}
}