Extract the sum of two things from datatable - c#

I have a table in a SQL Server database with many columns but the important columns are LoggedState and InteractionType.
I need to find the number of break agents and the number of idle agents.
What I have tried
SqlCommand GraphCmd = new SqlCommand("getAgentStatues", Graphsqlcon);
SqlParameter tdate = new SqlParameter();
GraphCmd.CommandType = CommandType.StoredProcedure; ;
SqlDataAdapter DAGraph = new SqlDataAdapter(GraphCmd);
DataSet DSGraph = new DataSet();
DSGraph.Clear();
DAGraph.Fill(DSGraph);
DataTable DTgraph = new DataTable();
DTgraph = DSGraph.Tables[0];
int numberOfBreakAgents = 0;
int numberOfIdelAgents = 0;
foreach (DataRow row in DTgraph.Rows)
{
String LoggedState = row["LoggedState"].ToString().Trim().ToLower();
String InteractionType = row["InteractionType"].ToString();
if (LoggedState == "break")
{
numberOfBreakAgents++;
}
else if ((LoggedState == "activo") && (row["InteractionType"] == DBNull.Value))
{
numberOfIdelAgents++;
}
}
it works perfectly, but I am asking if there is a way (like grouping) to avoid the foreach statement

You could use the Group function from Linq:
var loggedStateGroups = dt.AsEnumerable().GroupBy(d => d["LoggedState"].ToString(), (group, row) => new
{
LoggedState = group,
AllCount = row.Count(),
NullCount = row.Where(r => r["InteractionType"] == DBNull.Value).Count()
});
That will group by the LoggedState with a count for each matching row (AllCount) and a count for rows where the InteractionType is DBNull.Value (NullCount).
We can then select the counts we are after by doing:
int numberOfBreakAgents = loggedStateGroups.Where(y => y.LoggedState == "break").First().AllCount;
int numberOfIdelAgents = loggedStateGroups.Where(y => y.LoggedState == "activo").First().NullCount;
Note I'm only using First assuming you will always have results. If you won't always have results you should use FirstOrDefault and perform a null check.
You could filter before using the Group by adding the following Where depending on your data.
.Where(r => r["LoggedState"].ToString() == "break" || r["LoggedState"].ToString() == "activo")
I've tested this with the following setup:
DataTable dt = new DataTable();
dt.Columns.Add("LoggedState");
dt.Columns.Add("InteractionType");
dt.Rows.Add("break", "inter1");
dt.Rows.Add("activo", DBNull.Value);
dt.Rows.Add("break", "inter1");
dt.Rows.Add("break", "inter2");
dt.Rows.Add("activo", "inter2");
And I get 3 and 1 for the numberOfBreakAgents and numberOfIdelAgents respectively.
Edit for using FirstOrDefault:
If you'd like to perform the null check as mentioned above you can replace the two int declaration lines above with:
var breakAgents = loggedStateGroups.Where(y => y.LoggedState == "break").FirstOrDefault();
var idelAgents = loggedStateGroups.Where(y => y.LoggedState == "activo").FirstOrDefault();
int numberOfBreakAgents = breakAgents != null ? breakAgents.AllCount : 0;
int numberOfIdelAgents = idelAgents != null ? idelAgents.NullCount : 0;
This is taking the first group that has the LoggedState of "break" or null if there isn't one. It then assigns numberOfBreakAgents the AllCount property if the group is not null or 0 if it is.
A similar thing is done for numberOfIdelAgents except we filter for the "activo" group and use the NullCount property as we aren't interested in all rows we are only interested in those where the InteractionType was DBNull.Value which we've captured in the NullCount property.
The null check is necessary if the result set will ever contain zero rows with the LoggedState of "activo" or zero rows with the LoggedState of "break". In that instance the .First() will return null and accessing AllCount or NullCount from that will result in a "Sequence contains no elements" exception.
Using the following DataTable definition will highlight the difference as it causes an exception for numberOfBreakAgents using First() but correctly returns 0 when using FirstOrDefault.
DataTable dt = new DataTable();
dt.Columns.Add("LoggedState");
dt.Columns.Add("InteractionType");
dt.Rows.Add("activo", "inter1");
dt.Rows.Add("activo", DBNull.Value);
dt.Rows.Add("activo", "inter1");
dt.Rows.Add("activo", "inter2");
dt.Rows.Add("activo", "inter2");

Could you not do
var breakAgents = from row in DTgraph.AsEnumerable()
where row["LoggedState"].ToString().Trim().ToLower() == "break"
select row;
var breakAgentsCount = breakAgents.Count();
and
var idleAgents = from row in DTgraph.AsEnumerable()
where row["LoggedState"].ToString().Trim().ToLower() == "activo"
&& row["InteractionType"] == DBNull.Value
select row;
var idleAgentsCount = idleAgents.Count();

