I have two tables.
One is Master
Id
Date
1
2022-03-12
2
2022-02-14
3
2021-10-15
4
2021-04-09
5
2020-06-24
Another one is Detail
Id
MasterId
Name
Quantity
1
1
item1
25
2
1
item2
30
3
1
item3
20
4
2
item1
25
5
2
item2
20
6
3
item1
35
7
4
item4
25
8
5
item1
25
9
5
item3
29
From above two table I need a query which will give me 3rd table
Year
item1
item2
item3
item4
2020
25
0
29
0
2021
35
0
0
25
2022
50
50
20
0
You want a pivot table
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication18
{
class Program
{
public static void Main(String[] args)
{
DataTable masterTable = new DataTable("Master");
masterTable.Columns.Add("Id", typeof(int));
masterTable.Columns.Add("Date", typeof(DateTime));
masterTable.Rows.Add(new object[] { 1, DateTime.Parse("2022-03-12") });
masterTable.Rows.Add(new object[] { 2, DateTime.Parse("2022-02-14") });
masterTable.Rows.Add(new object[] { 3, DateTime.Parse("2021-10-15") });
masterTable.Rows.Add(new object[] { 4, DateTime.Parse("2021-04-09") });
masterTable.Rows.Add(new object[] { 5, DateTime.Parse("2020-06-24") });
DataTable detailTable = new DataTable("Detail");
detailTable.Columns.Add("Id", typeof(int));
detailTable.Columns.Add("IMasterId", typeof(int));
detailTable.Columns.Add("Name", typeof(string));
detailTable.Columns.Add("Quantity", typeof(int));
detailTable.Rows.Add(new object[] { 1, 1, "item1", 25 });
detailTable.Rows.Add(new object[] { 2, 1, "item2", 30 });
detailTable.Rows.Add(new object[] { 3, 1, "item3", 20 });
detailTable.Rows.Add(new object[] { 4, 2, "item1", 25 });
detailTable.Rows.Add(new object[] { 5, 2, "item2", 20 });
detailTable.Rows.Add(new object[] { 6, 3, "item1", 35 });
detailTable.Rows.Add(new object[] { 7, 4, "item4", 25 });
detailTable.Rows.Add(new object[] { 8, 5, "item1", 25 });
detailTable.Rows.Add(new object[] { 9, 5, "item3", 29 });
string[] items = detailTable.AsEnumerable().Select(x => x.Field<string>("Name")).OrderBy(x => x).Distinct().ToArray();
DataTable pivot = new DataTable("Pivot");
pivot.Columns.Add("Year", typeof(int));
foreach (string item in items)
{
pivot.Columns.Add(item, typeof(int));
}
var joinTable = (from m in masterTable.AsEnumerable().OrderBy(x => x.Field<DateTime>("Date"))
join d in detailTable.AsEnumerable() on m.Field<int>("Id") equals d.Field<int>("Id")
select new {id = m.Field<int>("Id"), year = m.Field<DateTime>("Date").Year, d = d}
).GroupBy(x => x.year).ToList();
foreach (var date in joinTable)
{
DataRow row = pivot.Rows.Add();
row["Year"] = date.Key;
var names = date.GroupBy(x => x.d.Field<string>("Name")).Select(x => new {name = x.Key, quant = x.Sum(y => y.d.Field<int>("Quantity"))});
foreach (var name in names)
{
row[name.name] = name.quant;
}
}
}
}
}
Related
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 the following table with attributes: Id, ParentId, Name etc.
The table contains a nesting level as: Objects contain Elements that contain Components).
Id | Parent | Name
---------------------------------------
1 | NULL | Name (this is object 1)
2 | 1 | Name (this is element 1)
3 | 2 | Name (this is component 1)
4 | 2 | Name (this is component 2)
5 | 2 | Name (this is component 3)
6 | 1 | Name (this is element 2)
7 | 6 | Name (this is component 4)
8 | 1 | Name (this is element 3)
9 | NULL | Name (this is object 2)
10 | 9 | Name (this is element 4)
11 | 10 | Name (this is component 5)
12 | 10 | Name (this is component 6)
I need a single LINQ query that handles this result as in the presented table.
I would not like to use nested for loops to get the objects, then for each object to get its elements, then for each element to get its components - the same is for recursion on the table and I do not want this.
Not appropriate for one linq statement. Use a recursive method :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
namespace ConsoleApplication7
{
class Program
{
static void Main(string[] args)
{
DataTable dt = new DataTable();
dt.Columns.Add("Id", typeof(int));
dt.Columns.Add("Parent", typeof(int));
dt.Columns.Add("Name", typeof(string));
dt.Rows.Add(new object[] { 1, null, "Name (this is object 1)" });
dt.Rows.Add(new object[] { 2, 1, "Name (this is element 1)" });
dt.Rows.Add(new object[] { 3, 2, "Name (this is component 1)" });
dt.Rows.Add(new object[] { 4, 2, "Name (this is component 2)" });
dt.Rows.Add(new object[] { 5, 2, "Name (this is component 3)" });
dt.Rows.Add(new object[] { 6, 1, "Name (this is element 2)" });
dt.Rows.Add(new object[] { 7, 6, "Name (this is component 4)"});
dt.Rows.Add(new object[] { 8, 1, "Name (this is element 3)" });
dt.Rows.Add(new object[] { 9, null, "Name (this is object 2)" });
dt.Rows.Add(new object[] { 10, 9, "Name (this is element 4)" });
dt.Rows.Add(new object[] { 11, 10, "Name (this is component 5)" });
dt.Rows.Add(new object[] { 12, 10, "Name (this is component 6)" });
Node.RecursiveFill(dt, null, Node.root);
}
}
public class Node
{
public static Node root = new Node();
public List<Node> children { get; set; }
public int id { get; set; }
public string name { get; set; }
public static void RecursiveFill(DataTable dt, int? parentId, Node parentNode)
{
foreach (DataRow row in dt.AsEnumerable().Where(x => x.Field<int?>("Parent") == parentId))
{
Node newChild = new Node();
if (parentNode.children == null) parentNode.children = new List<Node>();
parentNode.children.Add(newChild);
newChild.id = row.Field<int>("Id");
newChild.name = row.Field<string>("Name");
RecursiveFill(dt, newChild.id, newChild);
}
}
}
}
I have a model class like this.
Public Class SampleModel
{
public string ItemName{ get; set; }
public string ItemNo{ get; set; }
public int ItemQty{ get; set; }
public string GroupName{ get; set; }
public int Group1 { get; set; }
public int Group2 { get; set; }
public int Group3 { get; set; }
public int Group4 { get; set; }
}
The Table Contains the values for first 4 ItemName,ItemNo,ItemQty and GroupName.
for each ItemNo are having 4 rows and only ItemQty will be differ which is entered by each groupName(Group1Count ,Group2Count ,Group3Count ,GroupCount ).
Table content like this .
ItemName ItemNo ItemQty GroupName
Pen 234 2 Group1
Pen 234 4 Group2
Pen 234 6 Group3
Pen 234 3 Group4
item2 365 3 Group1
item2 365 5 Group2
item2 365 2 Group3
item2 365 3 Group4
item3 370 3 Group1
item3 370 2 Group4
item4 372 6 Group2
item4 372 9 Group4
so i am getting all the values using this query.
var data= from a in context.Batch where a.GroupName != "" select a;
from this again i need filter out based on the combination of ItemName,ItemNo,ItemQty and show the result like this.
ItemName ItemNo Group1 Group2 Group3 Group4
Pen 234 2 4 6 3
item2 365 3 5 2 3
item3 370 3 0 0 2
item4 372 0 6 0 9
Please suggest me how to write a linq query for this.
You can use
var list=context.Batch.GroupBy(x=>new{x.ItemName,x.ItemNo})
.Select(x=>new
{
ItemName=x.Key.ItemName,
ItemNo=x.Key.ItemNo,
Group1=x.Where(y=>y.GroupName=="Group1").FirstOrDefault(),
Group2=x.Where(y=>y.GroupName=="Group2").FirstOrDefault(),
Group3=x.Where(y=>y.GroupName=="Group3").FirstOrDefault(),
Group4=x.Where(y=>y.GroupName=="Group4").FirstOrDefault()
})
.Select(x=> new SampleModel{
ItemName=x.ItemName,
ItemNo=x.ItemNo,
Group1=x.Group1==null?0:x.Group1.ItemQty,
Group2=x.Group2==null?0:x.Group2.ItemQty,
Group3=x.Group3==null?0:x.Group3.ItemQty,
Group4=x.Group4==null?0:x.Group4.ItemQty
}).ToList();
var data = context.Batch
.GroupBy(i => new {i.ItemName, i.ItemNo})
.Select(g => new {
ItemName = g.Key.ItemName,
ItemNo = g.Key.ItemNo,
Group1 = g.Where(i => i.GroupName == “Group1”).First().ItemQty,
Group2 = g.Where(i => i.GroupName == “Group2”).First().ItemQty,
Group3 = g.Where(i => i.GroupName == “Group3”).First().ItemQty,
Group4 = g.Where(i => i.GroupName == “Group4”).First().ItemQty
})
.Where(i => i.GroupName != “”);
Here is the correct solution
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("ItemName", typeof(string));
dt.Columns.Add("ItemNo", typeof(int));
dt.Columns.Add("ItemQty", typeof(int));
dt.Columns.Add("GroupName", typeof(string));
dt.Rows.Add(new object[] {"Pen",234, 2, "Group1"});
dt.Rows.Add(new object[] {"Pen",234, 4, "Group2"});
dt.Rows.Add(new object[] {"Pen",234, 6, "Group3"});
dt.Rows.Add(new object[] {"Pen",234, 3, "Group4"});
dt.Rows.Add(new object[] {"item2",365, 3, "Group1"});
dt.Rows.Add(new object[] {"item2",365, 5, "Group2"});
dt.Rows.Add(new object[] {"item2",365, 2, "Group3"});
dt.Rows.Add(new object[] {"item2",365, 3, "Group4"});
List<string> groupNames = dt.AsEnumerable().Select(x => x.Field<string>("GroupName")).Distinct().ToList();
DataTable pivot = new DataTable();
pivot.Columns.Add("ItemName", typeof(string));
pivot.Columns.Add("ItemNo", typeof(int));
foreach (string groupName in groupNames)
{
pivot.Columns.Add(groupName, typeof(string));
}
var items = dt.AsEnumerable().GroupBy(x => x.Field<string>("ItemName"));
foreach (var item in items)
{
DataRow pivotRow = pivot.Rows.Add();
pivotRow["ItemName"] = item.First().Field<string>("ItemName");
pivotRow["ItemNo"] = item.First().Field<int>("ItemNo");
foreach (var group in item)
{
pivotRow[group.Field<string>("GroupName")] = group.Field<int>("ItemQty");
}
}
}
}
}
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();
}
}
}
I have 2 DataTable with the following columns:
Table 1
Title
NUMBER
Sub_num1
Sub_num2
Table 2
NUMBER
Sub_num1
Sub_num2
In Table 2 Combination of NUMBER, Sub_num1 and Sub_num2 is unique. Can be Many NUMBERS, but with different set of Sub1 and Sub2.
In Table 1 Title is unique. A couple titles can have the same NUMBER, but again different set of Subs.
I need to loop through Table 2 and check if Table 1 has exact match with all 3 columns, then get this title, if not I need to get all Titles that have this NUMBER.
What is the best and fastest way to do this search? On the top of my head I have only next:
Loop through records in Table 2 and for each record loop through Table 1 and check for match, but I think that this process can be very resource-intensive...
Any help, please?
UPDATE
Example:
var dt1 = new DataTable("Table 1");
dt1.Columns.Add("title", typeof(string));
dt1.Columns.Add("number", typeof(int));
dt1.Columns.Add("subnum1", typeof(int));
dt1.Columns.Add("subnum2", typeof(int));
dt1.Rows.Add(new object[] { "a", 1111, 1, 1 }); // Exact match!
dt1.Rows.Add(new object[] { "b", 2222, 1, 1 }); // Only NUMBER match
dt1.Rows.Add(new object[] { "b", 2222, 2, 2 }); // Only NUMBER match
dt1.Rows.Add(new object[] { "d", 3333, 1, 1 }); // Exact match!
dt1.Rows.Add(new object[] { "d", 3333, 1, 2 });
dt1.Rows.Add(new object[] { "d", 3333, 2, 1 });
var dt2 = new DataTable("Table 2");
dt2.Columns.Add("number", typeof(int));
dt2.Columns.Add("subnum1", typeof(int));
dt2.Columns.Add("subnum2", typeof(int));
dt2.Rows.Add(new object[] { 1111, 1, 1 }); // Exact match!
dt2.Rows.Add(new object[] { 2222, "", 5 }); // Only NUMBER match
dt2.Rows.Add(new object[] { 3333, 1, 1 }); // Exact match!
dt2.Rows.Add(new object[] { 3333, "", "" }); // Only NUMBER match
SO I'm looping through Table 2:
foreach (DataRow row in dt2.Rows)
{
// HERE Should be logic and search
}
RESULT should be:
If match print title, if not print ALL titleS with number, that match, so:
1. "a", 1111, 1, 1
2.1 "b", 2222, 1, 1
2.2 "b", 2222, 2, 2
3. "d", 3333, 1, 1
4.1 "d", 3333, 1, 1
4.2 "d", 3333, 1, 2
4.3 "d", 3333, 2, 1
One possibility would be to use the DataTable class build in filtering. You can define a dynamic filter and apply it to the DataTable object. The dynamic filter language is something like a subset of SQL, it has LIKE and other SQL keywords. An example of filtering code:
var dt = new DataTable("test");
dt.Columns.Add("A", typeof(string));
dt.Columns.Add("B", typeof(string));
dt.Rows.Add(new object[] { "a", "1" });
dt.Rows.Add(new object[] { "a", "2" });
var rows = dt.Select("B = '2'");
This way you can define the filter and apply it to both tables and compare only the result set and not every entry. The result is an array of Rows.
I used it in a project, that has DataTable objects containing more than 2K entries each and the performance is really good.
Another possibility would be to use LINQ to filter the data. You can query the DataTable's rows like this:
var rows = (from DataRow dr in dt.Rows
where dr["B"] == "2"
select dr).ToList();
This query returns the same result as the direct filtering. You can apply again the same approach here to check the mathching result only.
If i understood your question correctly, a possible solution to your problem could look like this:
// test DataTable objects for the example
var dt1 = new DataTable("Table 1");
dt1.Columns.Add("title", typeof(string));
dt1.Columns.Add("number", typeof(int));
dt1.Columns.Add("subnum1", typeof(int));
dt1.Columns.Add("subnum2", typeof(int));
dt1.Rows.Add(new object[] { "a", 1111, 1, 1 }); // Exact match!
dt1.Rows.Add(new object[] { "b", 2222, 1, 1 }); // Only NUMBER match
dt1.Rows.Add(new object[] { "b", 2222, 2, 2 }); // Only NUMBER match
dt1.Rows.Add(new object[] { "d", 3333, 1, 1 }); // Exact match!
dt1.Rows.Add(new object[] { "d", 3333, 1, 2 });
dt1.Rows.Add(new object[] { "d", 3333, 2, 1 });
var dt2 = new DataTable("Table 2");
dt2.Columns.Add("number", typeof(int));
dt2.Columns.Add("subnum1", typeof(int));
dt2.Columns.Add("subnum2", typeof(int));
dt2.Rows.Add(new object[] { 1111, 1, 1 }); // Exact match!
dt2.Rows.Add(new object[] { 2222, 0, 5 }); // Only NUMBER match
dt2.Rows.Add(new object[] { 3333, 1, 1 }); // Exact match!
dt2.Rows.Add(new object[] { 3333, 0, 0 }); // Only NUMBER match
foreach (DataRow row in dt1.Rows)
{
var matches = dt2.Select(string.Format("number = {0} and subnum1 = {1} and subnum2 = {2}", row["number"], row["subnum1"], row["subnum2"]));
if (matches.Count() > 0)
{
Console.WriteLine(row["title"]);
}
else
{
var fallback = dt2.Select(string.Format("number = {0}", row["number"]));
if (fallback.Count() > 0)
{
Console.WriteLine(" > " + row["title"]);
}
}
}
The output in this case is:
a
> b
> b
d
> d
> d
What values shoule be written to the output is up to you - at the point where the match is found you have all that you need.