Date table column Sum and differencer - c#

i need to make a data table like that:
Subjects old new diff
Sub_1 10 50 40
Sub_2 30 10 -20
total 40 60 20
this is a part of code
DataTable subjects = new DataTable();
subjects.Columns.Add("Subjects");
subjects.Columns.Add("old");
subjects.Columns.Add("new");
subjects.Columns.Add("diff");
subjects.Rows.Add("Sub_1", sub1.Old, sub1.New, (sub1.New - sub1.Old));
subjects.Rows.Add("Sub_2", sub2.Old, sub2.New, (sub2.New - sub2.Old));
subjects.Rows.Add("Total", .. total of above .. , .. total of above .., .. total of above ..);
so i need to ask how to calculate the total value of last column ( Total) , and is there is any other way to calculate the 4th coulmn ( 3rdcol - 2 2nd col )

First of all, you have declared your columns without DataType(thanks to #Steve for comment). So, please change Add() methods as:
subjects.Columns.Add("old", typeof(Int32));
subjects.Columns.Add("new", typeof(Int32));
Also, you can set the value for diff column like this:
subjects.Columns.Add("diff", typeof(Int32), "new - old");
And, then remove any other calculations in Rows.Add method:
subjects.Rows.Add("Sub_1", sub1.Old, sub1.New);
subjects.Rows.Add("Sub_2", sub2.Old, sub2.New);
And then you can use DataTable.Compute(string expression, string filter) method. It computes the given expression on the current rows that pass the filter criteria.
In your case expression will be Sum(columnName) and the filter will be empty string, because you don't need any filter.
subjects.Compute("Sum(old)", "")
So, change your code as:
subjects.Rows.Add("Total",
subjects.Compute("Sum(old)", ""),
subjects.Compute("Sum(new)", ""),
subjects.Compute("Sum(diff)", ""));

Related

Expression in datatable appends data instead of adding

I have a datatable with a few columns .
I am trying to add the column values using the datacolumn.expression.
The columns used for adding is of type decimal. Also the calculated column is also decimal. But while processing the expression, (like datatable column1+ datatable column2) its just appending the data.
SlNo Name F1 F2 F3
1 A 1 2 3
2 B 3 4 5
I am expecting an output similar to this.
SlNo Name F1 F2 F3 Total
1 A 1 2 3 6
2 B 3 4 5 12
What I tried.
dtTempData.Columns.Add("Total", typeof(Decimal));
dtTempData.Columns["Total"].Expression = "[F1]+[F2]+[F3]";
Now the output I am getting is in the following way
123
345
its just appending the data.Thanks in advance of any help.
I don't know why this is happening.
dtTempData.Columns.Add("Total", typeof(Decimal));
dtTempData.Columns["Total"].DefaultValue = 0;
dtTempData.Columns["Total"].Expression = expression;
This is the way I created the columns, but while performing suming based on expression, it appends the data.
I am importing data from another datatable which is type of string. So I tried to convert the data by using the following manner.
string expression="Convert(F1, 'System.Decimal') + Convert(F2,
'System.Decimal') + Convert(F3, 'System.Decimal')"
Now this is working and the Total column is having the value after addition. Thanks all for your help.

Stringbuilder and DataAdapter.Update duplicating lines