Using the Count-function that LINQ provides us, the following solution should work:
// Cast the rows to a collection of DataRows.
IEnumerable<DataRow> collection = DTgraph.Rows.Cast<DataRow>();
// Get the number of Break Agents.
int numberOfBreakAgents = collection.Count(row => row["LoggedState"].ToString().Trim().ToLower() == "break");
// Get the number of Idel Agents.
int numberOfIdelAgents = collection.Count(row => row["LoggedState"].ToString().Trim().ToLower() == "activo" && row["InteractionType"] == DBNull.Value);
The cast is used to allow the use of LINQ on the DataRow-collection.
Another option would be to cast the DataRow-collection to a List of type DataRow. Then using a ForEach (also LINQ), to determine the agent-type:
List<DataRow> collection = DTgraph.Rows.Cast<DataRow>().ToList();
collection.ForEach(row =>
{
if (row["LoggedState"].ToString().Trim().ToLower() == "break")
numberOfBreakAgents++;
else if (row["LoggedState"].ToString().Trim().ToLower() == "activo" && row["InteractionType"] == DBNull.Value)
numberOfIdelAgents++;
});
Above example is very much the same to your example, but written a bit shorter and without the use of two strings (LoggedState and InteractionType).

You can execute sql query like this:
select
sum(case when LoggedState = "break" then 1 else 0 end) break_count,
sum(case when LoggedState = "activo" and InteractionType is null then 1 else 0 end) active_count
from table_name

Related

Datatable search by LINQ to avoid duplication

I have a column in dataTable with blank rows
Column1
A
B
C
D
E
I need to set if exist and to avoid adding, but blank rows should not be counted. Only rows with data should be in the (if exists). Thanks
bool exists = dt.Select().ToList().Exists(row => row["column1"].ToString() == txtbox)
if(exists == true)
{}
else
// it includes blank so it goes to true, which I need is blank rows should not be included.
var lignesNonContainEmptyString = dt.Select()
.Where(row => row["column1"] != null
&& row["column1"].ToString() == txtbox
&& !string.IsNullOrEmpty(row["column1"].ToString()))
bool exists = lignes.Count() != 0;
OR
bool exists = dt.Select()
.Any(row => row["column1"] != null
&& row["column1"].ToString() == txtbox
&& !string.IsNullOrEmpty(row["column1"].ToString()))
You would like to return false if the textbox is empty, so add the condition for the textbox.
bool exists = !string.IsNullOrWhiteSpace(txtbox)
&& dt.Select().ToList()
.Exists(row => row["column1"].ToString() == txtbox)
By the way, instead of using .Select().ToList(), you can add a reference to System.Data.DataTableExtensions and use the Extension AsEnumerable:
dt.AsEnumerable().Any( .....
How about this: non linq way
bool DataTableNonEmptyCount()
{
int count =0;
foreach (DataColumn col in row.Table.Columns)
if (!row.IsNull(col))
count ++;
return count;
}
This will return a count of all non null row in column.
Based on Thierry's answer, you can also use the Any(predicate) syntax:
var existsLineWithoutEmptyString =
dt.AsEnumerable()
.Any(row => row["column1"] != null
&& row["column1"].ToString() == txtbox
&& !string.IsNullOrEmpty(row["column1"].ToString()))
this code can help you
from a in list where ( Column1 != null || Column1 != "")
&& Column1 == searchfield
select a

Building where statement dyamically

I have a list where I wish to add items to dyanmically in the where clause if I dont have a user id at present the linq will fall over.
List<ScreenermissionsForSearch> _screen = new List<ScreenermissionsForSearch>();
_screen= _security.GetScreenermissionsForSearch();
gridControl1.DataSource = _screen.Where(w => w.Code ==
Convert.ToInt32(txtUserId.Text) || w.ScreenName ==dbscreenanme.Text).ToList();
this.gridView1.Columns[0].Width = 50;
this.gridView1.Columns[1].Width = 100;
So I need some wway of being able to append to the where clause checking if the string is null or not first or am I not doing this right in the frist place?.
Edit to show clairty Here it is just listing them all when i want it to only show provider if user id is empty.
It works here and shows fine as should do but its not for the other condition
New Code
_screen= _security.GetScreenermissionsForSearch();
gridControl1.DataSource = _screen.Where(w => string.IsNullOrEmpty(txtUserId.Text) || w.ScreenName == dbscreenanme.Text).ToList();
this.gridView1.Columns[0].Width = 50;
this.gridView1.Columns[1].Width = 100;
Add this condition to where clause string.IsNullOrEmpty(txtUserId.Text) and change the condition;
gridControl1.DataSource = _screen.Where(w =>
(string.IsNullOrEmpty(txtUserId.Text) || w.Code == Convert.ToInt32(txtUserId.Text)) &&
w.ScreenName == dbscreenanme.Text)).ToList();
If you don't want to get result when parsing is failed try following code;
gridControl1.DataSource = _screen.Where(w =>
w.Code == int.TryParse(txtUserId.Text,out var val) ? val : -1 &&
w.ScreenName == dbscreenanme.Text)).ToList();
var entity = Context.Parents.Include(x => x.Name).ToList();

