Sqlite Get sum Columns based on another column - c#

I have a SQLite table data like this given below.
ID Type Amount
-----------------------
1 A 10
1 B 5
2 A 4
2 B 7
3 A 2
3 B 8
What i wanted to present in a datagridview is something like this
ID A B
-------------
1 10 5
2 4 7
3 2 8
This is in SQLite database. I need to show the data in datagridview. Currently i am taking the first dataset and using code to loop through to get the desired result but the table has a lot of results and therefore the process is extremely slow.
Can you guys please tell me how can i get the second result directly. I have search but i could not find any appropriate sql query to get this result

You can do that with a conditional aggregation
select ID,
sum(case when Type = 'A' then Amount) as A
sum(case when Type = 'B' then Amount) as B
from yourTable
group by ID
Another option is to join the table with itself
select ID, t1.Amount as A, t2.Amount as B
from yourTable t1
join yourTable t2
on t1.ID = t2.ID
where t1.Type = 'A' and
t2.Type = 'B'
The first option requires you to have only one row per ID / Type, but if that's the case performs better. The second one is safer, but joining the table with itself will decrease its performances

This should do:
SELECT ID,
SUM(CASE WHEN Type = 'A' THEN Amount ELSE 0 END) as A,
SUM(CASE WHEN Type = 'B' THEN Amount ELSE 0 END) as B
FROM TABLE
GROUP BY ID

You want a pivot table :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication49
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("ID", typeof(int));
dt.Columns.Add("Type", typeof(string));
dt.Columns.Add("Amount", typeof(int));
dt.Rows.Add(new object[] {1, "A", 10});
dt.Rows.Add(new object[] {1, "B", 5});
dt.Rows.Add(new object[] {2, "A", 14});
dt.Rows.Add(new object[] {2, "B", 7});
dt.Rows.Add(new object[] {3, "A", 2});
dt.Rows.Add(new object[] {3, "B", 8});
string[] uniqueTypes = dt.AsEnumerable().Select(x => x.Field<string>("Type")).Distinct().ToArray();
DataTable pivot = new DataTable();
pivot.Columns.Add("ID", typeof(int));
foreach (string _type in uniqueTypes)
{
pivot.Columns.Add(_type, typeof(int));
}
var groups = dt.AsEnumerable().GroupBy(x => x.Field<int>("ID")).ToList();
foreach (var group in groups)
{
DataRow newRow = pivot.Rows.Add();
newRow["ID"] = group.Key;
foreach (DataRow row in group)
{
newRow[row.Field<string>("Type")] = row.Field<int>("Amount");
}
}
}
}
}

Related

Group by column values with spaces Using linq