I have a DataTable similar to
Col1 Col2 Col3 Col4 Col5 Date
21 22 23 24 25 7/25/2014 12:00:00 AM
31 32 33 34 35 7/25/2014 12:00:00 AM
11 12 13 14 15 7/25/2014 12:00:00 AM
and I loop through it as
StringBuilder output = new StringBuilder("Col1\tCol2\tCol3\tCol4\tCol5\tDate\n");
foreach(DataRow row in partTable.Select()) {
output.AppendLine(String.Join("\t", row.ItemArray.ToArray()));
}
and do some formatting using StringBuilder.Replace on output. I print the result to a MessageBox and it duplicates my rows. The first time I call this it prints 2 copies, the next it prints 3, etc. (After one call.) I have checked repeatedly that the table is correct and doesn't contain duplicates. Below is the full code for this function.
private void printTable() {
updateDataSet();
if (partTable.Rows.Count == 0) {
MessageBox.Show("Table is empty.", "Table");
return;
}
StringBuilder output = new StringBuilder("Col1\tCol2\tCol3\tCol4\tCol5\tDate\n");
foreach(DataRow row in partTable.Select()) {
output.AppendLine(String.Join("\t", row.ItemArray.ToArray()));
}
// Get rid of time and type
output.Replace("12:00:00 AM", "");
output.Replace("W\t", "");
MessageBox.Show(output.ToString(), "Table");
output.Clear();
}
Solution Implemented: Commenting out updateDataSet() removes the duplication. I guess I just need to try to read MSDN more carefully... Replaced Fill with Update, but it would not remove any rows I deleted. Used a combination of Clear and Fill to get an updated table without recreating the connection.
If you call two times the DataAdapter.Fill(DataTable) method you double the records present in your datatable. To avoid this behaviour you need to write (inside the updateDataSet() method)
OleDbDataAdapter da = new OleDbDataAdapter(.....)
DataTable dt = new DataTable();
da.MissingSchemaAction = MissingSchemaAction.AddWithKey;
da.Fill(dt);
// Just for testing
// Check these results with and without the MissingSchemaAction flag
Console.WriteLine(dt.Rows.Count);
da.Fill(dt);
Console.WriteLine(dt.Rows.Count);
Of course, the presence of MissingSchemaAction.AddWithKey results in poorer performances if you don't remove the cause of the second (or third call) to updateDataSet() Infact, in this scenario the loading method should check every row present to find duplicates.
Please put all your code for analysis.
Also, there is logic/design issue in the code which has been provided.
PrintTable method is expected to read and print data only. UpdateDataset is breaking intention of code.

How to identify proper substring length

