I have several tables (the exact number is not known when the program is build) looking like this (the number of rows and columns may differ from table to table):
The source data is stored in a data set. Now I want to generate a new table where all data of all ids is stored (the picture shows only the result for id 10 and 20 but the target table should contain the data for all ids):
The equivalent SQLite statement for that looks like this:
SELECT * FROM Dataset
JOIN Datensatz2 ON (Dataset.ID=Datensatz2.ID)
JOIN Datensatz3 ON (Datensatz3.ID=Dataset.ID)
JOIN Datensatz4 ON (Datensatz4.ID=Dataset.ID)
WHERE Dataset.Id=10
UNION
SELECT * FROM Dataset
JOIN Datensatz2 ON (Dataset.ID=Datensatz2.ID)
JOIN Datensatz3 ON (Datensatz3.ID=Dataset.ID)
JOIN Datensatz4 ON (Datensatz4.ID=Dataset.ID)
WHERE Dataset.Id=20
...
The double id columns will be removed afterwards so donĀ“t worry about that. The questions is now how to convert it as a dynamic LINQ query?
There are plenty of open question but maybe this helps to solve it. Since the tables are already stored in a DataSet you could use Linq-To-DataSet and Enumerable.GroupBy to group by ID:
var idTables = ds.Tables.Cast<DataTable>().Where(t => t.Columns.Contains("Id"));
if(!idTables.Any()){ MessageBox.Show("No id-tables"); return; }
var idRowGroups = idTables.SelectMany(t => t.AsEnumerable())
.GroupBy(row => row.Field<int>("Id"))
.Select(grp => new { ID = grp.Key, Rows = grp });
foreach(var idGroup in idRowGroups)
{
Console.WriteLine("ID:{0} Rows:{1}"
, idGroup.ID
, String.Join(" | ", idGroup.Rows.Select(row => String.Join(",", row.ItemArray))));
}
Sample data:
var ds = new DataSet();
DataTable t1 = new DataTable();
t1.Columns.Add("Id", typeof(int));
t1.Columns.Add("Data", typeof(int));
t1.Rows.Add(1, 1);
t1.Rows.Add(2, 10);
t1.Rows.Add(3, 100);
t1.Rows.Add(4, 1000);
ds.Tables.Add(t1);
t1 = new DataTable();
t1.Columns.Add("Id", typeof(int));
t1.Columns.Add("Data", typeof(int));
t1.Rows.Add(4, 5);
t1.Rows.Add(5, 50);
t1.Rows.Add(7, 500);
t1.Rows.Add(3, 5997);
ds.Tables.Add(t1);
t1 = new DataTable();
t1.Columns.Add("Id", typeof(int));
t1.Columns.Add("Data1", typeof(int));
t1.Columns.Add("Data2", typeof(int));
t1.Rows.Add(1, 5, 0);
t1.Rows.Add(3, 7, 1);
t1.Rows.Add(5, 9, 11);
t1.Rows.Add(7, 11, 222);
ds.Tables.Add(t1);
Output:
ID:1 Rows:1,1 | 1,5,0
ID:2 Rows:2,10
ID:3 Rows:3,100 | 3,5997 | 3,7,1
ID:4 Rows:4,1000 | 4,5
ID:5 Rows:5,50 | 5,9,11
ID:7 Rows:7,500 | 7,11,222
Ok, I finally made it but it seems to be much too complicated. If someone is able to help me improve the solution he is very welcome.
DataSet dsResult = new DataSet();
var idTables = ds.Tables.Cast<DataTable>().Where(t => t.Columns.Contains("ID"));
if (!idTables.Any()) { MessageBox.Show("No id-tables"); return; }
var idRowGroups = idTables.SelectMany(t => t.AsEnumerable())
.GroupBy(row => row.Field<Int64>("ID"))
.Select(grp => new { ID = grp.Key, Rows = grp });
foreach (var idGroup in idRowGroups)
{
var liste = idGroup.Rows.ToList();
for (int i = 0; i < liste.Count; i++)
{
if (!dsResult.Tables.Contains(liste[i].Table.TableName))
{
dsResult.Tables.Add(liste[i].Table.TableName);
foreach (DataColumn dtCol in liste[i].Table.Columns)
{
if (dsResult.Tables[liste[i].Table.TableName].Columns.Contains("ID"))
dsResult.Tables[liste[i].Table.TableName].Columns.Add(dtCol.ColumnName+i.ToString());
else
{
dsResult.Tables[liste[i].Table.TableName].Columns.Add(dtCol.ColumnName);
}
dsResult.Tables[liste[i].Table.TableName].Columns[dtCol.ColumnName].DataType = dtCol.DataType;
}
}
DataRow dRow = dsResult.Tables[liste[i].Table.TableName].NewRow();
dRow.ItemArray = liste[i].ItemArray;
dsResult.Tables[liste[i].Table.TableName].Rows.Add(dRow);
}
IEnumerable<IEnumerable<DataRow>> allTablesRows = dsResult.Tables.Cast<DataTable>()
.Select(table => table.AsEnumerable())
.CartesianProduct();
int k = 0;
foreach (var rows in allTablesRows)
{
DataRow zRow = dsErgebnis.Tables[2].NewRow();
foreach (DataRow dRow in rows)
{
for (int i = 0; i < dRow.ItemArray.Length; i++)
{
zRow[k] = dRow.ItemArray[i];
k++;
}
}
k = 0;
dsErgebnis.Tables[2].Rows.Add(zRow);
}
dsResult.Clear();
}
First I filter the content by ID.
Then I put the result in a new tables (all rows with the ID 10 I found in 'Datasatz2' in dataset 'ds' for example I put to a new table 'Datasatz2' in the dataset 'dsResult').
At least I build the cartesian product of all tables and store it in dtaset dsErgebnis.
The result is what I expect but as mentioned before I am not satisfied with the solution.
Related
I am having an array of elements and records. I want to display all the records that only contains records in the array.
For eg:
array contains: [1,2,3]
records contains: [1,2,3,4,5,6,7,8,9,10]
I want to display only 1,2,3 records. How to compare this in c#
Sorry for my english.
Assuming that you are using linq for query:
int[] array = new[] { 1,2,3 };
var record1 = new[] { 1,2,3,4,5,6,7,8,9,10 };
var record2 = new[] { 4,5,6,7,8,9,10 };
var records = new[] { record1, record2 };
// this will return record if at least one record in array is matched
var result1 = from r in records where array.Any(a => r.Contains(a)) select r;
// this will return record only if all items in array are matched
var result2 = from r in records where array.All(a => r.Contains(a)) select r;
string[] a={"1","2","3"};
string[] b={"1","2","3","4","1","5","6","7","8","9","10"};
List<string> x=new List<string>();
foreach (string s in a)
{
if (b.Contains(s))
{
//if you only wants to display
Console.WriteLine(s);
// if you want it to store , add it to a list
if(!x.Contains(s))
x.Add(s);
}
}
I have following function that converts normal DataTable to Pivot Table.
But I have 1 more requirement, it should also show Value column i.e sum of all the Pivot column cell.
e.g. : ORDER | SUM | PLANDATE
1 50 10-Oct-2012
1 10 10-Oct-2012
2 15 12-Oct-2012
After PIVOT it should be like
ORDER | SUM | 10-Oct-2012 | 12-Oct-2012
1 60 50 10
2 15 15
my pivot function is :
DataTable Pivot(DataTable dt, DataColumn pivotColumn, DataColumn pivotValue) {
// find primary key columns
//(i.e. everything but pivot column and pivot value)
DataTable temp = dt.Copy();
temp.Columns.Remove( pivotColumn.ColumnName );
temp.Columns.Remove( pivotValue.ColumnName );
string[] pkColumnNames = temp.Columns.Cast<DataColumn>()
.Select( c => c.ColumnName )
.ToArray();
// prep results table
DataTable result = temp.DefaultView.ToTable(true, pkColumnNames).Copy();
result.PrimaryKey = result.Columns.Cast<DataColumn>().ToArray();
dt.AsEnumerable()
.Select(r => r[pivotColumn.ColumnName].ToString())
.Distinct().ToList()
.ForEach (c => result.Columns.Add(c, pivotColumn.DataType));
// load it
foreach( DataRow row in dt.Rows ) {
// find row to update
DataRow aggRow = result.Rows.Find(
pkColumnNames
.Select( c => row[c] )
.ToArray() );
// the aggregate used here is LATEST
// adjust the next line if you want (SUM, MAX, etc...)
aggRow[row[pivotColumn.ColumnName].ToString()] = row[pivotValue.ColumnName];
}
return result;
}
Thanks & any help will be appriciated...
Sample Data :
OrderStatus Qty Dated
New 100 10-Oct-2012
New 150 10-Oct-2012
New 100 10-Oct-2012
Conf 200 12-Oct-2012
New 100 12-Oct-2012
Reconf 300 12-Oct-2012
New 200 15-Oct-2012
New 200 15-Oct-2012
Reconf 100 18-Oct-2012
New 150 18-Oct-2012
Conf 150 18-Oct-2012
New 100 20-Oct-2012
Reconf 500 20-Oct-2012
New 100 30-Oct-2012
New 700 30-Oct-2012
New 100 30-Oct-2012
Conf 100 10-Oct-2012
Update Code :
private DataTable DataTableToPivot(DataTable dtNormalTable, DataColumn pivotColumn, DataColumn valueColumn)
{
cTracing.WriteTrace("Inside DataTableToPivot", "DataTableToPivot");
DataTable dtPivot = null;
DataTable tempPivot = null;
DataColumn sumColumn = new DataColumn();
string[] pkColumnNames = null;
try
{
sumColumn.ColumnName = valueColumn.ColumnName;
sumColumn.DataType = typeof(string);
sumColumn.DefaultValue = "0";
var allrows = dt.AsEnumerable().Select(delegate(DataRow r)
{
DataRow nr = dt.NewRow();
foreach (DataColumn item in dt.Columns)
{
nr.SetField(item.ColumnName, r[item.ColumnName] is DBNull ? string.Empty : r[item.ColumnName]);
}
return nr;
});
dtNormalTable = allrows.CopyToDataTable();
// find primary key columns
//(i.e. everything but pivot column and pivot value)
tempPivot = dtNormalTable.Copy();
tempPivot.Columns.Remove(pivotColumn.ColumnName);
tempPivot.Columns.Remove(valueColumn.ColumnName);
pkColumnNames = tempPivot.Columns
.Cast<DataColumn>()
.Select(c => c.ColumnName)
.ToArray();
// prep results table
dtPivot = tempPivot.DefaultView.ToTable(true, pkColumnNames).Copy();
dtPivot.PrimaryKey = dtPivot.Columns.Cast<DataColumn>().ToArray();
// include the sum column
dtPivot.Columns.Add(sumColumn);
try
{
dtNormalTable.AsEnumerable()
.Select(r => r[pivotColumn.ColumnName].ToString())
.Distinct()
.ToList()
.ForEach(c => dtPivot.Columns.Add(Convert.ToDateTime(c).ToString("dd-MMM-yyyy"), typeof(string)));
}
catch
{
dtNormalTable.AsEnumerable()
.Select(r => r[pivotColumn.ColumnName].ToString())
.Distinct()
.ToList()
.ForEach(c => dtPivot.Columns.Add(c, typeof(string)));
}
// load it
foreach (DataRow row in dtNormalTable.Rows)
{
// find row to update
DataRow aggRow = dtPivot.Rows.Find(
pkColumnNames
.Select(c => row[c])
.ToArray());
// the aggregate used here is LATEST
// adjust the next line if you want (SUM, MAX, etc...)
string columnName = string.Empty;
try
{
columnName = Convert.ToDateTime(row[pivotColumn.ColumnName]).ToString("dd-MMM-yyyy");
}
catch
{
columnName = row[pivotColumn.ColumnName].ToString();
}
if (aggRow.IsNull(columnName))
{
aggRow[columnName] = Convert.ToDecimal(row[valueColumn.ColumnName]);
}
else
{
aggRow[columnName] = Convert.ToDecimal(aggRow[columnName]) +
Convert.ToDecimal(row[valueColumn.ColumnName]);
}
if (numberFormat == "3")
{
// add the value to the sum
aggRow[sumColumn] = (Convert.ToDecimal(aggRow[sumColumn].ToString()) +
Convert.ToDecimal(row[valueColumn.ColumnName].ToString())).ToString("#0.000");
}
else if (numberFormat == "2")
{
// add the value to the sum
aggRow[sumColumn] = (Convert.ToDecimal(aggRow[sumColumn].ToString()) +
Convert.ToDecimal(row[valueColumn.ColumnName].ToString())).ToString("#0.00");
}
else if (numberFormat == "1")
{
// add the value to the sum
aggRow[sumColumn] = (Convert.ToDecimal(aggRow[sumColumn].ToString()) +
Convert.ToDecimal(row[valueColumn.ColumnName].ToString())).ToString("#0.0");
}
else
{
// add the value to the sum
aggRow[sumColumn] = (Convert.ToDecimal(aggRow[sumColumn].ToString()) +
Convert.ToDecimal(row[valueColumn.ColumnName].ToString())).ToString("#0");
}
}
return dtPivot;
}
catch (Exception ex)
{
cTracing.WriteTrace("Error inside DataTableToPivot", "DataTableToPivot");
cLogging.WriteLog(ex);
return null;
}
finally
{
if (dtPivot != null)
dtPivot.Dispose();
}
}
enter code here
How about adding each row's value to the sum column in the corresponding aggregate row?
DataTable Pivot(DataTable dt, DataColumn pivotColumn, DataColumn pivotValue) {
DataColumn sumColumn = new DataColumn();
sumColumn.ColumnName = "SUM";
sumColumn.DataType = typeof(int);
sumColumn.DefaultValue = 0;
// find primary key columns
//(i.e. everything but pivot column and pivot value)
DataTable temp = dt.Copy();
temp.Columns.Remove( pivotColumn.ColumnName );
temp.Columns.Remove( pivotValue.ColumnName );
string[] pkColumnNames = temp.Columns.Cast<DataColumn>()
.Select( c => c.ColumnName )
.ToArray();
// prep results table
DataTable result = temp.DefaultView.ToTable(true, pkColumnNames).Copy();
result.PrimaryKey = result.Columns.Cast<DataColumn>().ToArray();
// include the sum column
result.Columns.Add(sumColumn);
dt.AsEnumerable()
.Select(r => r[pivotColumn.ColumnName].ToString())
.Distinct()
.OrderBy(c => Convert.ToDateTime(c)) // Order by the date
.ToList()
.ForEach (c => result.Columns.Add(c, pivotColumn.DataType));
// load it
foreach( DataRow row in dt.Rows ) {
// find row to update
DataRow aggRow = result.Rows.Find(
pkColumnNames
.Select( c => row[c] )
.ToArray() );
// the aggregate used here is LATEST
// adjust the next line if you want (SUM, MAX, etc...)
string columnName = row[pivotColumn.ColumnName].ToString();
if(aggRow.IsNull(columnName))
{
aggRow[columnName] = (int)row[pivotValue.ColumnName];
}
else
{
aggRow[columnName] = Convert.ToInt32(aggRow[columnName]) +
(int)row[pivotValue.ColumnName];
}
// add the value to the sum
aggRow[sumColumn] = (int)aggRow[sumColumn] +
(int)row[pivotValue.ColumnName];
}
return result;
}
I am trying to use following code to create the PIVOT but its not working.
It's giving me compile time error. I don't know linq so unable to use it.
Please help :
DataTable Pivot(DataTable dt, DataColumn pivotColumn, DataColumn pivotValue) {
// find primary key columns
//(i.e. everything but pivot column and pivot value)
DataTable temp = dt.Copy();
temp.Columns.Remove( pivotColumn.ColumnName );
temp.Columns.Remove( pivotValue.ColumnName );
string[] pkColumnNames = temp.Columns.Cast(<DataColumn>)
.Select( c => c.ColumnName )
.ToArray();
// prep results table
DataTable result = temp.DefaultView.ToTable(true, pkColumnNames).Copy();
result.PrimaryKey = result.Columns.Cast(<DataColumn>).ToArray();
dt.AsEnumerable()
.Select(r =>; r[pivotColumn.ColumnName].ToString())
.Distinct().ToList()
.ForEach (c => result.Columns.Add(c, pivotColumn.DataType));
// load it
foreach( DataRow row in dt.Rows ) {
// find row to update
DataRow aggRow = result.Rows.Find(
pkColumnNames
.Select( c => row[c] )
.ToArray() );
// the aggregate used here is LATEST
// adjust the next line if you want (SUM, MAX, etc...)
aggRow[row[pivotColumn.ColumnName].ToString()] = row[pivotValue.ColumnName];
}
return result;
}
Code from : http://michaeljswart.com/2011/06/forget-about-pivot/
Moreover it tried to use following code, it works well except for it is not giving total sum for Value Column
public DataTable GetInversedDataTable(DataTable table, string columnX, string columnY, string columnZ, string nullValue, bool sumValues)
{
//Create a DataTable to Return
DataTable returnTable = new DataTable();
DataTable tempTable = table.Clone();
if (string.IsNullOrEmpty(columnX))
{
columnX = table.Columns[0].ColumnName;
}
tempTable.Columns.Remove(columnX);
//Add a Column at the beginning of the table
//returnTable.Columns.Add(columnY);
returnTable = tempTable.Clone();
//Read all DISTINCT values from columnX Column in the provided DataTale
List<string> columnXValues = new List<string>();
foreach (DataRow dr in table.Rows)
{
string columnXTemp = dr[columnX].ToString();
if (!columnXValues.Contains(columnXTemp))
{
//Read each row value, if it's different from others provided, add to the list of values and creates a new Column with its value.
columnXValues.Add(columnXTemp);
returnTable.Columns.Add(columnXTemp);
}
}
//Verify if Y and Z Axis columns re provided
if (!string.IsNullOrEmpty(columnY) && !string.IsNullOrEmpty(columnZ))
{
//Read DISTINCT Values for Y Axis Column
List<string> columnYValues = new List<string>();
foreach (DataRow dr in table.Rows)
{
if (!columnYValues.Contains(dr[columnY].ToString()))
{
columnYValues.Add(dr[columnY].ToString());
}
}
//Loop all Column Y Distinct Value
foreach (string columnYValue in columnYValues)
{
//Creates a new Row
DataRow drReturn = returnTable.NewRow();
drReturn[0] = columnYValue;
//foreach column Y value, The rows are selected distincted
DataRow[] rows = table.Select((columnY + "='") + columnYValue + "'");
//Read each row to fill the DataTable
foreach (DataRow dr in rows)
{
string rowColumnTitle = dr[columnX].ToString();
//Read each column to fill the DataTable
foreach (DataColumn dc in returnTable.Columns)
{
if (dc.ColumnName == rowColumnTitle)
{
//If Sum of Values is True it try to perform a Sum
//If sum is not possible due to value types, the value displayed is the last one read
if (sumValues)
{
try
{
drReturn[rowColumnTitle] = Convert.ToDecimal(drReturn[rowColumnTitle]) + Convert.ToDecimal(dr[columnZ]);
}
catch
{
drReturn[rowColumnTitle] = dr[columnZ];
}
}
else
{
drReturn[rowColumnTitle] = dr[columnZ];
}
}
}
}
returnTable.Rows.Add(drReturn);
}
}
else
{
throw new Exception("The columns to perform inversion are not provided");
}
//if a nullValue is provided, fill the datable with it
if (!string.IsNullOrEmpty(nullValue))
{
foreach (DataRow dr in returnTable.Rows)
{
foreach (DataColumn dc in returnTable.Columns)
{
if (string.IsNullOrEmpty(dr[dc.ColumnName].ToString()))
{
dr[dc.ColumnName] = nullValue;
}
}
}
}
return returnTable;
}
GetInversedDataTable(dtNormal, "Dated", "OrderStatus", "Qty", " ", true);
Please help :)
Here is the code with the compilation errors corrected:
DataTable Pivot(DataTable dt, DataColumn pivotColumn, DataColumn pivotValue) {
// find primary key columns
//(i.e. everything but pivot column and pivot value)
DataTable temp = dt.Copy();
temp.Columns.Remove( pivotColumn.ColumnName );
temp.Columns.Remove( pivotValue.ColumnName );
string[] pkColumnNames = temp.Columns.Cast<DataColumn>()
.Select( c => c.ColumnName )
.ToArray();
// prep results table
DataTable result = temp.DefaultView.ToTable(true, pkColumnNames).Copy();
result.PrimaryKey = result.Columns.Cast<DataColumn>().ToArray();
dt.AsEnumerable()
.Select(r => r[pivotColumn.ColumnName].ToString())
.Distinct().ToList()
.ForEach (c => result.Columns.Add(c, pivotColumn.DataType));
// load it
foreach( DataRow row in dt.Rows ) {
// find row to update
DataRow aggRow = result.Rows.Find(
pkColumnNames
.Select( c => row[c] )
.ToArray() );
// the aggregate used here is LATEST
// adjust the next line if you want (SUM, MAX, etc...)
aggRow[row[pivotColumn.ColumnName].ToString()] = row[pivotValue.ColumnName];
}
return result;
}
I changed Cast(<DataColumn>) to Cast<DataColumn>() in two locations and got rid of the semicolon in the middle of a lambda expression. The second part of your question is a little trickier. You may want to ask it as its own question.
Good one., but you might want to replace the below line
.ForEach (c => result.Columns.Add(c, pivotColumn.DataType));
with this (change pivotColumn to pivotValue)
.ForEach (c => result.Columns.Add(c, pivotValue.DataType));
Works perfectly for my requirement.
equal this
select id,name, count(*) from table group by id, name
what is in linq???
In case of entity framework it is better to return computed projection directly from SQL:
var query = from x in context.YourEntities
group x by new { x.ID, x.Name } into y
select new
{
y.Key.ID,
y.Key.Name,
y.Count()
};
This will do Count in database and reduce amount of transferred data.
var groups = table.GroupBy(elt => new {ID = elt.ID, Name = elt.name});
foreach (var group in groups)
{
var ID = group.Key.ID;
var name = group.Key.Name;
var count = group.Count();
...
}
I want to Enumerate Linq Query. Below i specified example.
EX:
DataTable _slidingDataTable = new DataTable("test");
for(int i=0; i<5;i++)
{
DataRow row = _slidingDataTable.NewRow();
startPosition = DateTime.Now;
for(int i=0; i<5;i++)
{
_slidingDataTable.Columns.Add("TransferTime");
row[columnName] = startPosition ;
_slidingDataTable.Columns.Add("TransferData");
row[columnName] = "Test"+i;
}
_slidingDataTable.Rows.Add(row);
}
var query1 = from myRow in _slidingDataTable.AsEnumerable()
where myRow.Field<DateTime>("TransferTime") == startPosition
select myRow;
This query output should be collection of rows. How to get collection row & iterate.
In your context, query1 is an EnumerableRowCollection<DataRow> because you used _slidingDataTable.AsEnumerable(), and you can iterate over it like so :
foreach (DataRow row in query1)
{
// Do stuff with that row
}
I'm giving you an example by which you can see it and it also includes sum in groupby.
var drdatedisp = from row in dtfullreport.AsEnumerable()
group row by row.Field<string>("Order_Date") into g
select new
{
Order_Date = g.Key,
totalQnty = g.Sum(a => a.Field<int>("Item_Quantity")),
totalTax = g.Sum(a => float.Parse(a.Field<decimal>("TAXAMT").ToString())),
totalAmt = g.Sum(a => float.Parse(a.Field<decimal>("VALAMT").ToString()))
};
DataTable dtdatedisp = new DataTable();
dtdatedisp.Columns.Add("Order_Date");
dtdatedisp.Columns.Add("Item_Quantity");
dtdatedisp.Columns.Add("TAXAMT");
dtdatedisp.Columns.Add("VALAMT");
dtdatedisp.Rows.Clear();
foreach (var g in drdatedisp)
{
DataRow newRow1 = dtdatedisp.NewRow();
newRow1[0] = g.Order_Date;
newRow1[1] = g.totalQnty;
newRow1[2] = String.Format("{0:0.00}", g.totalTax);
newRow1[3] = String.Format("{0:0.00}", g.totalAmt);
dtdatedisp.Rows.Add(newRow1);
}