I have got this situation with a datatable like this
C1 C2 C3
A AA 4
BB 6
B CC 3
DD 3
EE 4
C FF 5
GG 5
and my output should be like this
C1 C2 C3
A AA,BB 10
B CC,DD,EE 10
C FF,GG 10
How can i group by the column with the space till the next value comes up
What i did was i took all the row itemarray and then using some string manipulation and regex got the row value as for the first two values like this and assigned to a variable in a query using Let
A,AA,BB,10|B,CC,DD,EE,10 but then i cannot add it using the
**DT.clone.rows.Add(x.split("|"c))* method as there its not incrementing and adding the whole joined string
Any other input where i can manipulate and add it (P.S i know linq is querying language)
Thank you for your time
You can use .GroupBy to get result needed
Here is your class:
public class Data
{
public string C1 { get; set; }
public string C2 { get; set; }
public int C3 { get; set; }
}
Imagine that you have list of Data objects, so your GroupBy expression will be following:
var result = list.GroupBy(g => g.C1, (a, b) => new {C1 = a, C2 = b.ToList()})
.Select(g => new
{
g.C1,
C2 = string.Join(",", g.C2.Select(m => m.C2)),
C3 = g.C2.Sum(m => m.C3)
})
.ToList();
A simple .GroupBy can give you expected result, Edited to handle Null or WhiteSpace Columns
var res = ListModel.Where(e => !string.IsNullOrWhiteSpace(e.C1)
&& !string.IsNullOrWhiteSpace(e.C2))
.GroupBy(e => e.C1).Select(e => new
{
e.Key,
c2 = string.Join(",", e.Select(x => x.C2).ToList()),
c3 = e.Sum(x => x.C3)
}).ToList();
Hello All first of all Thank you for your time and effort i Did this use case using this code
This gave me all row item array in string and than in the end with a little Split method i was able to add it to my datatable
String.Join("|",(System.Text.RegularExpressions.Regex.Replace(String.Join("|",(From roww In DT.AsEnumerable() Select String.Join(",",roww.ItemArray) ).ToList),"\|,",",")).Split("|"c).
Select(Function(q)CStr(q)+","+CStr(String.join("|",System.Text.RegularExpressions.Regex.Matches(CStr(q),"\d+").Cast(Of match)).Split("|"c).Sum(Function(r) CInt(r) ))).tolist),",\d+,",",")```
Try following code which is tested
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable dt1 = new DataTable();
dt1.Columns.Add("C1", typeof(string));
dt1.Columns.Add("C2", typeof(string));
dt1.Columns.Add("C3", typeof(int));
dt1.Rows.Add(new object[] { "A", "AA", 4});
dt1.Rows.Add(new object[] { null, "BB", 6});
dt1.Rows.Add(new object[] { "B", "CC", 3});
dt1.Rows.Add(new object[] { null, "DD", 3});
dt1.Rows.Add(new object[] { null, "EE", 4});
dt1.Rows.Add(new object[] { "C", "FF", 5});
dt1.Rows.Add(new object[] { null, "GG", 5});
//replace nulls in column 1 with actual values
string previous = "";
foreach(DataRow row in dt1.AsEnumerable())
{
if (row.Field<string>("C1") == null)
{
row["C1"] = previous;
}
else
{
previous = row.Field<string>("C1");
}
}
DataTable dt2 = dt1.Clone();
var groups = dt1.AsEnumerable().GroupBy(x => x.Field<string>("C1")).ToList();
foreach (var group in groups)
{
dt2.Rows.Add(new object[] {
group.Key,
string.Join(",", group.Select(x => x.Field<string>("C2"))),
group.Select(x => x.Field<int>("C3")).Sum()
});
}
}
}
}
Yet another way using Skip, TakeWhile, and GroupBy extensions:
DataTable dt1 = new DataTable();
dt1.Columns.Add("C1", typeof(string));
dt1.Columns.Add("C2", typeof(string));
dt1.Columns.Add("C3", typeof(int));
//The output table.
DataTable dt2 = dt1.Clone();
dt1.Rows.Add(new object[] { "A", "AA", 3 });
dt1.Rows.Add(new object[] { null, "BB", 6 });
dt1.Rows.Add(new object[] { "B", "CC", 3 });
dt1.Rows.Add(new object[] { null, "DD", 3 });
dt1.Rows.Add(new object[] { null, "EE", 4 });
dt1.Rows.Add(new object[] { "C", "FF", 5 });
dt1.Rows.Add(new object[] { null, "GG", 6 });
var rows = dt1.Rows.Cast<DataRow>().AsEnumerable();
foreach (var row in rows.Where(r => r.Field<string>("C1") != null))
{
var indx = dt1.Rows.IndexOf(row) + 1;
var q = rows
.Skip(indx)
.TakeWhile(t => t.Field<string>("C1") == null)
.GroupBy(g => g.Field<string>("C1"))
.Select(g => new
{
C1 = row.Field<string>("C1"),
C2 = $"{row.Field<string>("C2")}, {string.Join(", ", g.Select(s => s.Field<string>("C2")))}",
C3 = row.Field<int>("C3") + g.Sum(s => s.Field<int>("C3")),
}).FirstOrDefault();
if (q != null)
dt2.Rows.Add(q.C1, q.C2, q.C3);
}
dataGridView1.DataSource = null;
dataGridView1.DataSource = dt2;
The idea behind this snippet is to:
Get the complete rows and iterate through them.
For each complete row, we get it's index from the original DataTable and add 1 to make a starting search point for the incomplete rows. The Skip extension is the method to achieve that.
The TakeWhile extension function gets the incomplete rows and stops at the next complete row.
The GroupBy extension function groups the incomplete rows to concatenate their C2 values and sum their C3 values, add the results to the values of the complete row and create a temporary anonymous object to hold these values.
Extract the anonymous object and add a new DataRow to the output DataTable.
And finally, bind the output DataTable to a DGV.
Happy 2020 for all.

Linq get max date, return list of records with max date

I would like to retrieve a list of records the given Year, class for the latest effective date.
grouping by Class, Year, RangeMin, RangeMax
Id Class...Year...EffectiveDate...Value...RangeMin...RangeMax
1. A.......2019....2019/1/1.........850......1.........100
2. A.......2019....2019/1/15........840......1.........100
3. A.......2019....2019/2/1.........550......101.......200
4. B.......2019....2019/1/5.........540......1.........100
5. B.......2020....2019/1/5.........650......1.........100
6. B.......2020....2019/5/1.........670......101.......200
7. B.......2020....2019/5/2.........680......101.......200
So if I'm querying for all records which are class A and year 2019 to return a list of rows: 2,3
If I'm querying for all records which are class B and year 2020 to return a list of rows: 5,7
var recordsInDb = (from record in context.records where record.Year == year & record.Class == class_ select record).ToList();
So far I have been able to get a list of all the records for given year, class.
I know I could add a order by descending on the effective date. Yet that still returns all the records not just the ones which have the highest effective date.
You can just group by your criteria and then select the latest from each group.
Using query syntax:
var recordsInDb = (from record in context.records
where record.Year == year & record.Class == class_
group record by new { record.Year, record.Class, record.RangeMin, record.RangeMax } into rg
select (
from record in rg
orderby record.EffectiveDate
select record
).Last()
)
.ToList();
I think it is a little easier to follow using Fluent/lambda syntax since the Last requires it anyway:
var ans = context.records.Where(r => r.Year == year && r.Class == class_)
.GroupBy(r => new { r.Year, r.Class, r.RangeMin, r.RangeMax })
.Select(rg => rg.OrderBy(r => r.EffectiveDate).Last())
.ToList();
Try following :
DataTable dt = new DataTable();
dt.Columns.Add("Id", typeof(int));
dt.Columns.Add("Class", typeof(string));
dt.Columns.Add("Year", typeof(int));
dt.Columns.Add("EffectiveDate", typeof(DateTime));
dt.Columns.Add("Value", typeof(int));
dt.Columns.Add("RangeMin", typeof(int));
dt.Columns.Add("RangeMax", typeof(int));
dt.Rows.Add(new object[] {1, "A", 2019, DateTime.Parse("2019/1/1"), 850,1, 100});
dt.Rows.Add(new object[] {2, "A", 2019, DateTime.Parse("2019/1/15"), 840,1, 100});
dt.Rows.Add(new object[] {3, "A", 2019, DateTime.Parse("2019/2/1"), 550,101, 200});
dt.Rows.Add(new object[] {4, "B", 2019, DateTime.Parse("2019/1/5"), 540,1, 100});
dt.Rows.Add(new object[] {5, "B", 2020, DateTime.Parse("2019/1/5"), 650,1, 100});
dt.Rows.Add(new object[] {6, "B", 2020, DateTime.Parse("2019/5/1"), 670,101, 200});
dt.Rows.Add(new object[] {7, "B", 2020, DateTime.Parse("2019/5/2"), 680,101, 200});
DataTable results = dt.AsEnumerable()
.OrderByDescending(x => x.Field<int>("Year"))
.ThenByDescending(x => x.Field<DateTime>("EffectiveDate"))
.GroupBy(x => new { cl = x.Field<string>("Class"), month = new DateTime(x.Field<DateTime>("EffectiveDate").Year, x.Field<DateTime>("EffectiveDate").Month, 1) })
.Select(x => x.FirstOrDefault())
.CopyToDataTable();

SQL display table in vertical

I am using C# and MySQL as (DBMS) I want to rotate my table. this is my query :
SELECT p.gender,Count(CASE WHEN disease like '%Anemia%' THEN 1 END) AS 'Anemia'
,Count(CASE WHEN disease like '%Injuries%' THEN 1 END) AS 'Injuries'
FROM phc_db.record r, phc_db.patient p
where r.malteser_id=p.malteser_id
group by p.gender
The result is :
Gender Anemia Injuries
------------------------
Female 1 0
Male 2 1
------------------------
And I want :
Disease Male Female
----------------------
Anemia 2 1
Injuries 1 0
----------------------
Any Idea? Any Suggestion?
Thanks
You can try pivoting on the disease instead of the gender. In this case, the genders become columns.
SELECT
disease,
SUM(CASE WHEN p.gender = 'Male' THEN 1 END) AS Male,
SUM(CASE WHEN p.gender = 'Female' THEN 1 END) AS Female
FROM phc_db.record r
INNER JOIN phc_db.patient p
ON r.malteser_id = p.malteser_id
GROUP BY disease
WHERE disease IN ('Anemia', 'Injuries')
Note that from your current output it appears that you may not need to use LIKE to match diseases. In my query above, I check for the exact disease names of anemia and injuries. If I assumed wrongly, then you can bring back LIKE.
Swap the aggregation:
SELECT
(CASE WHEN disease LIKE '%Anemia%' THEN 'Anemia'
WHEN disease like '%Injuries%' THEN 'Injury'
END) AS disease,
SUM (p.gender = 'Female') AS females,
SUM (p.gender = 'Male') AS males
FROM phc_db.record r
INNER JOIN phc_db.patient p
ON r.malteser_id = p.malteser_id
WHERE disease REGEXP 'Anemia|Injuries'
GROUP BY disease;
You can do in c# with following query which will get all the diseases
SELECT p.gender, disease
FROM phc_db.record r, phc_db.patient p
where r.malteser_id=p.malteser_id
The use following c#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("Gender", typeof(string));
dt.Columns.Add("Disease", typeof(string));
dt.Rows.Add(new object[] { "Male", "Anemia"});
dt.Rows.Add(new object[] { "Female", "Anemia"});
dt.Rows.Add(new object[] { "Male", "Anemia"});
dt.Rows.Add(new object[] { "Female", "Anemia"});
dt.Rows.Add(new object[] { "Male", "Anemia"});
dt.Rows.Add(new object[] { "Male","Injuries" });
dt.Rows.Add(new object[] { "Female", "Injuries" });
dt.Rows.Add(new object[] { "Male", "Injuries" });
dt.Rows.Add(new object[] { "Female", "Injuries" });
string[] uniqueDieses = dt.AsEnumerable().Select(x => x.Field<string>("Disease")).Distinct().ToArray();
DataTable pivot = new DataTable();
pivot.Columns.Add("Disease", typeof(string));
pivot.Columns.Add("Male", typeof(int));
pivot.Columns.Add("Female", typeof(int));
var groups = dt.AsEnumerable()
.GroupBy(x => x.Field<string>("Disease")).Select(x => new
{
disease = x.Key,
male = x.Where(y => y.Field<string>("Gender") == "Male").Count(),
female = x.Where(y => y.Field<string>("Gender") == "Female").Count()
}).ToList();
foreach (var group in groups)
{
pivot.Rows.Add(new object[] { group.disease, group.male, group.female });
}
}
}
}

How to use group by in rows?

How to group the below data ? as I am looping through the collection and it gives me only 1 row as there is no grouping in place.
I have to group the below records based on Id column and if there are repeating Ids ? I have to populate model with that many rows.
id name trID trName
1 a 5 x
2 b 6 y
2 c 7 z
3 d 8 m
3 e 9 n
4 f 10 0
class DataModel
{
Public int Id{get;set;}
Public string name{get;set;}
Public RepeatedIDs RepeatedIDCollection{get;set;}
}
class RepeatedIDs
{
Public int trId{get;set;}
Public string trname{get;set;}
}
(from DataRow dr in dataTable.Rows
select new IdModel
{
Id = Convert.ToInt32(dr["ID"]),
name = Convert.ToString(dr["name"]),
// need to group the records here and populate below mode with that many rows
RepeatedIDCollection = new List<RepeatedIDs>
{
new RepeatedIDs()
{
trId = Convert.ToInt32(dr["trId"]),
trname = Convert.ToString(dr["trname"]),
}
}
}).ToList();
What you need is:
var query = dataTable.AsEnumerable()
.GroupBy(r => r.Field<int>("ID"))
.Select(grp => new DataModel
{
Id = grp.Key,
name = String.Join(",", grp.Select(t => t.Field<string>("name"))), //Because there could be multiple names
RepeatedIDCollection = grp.Select(t => new RepeatedIDs
{
trId = t.Field<int>("trID"),
trname = t.Field<string>("trName")
}).ToList(),
});
What this query is doing:
Grouping the data based on ID column in DataTable
Later selecting an object of DataModel
The Id in DataModel is the key from group.
There will be multiple names in the grouped data
Later it creates a List<RepeatedIDCollection> by getting the trId and trname from grouped collection.
Make sure you specify the correct types in Field method.
Try following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("id", typeof (int));
dt.Columns.Add("name", typeof (string));
dt.Columns.Add("trID", typeof (int));
dt.Columns.Add("trName", typeof (string));
dt.Rows.Add(new object[] { 1,"a", 5,"x"});
dt.Rows.Add(new object[] { 2,"b", 6,"y"});
dt.Rows.Add(new object[] { 2,"c", 7,"z"});
dt.Rows.Add(new object[] { 3,"d", 8,"m"});
dt.Rows.Add(new object[] { 3,"e", 9,"n"});
dt.Rows.Add(new object[] { 4,"f", 510,"0"});
var groups = dt.AsEnumerable().GroupBy(x => x.Field<int>("id")).ToList();
}
}
}

Fill in the table values in the foreign key

I have two tables Table1 and Table2, each with two columns: Id, Name.
Tables are filled with some data. I want to create a new table with the columns: Id, T1_Id, T2_Id, where T1_Id and T2_Id are foreign keys to Table1 and Table2 respectively.
How to create the table as quickly as possible to fill it with all the values and T1_Id, T2_Id already contained in the tables Table1 and Table2?
For example:
Table 1:
Id Name
1 T1N1
2 T1N2
3 T1N3
Table 2:
Id Name
1 T2N1
2 T2N2
Result table;
Id T1_Id T2_Id
1 1 1
2 2 1
3 3 1
4 1 2
5 2 2
6 3 2
Use CROSS JOIN to get the Cartesian product of Table 1 and Table 3.
Try this
select row_number() over(order by T2_Id,T1_Id) as Id,
T1_Id ,
T2_Id
From [Table 1] A
CROSS JOIN [Table 3] B
You would use cross join:
select row_number() over (order by (select null)) as id,
t1.id as t1_id, t2.id as t2_id
into result
from table1 t1 cross join table2 t2;
This assumes that you don't actually care about the ordering of the id column in the result table. If you do, you can do:
select row_number() over (order by t1.id, t2.id) as id,
t1.id as t1_id, t2.id as t2_id
into result
from table1 t1 cross join table2 t2;
The first version is faster, because the second will actually do a sort.
Try this
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
DataTable dt1 = new DataTable();
dt1.Columns.Add("Id", typeof(int));
dt1.Columns.Add("Name", typeof(string));
dt1.Rows.Add(new object[] {1, "T1N1"});
dt1.Rows.Add(new object[] {2, "T1N2"});
dt1.Rows.Add(new object[] {3, "T1N3"});
DataTable dt3 = new DataTable();
dt3.Columns.Add("Id", typeof(int));
dt3.Columns.Add("Name", typeof(string));
dt3.Rows.Add(new object[] {1, "T2N1"});
dt3.Rows.Add(new object[] {2, "T2N2"});
DataTable results = new DataTable();
results.Columns.Add("Id", typeof(int));
results.Columns.Add("T1_Id", typeof(int));
results.Columns.Add("T2_Id", typeof(int));
int id = 1;
foreach (DataRow row3 in dt3.AsEnumerable())
{
foreach (DataRow row1 in dt1.AsEnumerable())
{
results.Rows.Add(new object[] { id++, row3.Field<int>("Id"), row1.Field<int>("Id") });
}
}
}
}
}

Categories