I'm trying to read column values from this file starting at the arrow position:
Here's my error:
I'm guessing it's because the length values are wrong.
Say I have column with value :"Dog "
with the word dog and a few spaces after it. Do I have to set the length parameter as 3 (for dog) or can I set it as 6 to accommodate the spaces after Dog. This because each column length is fixed. As you can see some words are smaller than others and in order to be consistent I just want to set length as max column length (ex: 28 is length of 3rd column of my file but not all 28 spots are taken up everytime - ex: the word client is only 6 characters long
Robert Levy's answer is correct for the issue you're seeing - you've attempted to pull a substring from a string with a starting position that is greater than the length of the string.
You're parsing a fixed-length field file, where each field has a certain amount of characters, whether or not it uses all of them, and the pos and len arrays are intended to define those field lengths for use with Substring. As long as the line you're reading matches the expected field starts and lengths, you will be ok. As soon as you come to a line that doesn't match (for example, what appears to be the totals line - 0TotalRecords: 3,390,315) the field length definitions you've been using won't work, as the format has changed (and the line length may not even be the same).
There are a couple of things I would change to make this work. First, I would change your pos and len arrays so that they take the entirety of the field, not part of it. You can use Trim() to get rid of any leading or trailing blanks. As defined, your first field will only take the last number of the Seq# (pos 4, len 1), and your second field will only take the first 5 characters of the field, even though it appears to have space for ~12 characters.
Take a look at this (it's hard to be exact working from the picture, but for purposes of demonstration it will work):
1 2 3 4
01234567890123456789012345678901234567890
Seq# Field Description
3 BELNR ACCOUNTING DOCUMENT NBR
The numbers are the position of each charcter in the line. I would define the pos array to be the start of the field (0 for the first field, and then the position of the first letter of the field heading for each field after that), so you would have:
Seq# = 0
Field = 6
Description = 18
The len array would hold the length of the field, which I would define as the amount of characters up to the beginning of the next field, like this:
Seq# = 6
Field = 12
Description = 28 (using what you have as it is hard to tell
This would make your array initialization the following:
int[] pos = new int[3] { 0, 6, 18 };
int[] len = new int[3] { 6, 12, 28 };
If you wanted the fourth field, it would start at position 36 (pos 18 + len 28 = 36).
The second thing is I would check in the loop to see if the Total Records line is there, and skip that line (most likely it's the last line):
foreach (string line in textBox1.Lines)
{
if (!line.Contains("Total Records"))
{
val[j] = line.Substring(pos[j], len[j]).Trim();
}
}
Another way to do this would be to modify the original query and add a TakeWhile clause to it to only take lines until you hit the Total Records one:
string[] lines = File.ReadAllLines(ofd.FileName).Skip(8)
.TakeWhile(l => !l.Contains("Total Records")).ToArray();
The above would skip the first 8 lines and take all the remaining lines up to, but not including, the first line to contain "Total Records" in the string.
Then you could do something like this:
string[] lines = File.ReadAllLines(ofd.FileName).Skip(8)
.TakeWhile(l => !l.Contains("Total Records")).ToArray();
textBox1.Lines = lines;
int[] vale = new int[3];
int[] pos = new int[3] { 0, 6, 18 };
int[] len = new int[3] { 6, 12, 28 };
foreach (string line in textBox1.Lines)
{
val[j] = line.Substring(pos[j], len[j]).Trim();
}
Now you don't have to check for the "Total Records" line.
Of course, if there are other lines in your file, or there are records after the "Total Records" line (which I rather doubt) you'll have to handle those cases as well.
In short, the code for pulling out the substrings will only work for lines that match that particular format (or more specifically, have fields that match those positions/lengths) - anything outside out of that will either give you incorrect values or throw an error (if the start position is greater than the length of the string).
that exception is complaining about the first parameter which suggests that your file contains a row that is < 18 characters

add a row to the datatable so that it should display sum of amounts on the desired columns [duplicate]

This question already has answers here:
Totals Row in a DataGridView
(3 answers)
Closed 9 years ago.
Hi all I am binding a DataTable to the DataGridView with some of the columns along with some amount fields.
I need to show the sum of the columns for each of the amount filed in DataGridView. I have already searched for many options to add a footer to the DataGridView. Is there any possibility to sum up few columns in the DataTable so that I can display the data in DataGridView?
This is my datatable data roughly:
Name Address Amount DiscountAmount
XYZ ABC 100.00 20.00
ABC DEF 150.00 0.00
Required result is:
Name Address Amount DiscountAmount
XYZ ABC 100.00 20.00
ABC DEF 150.00 0.00
250.00 20.00
I have already tried some thing like following - and it didn't worked:
DataRow lRow = lDTGrid.NewRow();
lRow[0] = "Totals";
for (int i = 1; i < lDTGrid.Columns.Count; i++)
{
lRow[lDTGrid.Columns[i].ColumnName] = lDTGrid.Compute("Sum(" + lDTGrid.Columns[i].ColumnName + ")", "");
}
Have you tried using the DataTable.Compute() method
private void ComputeBySalesSalesID(DataSet dataSet)
{
// Presumes a DataTable named "Orders" that has a column named "Total."
DataTable table;
table = dataSet.Tables["Orders"];
// Declare an object variable.
object sumObject;
sumObject = table.Compute("Sum(Total)", "EmpID = 5");
}
Does it have to be row in datagridview? Can't you use textboxes under datagridview? Much easier and still neat.
You can make them unabled, and put there a sum from column at one of events (cell edit, row added or something simillar).

Issue with data table Select statement

The following VB line, where _DSversionInfo is a DataSet, returns no rows:
_DSversionInfo.Tables("VersionInfo").Select("FileID=88")
but inspection shows that the table contains rows with FileID's of 92, 93, 94, 90, 88, 89, 215, 216. The table columns are all of type string.
Further investigation showed that using the ID of 88, 215 and 216 will only return rows if the number is quoted.
ie _DSversionInfo.Tables("VersionInfo").Select("FileID='88'")
All other rows work regardless of whether the number is quoted or not.
Anyone got an explanation of why this would happen for some numbers but not others? I understand that the numbers should be quoted just not why some work and others don't?
I discovered this in some VB.NET code but (despite my initial finger pointing) don't think it is VB.NET specific.
According to the MSDN documentation on building expressions, strings should always be quoted. Failing to do so produces some bizarro unpredictable behavior... You should quote your number strings to get predictable and proper behavior like the documentation says.
I've encounted what you're describing in the past, and kinda tried to figure it out - here, pop open your favorite .NET editor and try the following:
Create a DataTable, and into a string column 'Stuff' of that DataSet, insert rows in the following order: "6", "74", "710", and Select with the filter expression "Stuff = 710". You will get 1 row back. Now, change the first row into any number greater than 7 - suddenly, you get 0 rows back.
As long as the numbers are ordered in proper descending order using string ordering logic (i.e., 7 comes after 599) the unquoted query appears to work.
My guess is that this is a limitation of how DataSet filter expressions are parsed, and it wasn't meant to work this way...
The Code:
// Unquoted filter string bizzareness.
var table = new DataTable();
table.Columns.Add(new DataColumn("NumbersAsString", typeof(String)));
var row1 = table.NewRow(); row1["NumbersAsString"] = "9"; table.Rows.Add(row1); // Change to '66
var row2 = table.NewRow(); row2["NumbersAsString"] = "74"; table.Rows.Add(row2);
var row4 = table.NewRow(); row4["NumbersAsString"] = "90"; table.Rows.Add(row4);
var row3 = table.NewRow(); row3["NumbersAsString"] = "710"; table.Rows.Add(row3);
var results = table.Select("NumbersAsString = 710"); // Returns 0 rows.
var results2 = table.Select("NumbersAsString = 74"); // Throws exception "Min (1) must be less than or equal to max (-1) in a Range object." at System.Data.Select.GetBinaryFilteredRecords()
Conclusion: Based on the exception text in that last case, there appears to be some wierd casting going on inside filter expressions that is not guaranteed to be safe. Explicitely putting single quotes around the value for which you're querying avoids this problem by letting .NET know that this is a literal.
DataTable builds an index on the columns to make Select() queries fast. That index is sorted by value, then it uses a binary search to select the range of records that matches the query expression.
So the records will be sorted like this 215,216,88,89,90,92,93,94. A binary search is done treating them as integer (as per our filter expression) cannot locate certain records because, it is designed to only search properly sorted collections.
It indexes the data as string and Binary search searches as number. See the below explanation.
string[] strArr = new string[] { "115", "118", "66", "77", "80", "81", "82" };
int[] intArr = new int[] { 215, 216, 88, 89, 90, 92, 93, 94 };
int i88 = Array.BinarySearch(intArr, 88); //returns -ve index
int i89 = Array.BinarySearch(intArr, 89); //returns +ve index
This should be a bug in the framework.
this error usually comes due to invalid data table column type in which you are going to search
i got this error when i was using colConsultDate instead of Convert(colConsultDate, 'System.DateTime')
because colConsultDate was a data table column of type string which i must have to convert into System.DateTime therefor your search query should be like
string query = "Convert(colConsultDate, 'System.DateTime') >= #" + sdateDevFrom.ToString("MM/dd/yy") + "# AND Convert(colConsultDate, 'System.DateTime') <= #" + sdateDevTo.ToString("MM/dd/yy") + "#";
DataRow[] dr = yourDataTable.Select(query);
if (dr.Length > 0)
{
nextDataTabel = dr.CopyToDataTable();
}
#Val Akkapeddi just wanna add things to your answer.
if you do something like this it would be benefited specially when you have to use comparison operators. because you put quotes around 74 it will be treated as string. please see yourself by actually writing code. Comparison operators
(decimal is just for reference you can add your desired datatype instead.)
var results2 = table.Select("Convert(NumbersAsString , 'System.Decimal') = 74.0")

Categories

Resources