Unable to get sum of column in datatable - c#

I'm trying to write a method that returns me the sum of a column from entries between two dates. However it keeps returning a DBnull no matter what. I have other similar methods that work when the queries are simpler (no SUM, simple select * where statement). performQuery() is just helper method that returns a filled datatable with the query results.
public static int getBookedQuantity(int shopID, int bikeID, DateTime datetime)
{
string query = "SELECT sum(quantity) as \"quantity\" FROM booking WHERE bikeID=#bikeID AND shopID = #shopID AND starttime >= #Datetime AND endtime < #Datetime";
SqlParameter param1 = new SqlParameter("#bikeID", bikeID);
SqlParameter param2 = new SqlParameter("#shopID", shopID);
SqlParameter param3 = new SqlParameter("#Datetime", SqlDbType.DateTime);
param3.Value = datetime;
DataTable bookingData = performQuery(query, param1, param2, param3);
DataRow[] row = bookingData.Select();
int totalBooked = 0;
if ((row.Count()) > 0 && (bookingData != null)) // if there are rows returned
totalBooked = Convert.ToInt32(row[0]["quantity"]);
return totalBooked;
}
Thank you in advance!

Try modifying query to
string query = "SELECT sum(quantity) as 'quantity' FROM booking WHERE bikeID=#bikeID AND shopID = #shopID AND starttime >= #Datetime AND endtime < #Datetime";

DataTable is not a correct way to get the sum. To fix your problem you have to do this:
var sum = Convert.ToInt32(bookingData.Rows[0]["quantity"]);
return sum;
But, the correct way to execute this command, is this:
var statement = "SELECT COUNT(*) ...";
var command = new SqlCommand(statement, connection);
command.AddParam(param1);
command.AddParam(param2);
command.AddParam(param3);
var sum = Convert.ToInt32(command.ExecuteScalar());
return sum;
Please, refer to MSDN doc about scalar: https://msdn.microsoft.com/it-it/library/system.data.sqlclient.sqlcommand.executescalar(v=vs.110).aspx
Hope this can help.

Related

format string with int64 value

i have a web service in which training number is assigning as per my database
there is a line
dates + "1"; this lines add 1 in date
like 27012017 will become 270120171
then i convert this into int64
newid = Convert.ToInt64(dates);
but now i want to add trainer id in this
so i updated my line with this
dates ="00"+trainerid +"-"+ dates + "1";
newid = Convert.ToInt64(dates);
the error is coming input string is not in correct format,
i know this is because of the addition of +"-"+
but i want to store the data in this format only
and my whole portion look like
DateTime dt = DateTime.Now;
string dates = dt.ToString();
dates = dates.Replace("-", "");
dates = dates.Substring(0, 8);
SqlCommand cmdbill = new SqlCommand("select top 1 * from listmybill where bill_id like #trid order by bill_id desc", con);
con.Open();
cmdbill.Parameters.AddWithValue("#trid", "%" + dates + "%");
SqlDataReader dr = cmdbill.ExecuteReader();
while (dr.Read())
{
value = dr["bill_id"].ToString();
}
con.Close();
con.Open();
SqlDataAdapter da = new SqlDataAdapter(cmdbill);
DataTable dat = new DataTable();
da.Fill(dat);
if (dat.Rows.Count > 0)
{
newid = Convert.ToInt64(value);
newid = newid + 1;
}
else
{
dates ="00"+trainerid +"-"+ dates + "1";
newid = Convert.ToInt64(dates);
}
con.Close();
what should i do here,
i want to enter the data like 001-270120171
and if i convert toInt64 into string,
there can be problem when a row found in the table
if (dat.Rows.Count > 0)
{
newid = Convert.ToInt64(value);
newid = newid + 1;
}
what i need to do now?
i want to store this into my database
I hardly think you will be doing mathematical calculations on this training number string. I suggest that you convert the int in your database into a varchar using alter statement. Then you you will be able to store the training number in whichever format you like.
You can change new id to string and do the following
if (dat.Rows.Count > 0)
{
newid = Convert.ToString(Convert.ToInt64(value)+1);
}
else
{
newid="00"+trainerid +"-"+ dates + "1";
}

