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 });
}
}
}
}
Related
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.
I want to create a chart having multiple series from same gridview column.
My GridView data (from Stored Procedure):
Name | Month | Volume
A | 2019-01 | 1400
B | 2019-01 | 1100
B | 2019-02 | 400
C | 2019-01 | 6500
B | 2019-03 | 6500
A | 2019-02 | 500
C | 2019-02 | 35
And I would like to add a Chart with Volume/Name on the axes and seperate series for each month like one serie for 2019-01, another for 2019-02.
For the example above I'll have three series.
I'm very new to ASP.NET and I'll be glad for any clue how I can achive that. Thanks in advance for your help!
You first have to group the data by months. I did that in code below by creating a pivot table and grouping the data by Names.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication106
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("Name", typeof(string));
dt.Columns.Add("Month", typeof(DateTime));
dt.Columns.Add("Volume", typeof(int));
dt.Rows.Add(new object[] {"A", DateTime.ParseExact("2019-01", "yyyy-MM", System.Globalization.CultureInfo.InvariantCulture), 1400});
dt.Rows.Add(new object[] { "B", DateTime.ParseExact("2019-01", "yyyy-MM", System.Globalization.CultureInfo.InvariantCulture), 1100 });
dt.Rows.Add(new object[] { "B", DateTime.ParseExact("2019-02", "yyyy-MM", System.Globalization.CultureInfo.InvariantCulture), 400 });
dt.Rows.Add(new object[] { "C", DateTime.ParseExact("2019-01", "yyyy-MM", System.Globalization.CultureInfo.InvariantCulture), 6500 });
dt.Rows.Add(new object[] { "B", DateTime.ParseExact("2019-03", "yyyy-MM", System.Globalization.CultureInfo.InvariantCulture), 6500 });
dt.Rows.Add(new object[] { "A", DateTime.ParseExact("2019-02", "yyyy-MM", System.Globalization.CultureInfo.InvariantCulture), 500 });
dt.Rows.Add(new object[] { "C", DateTime.ParseExact("2019-02", "yyyy-MM", System.Globalization.CultureInfo.InvariantCulture), 35 });
DateTime[] dates = dt.AsEnumerable().Select(x => x.Field<DateTime>("Month")).Distinct().OrderBy(x => x).ToArray();
DataTable pivot = new DataTable();
pivot.Columns.Add("Name", typeof(string));
foreach (DateTime date in dates)
{
pivot.Columns.Add(date.ToString("yyyy-MM"), typeof(int));
}
var groups = dt.AsEnumerable().GroupBy(x => x.Field<string>("Name")).ToList();
foreach(var group in groups)
{
DataRow newRow = pivot.Rows.Add();
newRow["Name"] = group.Key;
foreach(DataRow row in group)
{
newRow[row.Field<DateTime>("Month").ToString("yyyy-MM")] = row.Field<int>("Volume");
}
}
}
}
}
Gender Age Category
--------------------------------
Male | 10 | 2
Female | 15 | 1
Trans | 13 | 3
Female | 10 | 1
Male | 20 | 2
i have a datatable with above values. Male CategoryId is 2. in above table there are total 2 Males rows. based on Category, merged two rows and divide into a seperate datatable.
My required output is :-
Datatable 1
Gender Age Category
--------------------------------
Male | 10 | 2
Male | 20 | 2
DataTable 2
Gender Age Category
--------------------------------
Female | 15 | 1
Female | 10 | 1
DataTable 3
Gender Age Category
--------------------------------
Trans | 13 | 3
var view = sourceDataTable.DefaultView;
view.RowFilter = "Category = 2";
var maleDataTable = view.ToTable();
view.RowFilter = "Category = 1";
var femaleDataTable = view.ToTable();
view.RowFilter = "Category = 3";
var transDataTable = view.ToTable();
Here you go:
List<DataTable> result = DTHead.AsEnumerable()
.GroupBy(row => row.Field<DataType>("Category"))
.Select(g => g.CopyToDataTable())
.ToList();
For more details please check this post:Split Tables
See following :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication48
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("Gender", typeof(string));
dt.Columns.Add("Age", typeof(int));
dt.Columns.Add("Category", typeof(int));
dt.Rows.Add(new object[] {"Male", 10, 2});
dt.Rows.Add(new object[] {"Female", 15, 1});
dt.Rows.Add(new object[] {"Trans", 13, 3});
dt.Rows.Add(new object[] {"Female", 10, 1});
dt.Rows.Add(new object[] {"Male", 20, 2});
DataTable dt1 = dt.AsEnumerable().Where(x => x.Field<string>("Gender") == "Male").CopyToDataTable();
DataTable dt2 = dt.AsEnumerable().Where(x => x.Field<string>("Gender") == "Feale").CopyToDataTable();
DataTable dt3 = dt.AsEnumerable().Where(x => x.Field<string>("Gender") == "Trans").CopyToDataTable();
}
}
}
Here is a more generic solution that get every type in a column :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication48
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("Gender", typeof(string));
dt.Columns.Add("Age", typeof(int));
dt.Columns.Add("Category", typeof(int));
dt.Rows.Add(new object[] { "Male", 10, 2 });
dt.Rows.Add(new object[] { "Female", 15, 1 });
dt.Rows.Add(new object[] { "Trans", 13, 3 });
dt.Rows.Add(new object[] { "Female", 10, 1 });
dt.Rows.Add(new object[] { "Male", 20, 2 });
//updated code
string[] rowNames = dt.AsEnumerable().Select(x => x.Field<string>("Gender")).Distinct().ToArray();
DataSet ds = new DataSet();
foreach (string gender in rowNames)
{
DataTable newDt = dt.AsEnumerable().Where(x => x.Field<string>("Gender") == gender).CopyToDataTable();
newDt.TableName = gender;
ds.Tables.Add(newDt);
}
}
}
}
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");
}
}
}
}
}
I am pulling some historical data from Firebird database as below:
Product_ID Date Price
1 2001-01-01 10
1 2001-02-01 10
1 2001-03-01 15
1 2001-04-01 10
1 2001-05-01 20
1 2001-06-01 20
What I am trying to do is to extract the first for occurrence every price change.
Example of expected data set:
Product_ID Date Price
1 2001-01-01 10
1 2001-03-01 15
1 2001-04-01 10
1 2001-05-01 20
I know that on MSSQL I could leverage LAG for that. Is it possible to do that with Firebird?
You can try this, but be aware I didn't tested it:
CREATE PROCEDURE SP_Test
RETURNS (
Product_ID INTEGER,
Date DATE,
Price INTEGER
)
AS
DECLARE VARIABLE Last_Product_ID INTEGER;
DECLARE VARIABLE Last_Date DATE;
DECLARE VARIABLE Last_Price INTEGER;
BEGIN
FOR SELECT Product_ID, Date, Price
FROM xxxx
ORDER BY Product_ID, Date
INTO Product_ID, Date, Price
DO BEGIN
IF ((:Last_Product_ID IS NULL) OR
(:Last_Date IS NULL) OR
(:Last_Price IS NULL) OR
(:Product_ID <> :Last_Product_ID) OR
(:Price <> :Last_Price)) THEN
SUSPEND;
Last_Product_ID = :Product_ID;
Last_Date = :Date;
Last_Price = :Price;
END;
END;
in MoreLinq there is a Lag extension method but it is supported only in Linq to Objects...
What you can do, if you are looking for a C# linq answer for that you can:
Basically order your data the correct way and then add a row index for while price (and product_id) is still the same. Then group by it and select the min date.
int groupingIndex = 0;
int previousPrice = 0;
var response = data
.OrderBy(item => item.Product_ID)
.ThenBy(item => item.Date)
.Select(item =>
{
if (item.Price != previousPrice)
{
previousPrice = item.Price;
groupingIndex++;
}
return new { Index = groupingIndex, Item = item };
})
.GroupBy(item => new { item.Index, item.Item.Product_ID, item.Item.Price } )
.Select(group => new Record
{
Product_ID = group.Key.Product_ID,
Price = group.Key.Price,
Date = group.Min(item => item.Item.Date)
}).ToList();
And if you don't mind doing the operation in the C# and not the DB (and using a beta version of the MoreLinq) then:
int index = 0;
var result2 = data
.OrderBy(item => item.Product_ID)
.ThenBy(item => item.Date)
.Lag(1, (current, previous) => new { Index = (current.Price == previous?.Price ? index : ++index), Item = current })
.GroupBy(item => new { item.Index, item.Item.Product_ID, item.Item.Price })
.Select(group => new Record { Product_ID = group.Key.Product_ID, Price = group.Key.Price, Date = group.Min(item => item.Item.Date) })
.ToList();
This is a little complicated but it works
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication6
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("Product_ID", typeof(int));
dt.Columns.Add("Date", typeof(DateTime));
dt.Columns.Add("Price", typeof(int));
dt.Rows.Add(new object[] {1, DateTime.Parse("2001-01-01"), 10});
dt.Rows.Add(new object[] {1, DateTime.Parse("2001-02-01"), 10});
dt.Rows.Add(new object[] {1, DateTime.Parse("2001-03-01"), 15});
dt.Rows.Add(new object[] {1, DateTime.Parse("2001-04-01"), 10});
dt.Rows.Add(new object[] {1, DateTime.Parse("2001-05-01"), 20});
dt.Rows.Add(new object[] {1, DateTime.Parse("2001-06-01"), 20});
dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-01-01"), 10 });
dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-02-01"), 10 });
dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-03-01"), 15 });
dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-04-01"), 10 });
dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-05-01"), 20 });
dt.Rows.Add(new object[] { 2, DateTime.Parse("2001-06-01"), 20 });
dt = dt.AsEnumerable().OrderBy(x => x.Field<DateTime>("Date")).CopyToDataTable();
List<DataRow> results = dt.AsEnumerable()
.GroupBy(g => g.Field<int>("Product_ID"))
.Select(g1 => g1.Select((x, i) => new { row = x, dup = (i == 0) || ((i > 0) && (g1.Skip(i - 1).FirstOrDefault().Field<int>("Price") != g1.Skip(i).FirstOrDefault().Field<int>("Price"))) ? false : true })
.Where(y => y.dup == false).Select(z => z.row)).SelectMany(m => m).ToList();
}
}
}