Group by two columns in DataTable Column sum - c#

In the following code for finding sum of Rate column in the DataTable dt:
dt.Compute("Sum(Convert(Rate, 'System.Int32'))");
In this is it possible to assign group by clause like SQL Inn order to get Sum based on a value in another column of the same dt Fo eg:
----------------------------
Rate | Group |type
----------------------------
100 | A | 1
120 | B | 2
70 | A | 1
50 | A | 2
----------------------------
I just wanna to get.
Sum A=170(type-1) SUMA=50(type-2) an Sum B=120(type-2)
Ref: I got ans in my previous question in single column case
Group by in DataTable Column sum.

You can group by an anonymous type:
var result = from r in dt.AsEnumerable()
group r by new { Group = r["Group"], Type = r["Type"] } into g
select new { Group = g.Key.Group,
Type = g.Key.Type,
Sum = g.Sum(x => Convert.ToInt32(x["Rate"])) };
foreach (var r in result)
{
Console.WriteLine("Group {0}, Type {1}, Sum {2}", r.Group, r.Type, r.Sum);
}

Related

How do I merge two count queries from one table?

The first query :
Id | UserId | projectId |date | Status
1 | 1 | 1 | 2020 | PENDDING
2 | 1 | 2 | 2020 | DONE
3 | 2 | 1 | 2020 | PENDDING
And what I tried two queries :
the first is about to get all userwork with X project for example id = 1
var FirstQery = context.table1.where (C => C.ProjectId == 1).count();
The second query is to fetch the number of user with project x have "done"status
var SecondQery = context.table1.where (C => C.ProjectId == 1 && C.Status == "DONE").count();
I want return object have only two values : countNumberUserWithXProject
and countNumberUserByXProjectHaveXStatus
It is known approach with fake grouping.
var query =
from t in context.table1
where t.ProjectId == 1
group t by 1 into g
select new
{
Count = g.Count(),
DoneCount = g.Sum(x => x.Status == "DONE" ? 1 : 0)
}
var result = query.FirstOrDefault();
How about returning an anonymous object (or if you like it more a typized object like a int[]).
return new {count1 = FirstQery , count2 = SecondQery };
(Return, or assign like var result = new {count1....} etc
You can also replace the FirstQuery and SecondQuery directly with the Linq query.

Merge two datatables in C#?

I have two datatables ..
DataTable dtTemp= new DataTable();
dtTemp.Columns.AddRange(new[]
{
new DataColumn("segment_id", typeof(int)),
new DataColumn("seg_description")
});
DataTable dtTemp2 = new DataTable();
dtTemp2.Columns.Add("set_id",typeof(int));
Now lets have some rows into first table..
segment_id|seg_description
------ |---------------
1 | desc..
2 | desc2..
3 | desc3..
Now lets have some data into second table..
set_id
--------
1
--------
2
Now, I want marge this two tables to get below output
set_id | segment_id |seg_description
--------| ---------- | --------------
1 | 1 | desc..
1 | 2 | desc2..
1 | 3 | desc3..
2 | 1 | desc..
2 | 2 | desc2..
2 | 3 | desc3..
How can I do this?using Merge() can I achieve this?
So you want to "cross-join" the tables by building a cartesian product of all rows? Of course there is no builtin way, you can use this method:
public static DataTable CrossJoinTables(DataTable t1, DataTable t2)
{
if (t1 == null || t2 == null)
throw new ArgumentNullException("t1 or t2", "Both tables must not be null");
DataTable t3 = t1.Clone(); // first add columns from table1
foreach (DataColumn col in t2.Columns)
{
string newColumnName = col.ColumnName;
int colNum = 1;
while (t3.Columns.Contains(newColumnName))
{
newColumnName = string.Format("{0}_{1}", col.ColumnName, ++colNum);
}
t3.Columns.Add(newColumnName, col.DataType);
}
IEnumerable<object[]> crossJoin =
from r1 in t1.AsEnumerable()
from r2 in t2.AsEnumerable()
select r1.ItemArray.Concat(r2.ItemArray).ToArray();
foreach(object[] allFields in crossJoin)
{
t3.Rows.Add(allFields);
}
return t3;
}
Usage:
DataTable tblresult = CrossJoinTables(dtTemp2, dtTemp); // swapped order because you want columns from dtTemp2 first
To do this you need to use the CROSS JOIN operation.
Select * Table1 CROSS JOIN Table2
It literally gives you the product of the two tables : Every row in A joined to every row in B. if A has 100 rows and B has 100 rows, the Cross Join has 10,000 rows.
How about this:
var dt1 = dtTemp1.AsEnumerable();
var dt2 = dtTemp2.AsEnumerable();
var q = from x in dt1
from y in dt2
select new { set_id = (int)y["set_id"], segment_id = (int)x["segment_id"], seg_description = (string)x["seg_description"] };

How to compare 2 columns in different datatables

table1
id | fileName | fileDateTime
1 | somefile | somedatetime
2 | somefile2 | somedatetime2
table2
id | fileName | fileDateTime
| somefile1 | somedatetime1
| somefile2 | somedatetime2
output table3
id | fileName | fileDatetime
| somefile1 | somedatetime1
I want to compare the 2 tables (column 2 & 3 only) and only have what is not in both tables, there is no ID field in the 2nd table. Then I plan on parsing the data in the file and add file info to database to record file has been parsed. I am having trouble comparing the 2 fields. This does not seem to work.
for (int i = 0; i < finalTable.Rows.Count; i++)
{
for (int r = 0; r < filesTable.Rows.Count; i++)
{
if (finalTable.Rows[i][2] == filesTable.Rows[r][2])
{
finalTable.Rows.Remove(finalTable.Rows[i]);
}
}
}
Assuming the value is a string, you could just do
for (int i = 0; i < finalTable.Rows.Count; i++)
{
for (int r = 0; r < filesTable.Rows.Count; i++)
{
if (finalTable.Rows[r].Field<string>(2) == filesTable.Rows[r].Field<string>(2))
{
finalTable.Rows.Remove(finalTable.Rows[i]);
}
}
}
If it's another type, just change the <string> for the real type !
You can use Linq to achieve it as shown below.
Assuming your fields are string. For other datatype you can cast them accordingly:
using System.Data.Linq;
......
......
// Merge both tables data and group them by comparing columns values
dt1.Merge(dt2);
var g = dt1.AsEnumerable()
.GroupBy(x => x[0]?.ToString() + x[1]?.ToString()) // You can build Unique key here, I used string concatination
.ToDictionary(x => x.Key, y => y.ToList());
var unique = dt1.Clone();
foreach (var e in g)
{
if (e.Value.Count() == 1) // In either table - Intersaction
{
e.Value.CopyToDataTable(unique, LoadOption.OverwriteChanges);
}
if (e.Value.Count() == 2)
{
// In both tables -Union
}
}

Linq Left join, where, group by, count()

I need help with doing a Left join in a linq statement. My T-sql query works as expected but I can't seem to get the wanted results from the Linq. I also realize that there are ton of questions like mine, but I can't seem to apply any of the solutions to my case.
Products table
+---+------------+-----------+
| |transportID | Type(int)|
+---+------------+-----------+
| 1 | 5 | 1 |
| 2 | 5 | 3 |
| 3 | 6 | 3 |
+---+------------+-----------+
Stores
+---+------------+-------------+
| |Name |Type1(string)|
+---+------------+-------------+
| 1 | Ho | 1 |
| 2 | He | 2 |
| 3 | Be | 3 |
| 4 | Ke | 4 |
| 5 | Fe | 5 |
+---+------------+-------------+
My wanted result is
+---+------------+-------------+
| |Type |Count |
+---+------------+-------------+
| 1 | 1 | 1 |
| 2 | 2 | 0 |
| 3 | 3 | 1 |
| 4 | 4 | 0 |
| 5 | 5 | 0 |
+---+------------+-------------+
My tsql that works as intended
SELECT
Type1,
Count(Pro.transportId) as Count
FROM dbo.stores as sto
left Join dbo.products as pro on (sto.Type1 = pro.Type AND pro.transportId=5)
Where Type1 is not null
group by Type1
ORDER BY Type1 * 1 ASC
My Linq attempt returns this.
+---+------------+-------------+
| |Type |Count |
+---+------------+-------------+
| 1 | 1 | 1 |
| 3 | 3 | 1 |
+---+------------+-------------+
Linq Statement.
var res = (from sto in _context.Stores
join pro in _context.Products on sto.Type1 equals System.Data.Objects.SqlClient.SqlFunctions.StringConvert((double)pro.Type).Trim()
where pro.transportId == transportId
group pro by pro.Type1 into pt1
select new TypeTransportation()
{
Type = pt1.Key, // Needs to be int
Count = pt1.Count()
}).ToList();
I've tried doing some defaultifempty but can't seem to make it work.
Here is MSDN link "How to: Perform Left Outer Joins" with LINQ: https://msdn.microsoft.com/en-gb/library/bb397895.aspx
You code should be like this:
var res = (from sto in _context.Stores
join pro in _context.Products on sto.Type1 equals System.Data.Objects.SqlClient.SqlFunctions.StringConvert((double)pro.Type).Trim() into grpJoin
from product in grpJoin.DefaultIfEmpty()
where product.transportId == transportId
group product by product.Type1 into pt1
select new TypeTransportation()
{
Type = pt1.Key, // Needs to be int
Count = pt1.Count()
}).ToList();
Wow .. lastly i did it ..
var transportId = 5;
var res = from s in _context.Stores
let Type = _context.Stores.Take(1).Select(x => s.Type1).Cast<int>().FirstOrDefault()
group Type by Type into pt1
select new TypeTransportation
{
Type = pt1.Key, // Needs to be int
Count = _context.Products.Where(i => i.transportId == transportId && i.Type == pt1.Key).Count()
};
foreach (var item in res)
{
Console.WriteLine(item.Type + " " + item.Count);
}
Console.ReadKey();
I can't do it in query syntax, but using extension method syntax it will be
var products = new[]
{
new {transportId = 5, Type = 1},
new {transportId = 5, Type = 3},
new {transportId = 6, Type = 3},
new {transportId = 5, Type = 3},
new {transportId = 5, Type = 5},
};
var stores = new[]
{
new {Name = "Ho", Type1 = "1"},
new {Name = "He", Type1 = "2"},
new {Name = "Be", Type1 = "3"},
new {Name = "Ke", Type1 = "4"},
new {Name = "Fe", Type1 = "5"},
};
var transportId = 5;
var res = stores
.GroupJoin(
inner: products
.Where(product =>
product.transportId == transportId),
innerKeySelector: product => product.Type,
outerKeySelector: store => Int32.Parse(store.Type1),
resultSelector: (store, storeProducts) =>
new
{
StoreType = store.Type1,
StoreName = store.Name,
ProductsCount = storeProducts.Count()
})
.ToList();
foreach (var item in res)
{
Console.WriteLine(item);
}
Just replace Int32.Parse with appropriate sql function call for actual DbContext query code.
With query syntax this is probably the best I can propose:
var res =
from store in stores
join product in
(from prod in products where prod.transportId == transportId select prod)
on store.Type1 equals product.Type.ToString() into storeProducts
select new
{
StoreType = store.Type1,
StoreName = store.Name,
ProductsCount = storeProducts.Count()
};
Basically you need to follow the left join pattern described in join clause (C# Reference). The only tricky part is the pro.transportId=5 condition in
left Join dbo.products as pro on (sto.Type1 = pro.Type AND pro.transportId=5)
The important thing is to not include it as where clause after the join.
One possible way to handle it is like this:
var res = (from sto in _context.Stores
join pro in _context.Products
on new { sto.Type1, transportId } equals
new { Type1 = pro.Type.ToString(), pro.transportId }
into storeProducts
from pro in storeProducts.DefaultIfEmpty()
group sto by sto.Type1 into pt
select new
{
Type = pt.Key, // the string value, there is no way to convert it to int inside the SQL
Count = pt.Count()
}).AsEnumerable() // switch to LINQ to Objects context
.Select(pt => new TypeTransportation()
{
Type = Convert.ToInt32(pt.Type), // do the conversion here
Count = pt.Count()
}).ToList();
or just apply it as where clause before the join:
var res = (from sto in _context.Stores
join pro in _context.Products.Where(p => p.transportId == transportId)
on sto.Type1 equals pro.Type.ToString()
into storeProducts
// the rest ... (same as in the first query)
Another detail to mention is that in order to make LEFT JOIN effectively apply, you need to group by the left table (Stores in your case) field (like in the original SQL query), thus ending up with a string key. If you wish to get the int key, there is no way to do it inside the db query, so you need to use a temporary projection, context switch and the final projection as shown above.
UPDATE: The last thing that I didn't realize initially is that the original SQL Count(Pro.transportId) is excluding NULLs from the right side of the join. So the final correct equivalent LINQ query is:
var res = (from sto in _context.Stores
join pro in _context.MyProducts
on new { sto.Type1, transportId } equals
new { Type1 = pro.Type.ToString(), pro.transportId }
into storeProducts
from pro in storeProducts.DefaultIfEmpty()
group new { sto, pro } by sto.Type1 into pt
select new
{
Type = pt.Key,
Count = pt.Sum(e => e.pro != null ? 1 : 0)
})
.AsEnumerable()
.Select(pt => new TypeTransportation()
{
Type = Convert.ToInt32(pt.Type),
Count = pt.Count
}).ToList();

LINQ sum of one column and insert to another table C#

i have two tables suck as the one below i wanna know how to sum "calorie" column based on name from table 1 and then insert the value to table 2
table1(PK->ID(int),Name(nvarchar),amount(int),calorie(int))
table2(pk->ID(int),name(nvarchar),totalcalorie(int))
+-------+--------+----------+--------------+
| int | name | amount | calorie |
+-------+--------+----------+--------------+
| 1 | a | 10 | 20 |
| 2 | b | 5 | 20 |
| 2 | b | 10 | 10 |
| 1 | a | 10 | 10 |
| 2 | b | 15 | 35 |
| 3 | c | 20 | 15 |
+-------+--------+----------+--------------+
something like this is my first table now imagine same kinda table for table2
only this time something like :
1-------a--------30
2-------b--------65
3-------c--------15
is this possible at all? what i wrote till now and doesn't work is this :
DataClasses1DataContext db = new DataClasses1DataContext();
var q = from row in db.table1
group row by new { row.name }
into grp
select new
{
grp.Key.name,
sum = grp.Sum(row => row.calorie)
};
db.SubmitChanges();
Now you are just selecting data. You need the block for insertion like the following:
var q = (from row in db.table1
group row by new { row.id, row.name } into grp
select new
{
grp.Key.id,
grp.Key.name,
sum = grp.Sum(s => s.calorie)
}).ToList();
foreach(var item in q)
{
var e = new db.table2Entity
{
id = item.id,
name = item.name,
totalcalorie = item.sum
};
db.Table2.AddObject(e);
}
db.SaveChanges();
I think that you do not see the result on you databse after db.SubmitChanges() wright ? Linq query should work fine but Submit doesn't see the change becouse there is no change in table2. You only select data and group it from table1. Please debug and see what is in q variable.

Categories