Rest API SQL query times out from too many rows, how to limit reader from reading too many rows

I setup this C# query with a simple form of pagination, but some of the queries it performs may have millions of rows. I was recently testing it with various parameters, and some of the queries were timing out when there were more than 200,000 or so records. How can I go about limiting the reader to read say ~50,000 rows each time?
public DataTable GetTranReport(string aliasName, string pageString, string year, string month)
{
DataTable dataTable = new DataTable();
dataTable.Columns.Add(new DataColumn("recid"));
dataTable.Columns.Add(new DataColumn("folder"));
dataTable.Columns.Add(new DataColumn("cust"));
dataTable.Columns.Add(new DataColumn("direction"));
//pagination variables (pageString must be 1+ in order to represent current page)
int pageInt;
Int32.TryParse(pageString, out pageInt);
if (dbConnection5.State.ToString() != "Open")
{
dbConnection5.Open();
}
int itemNum = 0;
string selecteddate = string.Format("[" + year + month + "]");
string query = string.Format("SELECT recid, folder, cust, direction FROM " + selecteddate + " WHERE cust = #aliasname order by thedate DESC;");
SqlCommand command = new SqlCommand(query, dbConnection5);
command.Parameters.AddWithValue("#selecteddate", selecteddate);
command.Parameters.AddWithValue("#aliasname", aliasname);
SqlDataReader reader = command.ExecuteReader();
int i = 0;
DataRow newTRRow;
if (reader.HasRows)
{
while (reader.Read())
{
++i;
if (pageInt > 1)
{
if (i >= ((pageInt * 10) - 10) && i < (10 * pageInt))
{
itemNum += 1;
string itemString = string.Format("itemString" + itemNum);
newTRRow = dataTable.NewRow();
newTRRow["recid"] = reader["recid"];
newTRRow["folder"] = reader["folder"];
newTRRow["customer"] = reader["customer"];
newTRRow["direction"] = reader["direction"];
dataTable.Rows.Add(newTRRow);
}
}
else
{
if (itemNum < 10)
{
itemNum += 1;
string itemString = string.Format("itemString" + itemNum);
newTRRow = dataTable.NewRow();
newTRRow["recid"] = reader["recid"];
newTRRow["folder"] = reader["folder"];
newTRRow["customer"] = reader["customer"];
newTRRow["direction"] = reader["direction"];
dataTable.Rows.Add(newTRRow);
}
}
}
}
dataTable.Rows.Add(string.Format("Total number of records is: {0}", i));
reader.Close();
dbConnection5.Close();
return dataTable;
}
you need to add 2 extra parameter (the position of records you want) and use a CTE which has a row_number:
assuming you want records from rcA to rcB, you can do:
with cte as(
SELECT recid, folder, cust, direction,
row_number() over(order by recid) rn
FROM your_table
where --your conditions (it's not need to use order by here)
)
select * from cte
where rn between rcA and rcB
You can limit your no. of rows at the SQL query level itself. Just use the TOP keyword to limit it.
string query = string.Format("SELECT TOP 50000 recid, folder, cust, direction FROM " + selecteddate + " WHERE cust = #aliasname order by thedate DESC;");

sending null value with parametrized queries

public static DataTable GetBatches(long storeID, long? ProfileID)
{
string query =
"SELECT .... where " + (ProfileID.HasValue ? "PROFILE_ID=" + ProfileID.Value : "STORE_ID=" + storeID);
i want to change that query to accept parametrized values like this :
List<SqlParameter> params_list = new List<SqlParameter>();
SqlParameter param_ProfileID = new SqlParameter("#PROFILE_ID", ProfileID);
param_StoreID.SourceColumn = "PROFILE_ID";
param_StoreID.DbType = DbType.Int64;
params_list.Add(param_ProfileID);
but what if Profile_id is null ? how can i do it
Basically, you use DBNull.Value:
SqlParameter param_ProfileID = new SqlParameter("ProfileID",
(object)ProfileID ?? DBNull.Value);
And use #PROFILE_ID in the TSQL, for example:
where table.ProfileID = #ProfileID
Alternatively, use a helper tool like dapper, and forget about it:
return connection.Query<SomeType>(
"select * from SomeTable where ProfileID = #ProfileID",
new { ProfileID }).ToList();
(which returns a List<SomeType>, not a DataTable)
Re conditional searching, there are various approaches here; one is the sub-optimal:
where (#Foo is null or table.Foo = #Foo)
and (#Bar is null or table.Bar = #Bar)
which matches #Foo when it is provided, and #Bar when it is provided - but... it isn't great at hitting indexes, especially if you add more clauses. In your case, I would be tempted to do:
var sql = ProfileID == null
? "select * from Blah where StoreID = #StoreID"
: "select * from Blah where StoreID = #StoreID and ProfileID = #ProfileID";
which uses optimal TSQL for the 2 scenarios. It doesn't matter if you provide unused parameters on the command, but you could also do:
cmd.Parameters.AddWithValue("StoreID", StoreID);
if(ProfileID != null) cmd.Parameters.AddWithValue("ProfileID", ProfileID);
Based upon your comments, the query should look like this:
SELECT * FFROM tbl WHERE (#Profile_Id = NULL AND Store_Id = #Store_Id)
OR (#Profile_Id <> NULL AND Profile_Id = #Profile_Id)
You can set up the parameters like this:
List<SqlParameter> params_list = new List<SqlParameter>();
SqlParameter param_ProfileID = new SqlParameter("#PROFILE_ID", (object)ProfileID ?? DBNull.Value);
param_StoreID.SourceColumn = "PROFILE_ID";
params_list.Add(param_ProfileID);
SqlParameter param_StoreID = new SqlParameter("#STORE_ID", StoreId);
param_StoreID.SourceColumn = "STORE_ID";
params_list.Add(param_StoreID);
you can use this work around
List<SqlParameter> params_list = new List<SqlParameter>();
if(ProfileID==null)
{
SqlParameter param_ProfileID = new SqlParameter("#PROFILE_ID", DBNull.Value);
}
else
{
SqlParameter param_ProfileID = new SqlParameter("#PROFILE_ID", ProfileID);
}
param_StoreID.SourceColumn = "PROFILE_ID";
param_StoreID.DbType = DbType.Int64;
params_list.Add(param_ProfileID);
Looking at your first example, it seems clear that you don't need to build a parameter if your profileID variable is null, simply you write a different query
public static DataTable GetBatches(long storeID, long? ProfileID)
{
List<SqlParameter> pList = new List<SqlParameter>();
string query = "SELECT .... where ";
if(ProfileID.HasValue)
{
query += "PROFILE_ID= #proID";
SqlParameter pProfileID = new SqlParameter("#proID", SqlDbType.BigInt)
.Value = ProfileID.Value;
pList.Add(pProfileID);
}
else
{
query += "STORE_ID= #stoID";
SqlParameter pStoreID = new SqlParameter("#stoID", SqlDbType.BigInt)
.Value = storeID;
pList.Add(pStoreID);
}
If ProfileID is not null you search for it otherwise you search for StoreID and that's all.

C#: count and get Id of rows from sql

what's wrong pls?
I can get number of records but can't get id value of each record
I have this function to count and get id values:
I think the issue is in the function
form1
public List<int> countInTable(Double codebar)
{
cmd.Connection = cn; // sqlconn
cn.Open();
List<int> id = new List<int>();
String countString = "SELECT COUNT (*) FROM medicaments WHERE code_bare = #codebar ";
var myCommand = new SqlCommand(countString, cn);
myCommand.Parameters.AddWithValue("#codebar", codebar);
int count = Convert.ToInt32(myCommand.ExecuteScalar());
id.Add(count);
using (rd = myCommand.ExecuteReader())
{
// loop over all rows returned by SqlDataReader
while (rd.Read())
{
// grab the column no. 0 (id" from
// SQL select statement) as a string then int, and store it into list of id
t0 = rd[0].ToString();
id.Add(int.Parse(t0));
}
}
rd.Close();
cn.Close();
return id;
}
Then i call the first function in another form to get data of each record
form2
private void laodProduct(Double codebar)
{
this.listView2.Items.Clear();
List<int> total = manipBaseDonnees.countInTable(double.Parse(textBox2.Text));
foreach (int t in total)
{
listBox1.Items.Add(t);
List<string> donnees = manipBaseDonnees.getFromTableID(t);
var item1 = new ListViewItem(new[] { donnees[1], donnees[2], donnees[6], donnees[7], donnees[8], donnees[4], donnees[0] }, -1, Color.Empty, Color.Yellow, null);
this.listView2.Items.AddRange(new ListViewItem[] { item1 });
}
}
Your query is a count query, you cannot use the same query to get each entry. You need to use a second query for that:
string query = "SELECT * FROM medicaments WHERE code_bare = #codebar";
If you only want the ID field, then only include that in your query:
string query = "SELECT ID FROM medicaments WHERE code_bare = #codebar";
In addition, if you are just going to iterate over all the results anyway, you might as well just drop he count query and count the results of ExecuteReader as you loop over them.
The problem is not with your function but with the way you select data, you use aggregate function COUNT(*) and this returns only row count if you add MAX(ID) this will do same think as if you would've grouped your data by ID what is wrong with is is that in that case you will get two columns one will be ID value and the second one would've been COUNT but it would be the same for all the ID's
String countString = "SELECT MAX(ID), COUNT (*) FROM medicaments WHERE code_bare = #codebar ";
oh yes Thanks, I copied the string from my first function without giving attention
string query = "SELECT * FROM medicaments WHERE code_bare = #codebar";

problem with getting data from database

I am trying to get the data from database by using the below code.....
if there is no data in the table it will always goes to
this statement
I am using mysql.net connector for getting the data and i am doing winforms applications
using c#
public DataTable sales(DateTime startdate, DateTime enddate)
{
const string sql = #"SELECT memberAccTran_Source as Category, sum(memberAccTran_Value) as Value
FROM memberacctrans
WHERE memberAccTran_DateTime BETWEEN #startdate AND #enddate
GROUP BY memberAccTran_Source";
return sqlexecution(startdate, enddate, sql);
}
and the below code is for return sqlexceution...function..
private static DataTable sqlexecution(DateTime startdate, DateTime enddate, string sql)
{
var table = new DataTable();
using (var conn = new MySql.Data.MySqlClient.MySqlConnection(connectionstring))
{
conn.Open();
var cmd = new MySql.Data.MySqlClient.MySqlCommand(sql, conn);
var ds = new DataSet();
var parameter = new MySql.Data.MySqlClient.MySqlParameter("#startdate", MySql.Data.MySqlClient.MySqlDbType.DateTime);
parameter.Direction = ParameterDirection.Input;
parameter.Value = startdate.ToString(dateformat);
cmd.Parameters.Add(parameter);
var parameter2 = new MySql.Data.MySqlClient.MySqlParameter("#enddate", MySql.Data.MySqlClient.MySqlDbType.DateTime);
parameter2.Direction = ParameterDirection.Input;
parameter2.Value = enddate.ToString(dateformat);
cmd.Parameters.Add(parameter2);
var da = new MySql.Data.MySqlClient.MySqlDataAdapter(cmd);
da.Fill(ds);
try
{
table = ds.Tables[0];
}
catch
{
table = null;
}
}
return table;
}
even if there is no data the process flow will goes to this line
table = ds.Tables[0];
how can i reduce this .....
would any one pls help on this....
In your case if you are think that catch block will get excuted if there is no row available than you are wrong because Even if there is no data once select query is get exucuted without exception it Creates datatable with the columns but with no rows.
for this i think you can make use of ds.table[0].rows.count property which return 0 if there is no row in datatable.
if ( ds.Tables[0].Rows.Count > 0 )
table = ds.Tables[0];
else
table=null;
It returns an empty table. This is common behavior. If you want to have table null you should check for the row count :
If ( ds.Tables[0].Rows.Count >. 0 )
table = ds.Tables[0];
Else
table=0
I'm not really sure what you're asking here ... I assume you want it to skip the table = ds.tables[0] line if there is no data?
if thats the case a try/catch wont work as it wont throw an exception ... try something like this instead ...
if(ds.Tables.Count > 0 && ds.Tables[0].Rows.Count >0)
{
table = ds.Tables[0];
}
else
{
table = null;
}

Categories