Setting a datatable cell value using LINQ according to some conditions

I am trying to set a true boolean value to a cell in datatable according to some conditions using LINQ.
foreach (DataRow dr in dtLeftResult.Rows)
{
var shipNo = dr[0].ToString();
var invoiceNo = dr[1].ToString();
var res = dtInvoicesList.Rows
.Cast<DataRow>()
.Where(r => r.Field<string>("[Shipment#]") == shipNo && r.Field<string>("[Invoice#]") == invoiceNo)
.Select(r => r.Field<string>("IsValid")).First();
}
I need to set the IsValid field (boolean) to true after finding the correcet row in the datatable. I am finding the row but I am not able to set the value.
How can I do it?
In your res variable is the value of IsValid because of your call to Select(...)
What you want to do is get the row:
var row = dtInvoicesList.Rows
.Cast<DataRow>()
.Where(r => r.Field<string>("[Shipment#]") == shipNo
&& r.Field<string>("[Invoice#]") == invoiceNo)
.First();
And set its value: row["IsValid"] = true;
Select statement in your query returns the value of a cell, what you want is the DataRow of the DataTable.
Modify your query to get filtered rows and then access the DataRow columns using columnname or Index.
var firstMatch = dtInvoicesList.Rows
.AsEnumerable()Where(r => r.Field<string>("[Shipment#]") == shipNo && r.Field<string>("[Invoice#]") == invoiceNo)
.FirstOrDefault();
if(firstMatch!= null)
{
firstMatch["IsValid"] = true; //new value.
}

Retrieve the count of datarows where a column has a value

I have a DataTable with a column named ContainerTitle. I would like to get the count of the number of rows in the DataTable where the ContainerTitle has a particular value. For example, lets say the DataTable has 16 rows where ContainerTitle = "Widget1" and 10 rows where ContainerTitle = "Widget2". I would like to query to get the count of rows where ContainerTitle = "Widget1". Later, when done processing those rows, I would like to get the count of rows where ContainerTitle = "Widget2". I can't figure out how to get the count.
This is what I have come up with so far where the code is incrementing through the datarows:
for(int i=0;i<dt.Rows.Count;i++)
{
DataRow dr = dt.Rows[i];
szContainerName = dr["ContainerTitle"].ToString();
// here is where I am attempting to get the count
var tst = dt.AsEnumerable().Where(p => p.Field<string>("ContainerTitle") == szContainerName );
.
.
.
if (szContainerName != szPrevContainerName)
{
szPrevContainerName= szContainerName ;
}
}
Count would take the predicate, you can use it like:
var count = dt
.AsEnumerable()
.Count(p => p.Field<string>("ContainerTitle") == szContainerName );
You can also add Count to your where clause like:
var count = dt
.AsEnumerable()
.Where(p => p.Field<string>("ContainerTitle") == szContainerName );
.Count();

my sql returns only single value

I have
staff table
staff_Id
staff_Accesscode values (access123,code123,staff12)....
i have done like this for getting all values in staff_accesscode
DataTable dt2 = null;
string sql = #"SELECT staff_AccessCode FROM staff";
dt2 = xxxxxx.GetData(sql, gBaseUrl);
if (dt2 != null && dt2.Rows.Count > 0)
{
accessname = dt2.Rows[0]["staff_AccessCode"].ToString();
}
but the problem is it returns only last value stored in staff_Accesscode, I mean it returns only this value (staff12)
now i want to get the all values stored in staff_Accesscode field and further i want to check this value with the txtbox.text
You're only accessing the first row in the line accessname = dt2.Rows[0]...; you need to loop through all of the available rows in the collection.
You are accessing the first row (with index 0) only.
You might iterate through the complete result set:
for ( int i = 0; dt2 != null && i < dt2.Rows.Count; ++i )
{
String tmp = dt2.Rows[ i ]["staff_AccessCode"].ToString();
if ( tmp.Equals( what_ever_variable_or_constant /* e.g., txtbox.Text */ ) )
{
accessname = tmp;
//break; ?
}
}

Categories