Performance differences in c# - c#

I have below code. Sometime it takes a bit longer time to get this code executed.
Need to improve the code.
//Get products from DB
DataTable dtProducts = GetAllProducts();
if(dtProducts !=null && dtProducts.Rows.Count >0)
{
dtProducts .DefaultView.RowFilter = null;
dtProducts .DefaultView.RowFilter = " product_id = '" +product_id.Trim() + "'";
DataTable dtProductCount = dtProducts .DefaultView.ToTable();
if (dtProductCount != null && dtProductCount.Rows.Count > 0)
{
object obj = dtProductCount.Compute("SUM(qty)", "");
if (!Convert.IsDBNull(obj))
{
int qtyProd = Convert.ToInt32(obj);
}
}
}
Is there any scope of performance improvement here ?
Like instead of Count, can we use Any(), in data table.

One definite improvement would be including the filter condition product_id = '" +product_id.Trim() + "' in SQL query itself in WHERE clause thus returning you only the required dataset instead of everything
select * from products
where product_id = #product_id
Per your comment Because this code run inside a for each loop from where I got product Id ... well then from your for loop collect all the product_id and create a inlist like 1,2,34,..... then use that solely in your query using a IN operator.

The database itself is specialized in data aggregation, so you could deliver that job for it:
public DataTable GetProductQuantitySummary(int? productId)
{
using (var cn = new SqlConnection("...."))
using (var cm = new SqlCommand ("", cn))
{
cn.Open();
cm.CommandText = #"
SELECT product_id, SUM(qty) SumQty
FROM Products
WHERE product_id = #product_id OR #product_id IS NULL
GROUP BY product_id";
cm.Parameters.AddWithValue("#product_id", (object) productId ?? DBNull.Value);
var table = new DataTable();
using (var reader = cm.ExecuteReader())
{
table.Load(reader);
}
return table;
}
}
This way, you'll only return the required data.
If you prefer to call this method always passing the productId, you don't need to return the DataTable at all, and you could return the result:
public int GetProductQuantitySummary(int productId)
{
using (var cn = new SqlConnection("..."))
using (var cm = new SqlCommand("", cn))
{
cn.Open();
cm.CommandText = #"
SELECT SUM(qty) SumQty
FROM Products
WHERE product_id = #product_id";
cm.Parameters.AddWithValue("#product_id", productId);
return cm.ExecuteScalar() as int? ?? 0;
}
}

Related

Replacing if (Convert.ToString(rdr["Data"]) != bItems)

I want to replace if (Convert.ToString(rdr["Data"]) != bItems) with something that would check if data already exist in my database or not to make process faster as going in that loop taking too much time for bigger database. Plz HELP!
for (int p = 0; p < 256; p++) {
bItems += "P" + buffer[p];
}
using (SQLiteConnection con = new SQLiteConnection(databaseObject.myConnection)) {
con.Open();
SQLiteCommand cmd = new SQLiteCommand("select ID, Data from B where Data like 'P%'", con);
var rdr = cmd.ExecuteReader();
while (rdr.Read()) {
if (Convert.ToString(rdr["Data"]) != bItems) {
SQLiteCommand cmd1 = new SQLiteCommand("INSERT INTO B ('Data') SELECT #Data WHERE NOT EXISTS (SELECT ID, Data FROM B WHERE Data = #Data)", con);
cmd1.Parameters.AddWithValue("#Data", bItems);
cmd1.ExecuteNonQuery();
}
else (Convert.ToString(rdr["Data"]) == bItems) {
sItems = "B" + Convert.ToString(rdr["ID"]);
rdr.Close();
break;
}
}
}
bItems = "";
Console.WriteLine(sItems);
}
instead of reading each row and check the data against bItems. You may need to either query the table to see if there is any record matches bItems, if not then insert it. Or, you can simply insert the data if not exists (which what you did in the first condition.
To simplify your work, you can do this :
// insert the new item if not exists in the table
// returns the item Id
private int InsertDataIfNotExists(string bItems)
{
using (SQLiteConnection connection = new SQLiteConnection(databaseObject.myConnection))
using(SQLiteCommand command = new SQLiteCommand("INSERT INTO B ('Data') SELECT #Data WHERE NOT EXISTS (SELECT 1 FROM B WHERE Data = #Data);", con))
{
connection.Open();
command.Parameters.AddWithValue("#Data", bItems);
// insert data if not exists
command.ExecuteNonQuery();
// get the data's Id
cmd.CommandText = "SELECT ID FROM B WHERE Data = #Data LIMIT 1;";
cmd.Parameters.AddWithValue("#Data", bItems);
var result = cmd.ExecuteScalar()?.ToString();
return int.TryParse(result, out int id) && id > 0 ? id : -1;
}
}
with the above, you only insert the data if not exists, and then return the id.
usage :
var insertResultId = InsertDataIfNotExists(bItems);
if(insertResultId == -1)
{
// handle exceptions
}
else
{
Console.WriteLine(insertResultId);
}

How to modify specific cell value mysql table in c#?

I have table
itemID
storeID
qty
103
LAB
20
I want to add qantity of specific item for example:'103' stored in warehouse 'LAB'.
public void addQuantity(string store, string item, int qty)
{
con.Open();
string sql = "SELECT qty,warehouse.storeID,item.itemID FROM Item,warehouse,stocker WHERE stocker.storeID=warehouse.storeID AND stocker.itemID=item.itemID AND warehouse.storeID='"+store+"' AND Item.itemID='"+item+"' ";
using (MySqlDataAdapter adapter = new MySqlDataAdapter(sql, con))
{
using (DataTable tempTable = new DataTable())
{
adapter.Fill(tempTable);
if (tempTable.Rows.Count == 0) throw new Exception("No such product");
foreach (DataRow r in tempTable.Rows)
{
int newQty = (int)r["qty"] + qty;
if (newQty > 0)
{
r["qty"] = newQty;
qty = 0;
break;
}
else
{
MessageBox.Show("error");
}
}
using (MySqlCommandBuilder cb = new MySqlCommandBuilder(adapter))
{
adapter.UpdateCommand = cb.GetUpdateCommand();// there is error
adapter.Update(tempTable);
}
}
}
con.Close();
}
it says:"Dynamic SQL generation is not supported for multiple base tables".
what would you advice me?
If qty is integer column, you may try to:
Increment its current value by some value:
using (var updateCommand = new MySqlCommand())
{
updateCommand.CommandText = "UPDATE mytable t SET t.qty = t.qty + #newQty WHERE *...Your WHERE clause...*`"
updateCommand.Parameters.AddWithValue("#newQty", newQtyValue);`
// ...
}
Or append entire new value:
using (var updateCommand = new MySqlCommand())
{
updateCommand.CommandText = "UPDATE mytable t SET t.qty = #newQtyValue WHERE *...Your WHERE clause...*`"
updateCommand.Parameters.AddWithValue("#newQtyValue", newQtyValue);`
// ...
}
As #sticky bit said, it is preferred to use Command.Parameters.AddWithValue instead of string concat/interpolation.

LINQ query not showing results - LEFT join with WHERE clause

This is my first foray into LINQ.
I still have to wrap my head around the results part, but I can't seem to get any results from this.
var institutions = from lots in lotsdb.NEWinstitution
join webs in webbitdb.tblinstitution
on lots.institutionid equals webs.dispenseinstid into newinsts
from webs2 in newinsts.DefaultIfEmpty()
where webs2 == null
select new
{
instid = lots.institutionid,
instname = lots.institutionname
};
foreach(var instfound in institutions)
{
MessageBox.Show(instfound.instid.ToString() + " " + instfound.instname.ToString());
}
I'm using Datasets created by Visual Studio in the DATASources list.
Below is my original SQL string that i have "tried" to adapt to LINQ
string strgetloc = #"
SELECT NEWinstitution.institutionid, NEWinstitution.institutionname
FROM NEWinstitution
LEFT JOIN tblinstitution
ON NEWinstitution.institutionid = tblinstitution.dispenseinstid
WHERE (((tblinstitution.institutionid) Is Null));"
You probably need something like this:
var institutions =
from lots in lotsdb.NEWinstitution
join webs in webbitdb.tblinstitution on lots.institutionid equals webs.dispenseinstid
where webs.IsInstitutionIdNull()
select new
{
instid = lots.institutionid,
instname = lots.institutionname
};
The IsInstitutionIdNull() method is generated by the MSDataSetGenerator when the columns allows DBNull. Because you cannot compare it directly to DBNull or to null.
(fixed a typo)
So I ended up using the following code:
var idsNotInB = dtLotsInst.AsEnumerable().Select(r => r.Field<int>("institutionid"))
.Except(dtWebbitInst.AsEnumerable().Select(r => r.Field<int>("institutionid")));
count = idsNotInB.Count();
if (count != 0)
{
DataTable dtOnlyLots = (from row in dtLotsInst.AsEnumerable()
join id in idsNotInB
on row.Field<int>("institutionid") equals id
select row).CopyToDataTable();
using (OleDbConnection con = new OleDbConnection(PackChecker.Properties.Settings.Default["WebbitConnectionString"].ToString()))
{
string strgetloc = #"INSERT INTO tblinstitution ( dispenseinstid, institutionname ) VALUES (?,?)";
using (OleDbCommand cmd = new OleDbCommand(strgetloc, con))
{
con.Open();
foreach (DataRow dr in dtOnlyLots.Rows)
{
cmd.Parameters.Add("?", OleDbType.Integer).Value = Convert.ToInt32(dr["institutionid"]);
cmd.Parameters.Add("?", OleDbType.VarWChar).Value = dr["institutionname"].ToString();
cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
}
con.Close();
}
}
}
This uses LINQ and works well by using "EXCEPT" in the first LINQ section to find values not in one table. Then it uses this list to generate the rows from the table I want.

How can send every 500 employeed id to Oracle to resolve ORA-07195

I already have this code to make stringBuilder for every employee, I get all the employeesId from another table . But if I get more than 1000 employees, I get the error ORA-07195 , I know this error is related to a Maximum of expression in a list . Therefore how can I send every 500 employees to my query in Data Access Objects.
Public List<GraphModel> countRequestCreatedByTypeDefaulPage(int year, int month, String employeeID)
{
int count = 0;
int countEmployeess = 0;
string employeesid = "";
DataView dv = _employeeOverrideBO.getRelatedEmployees(year, month, employeeID);
StringBuilder listEmployees = new StringBuilder();
for (int i = 0; i < countEmployees; i += 500)
{
foreach (DataRowView rowView in dv)
{
DataRow row = rowView.Row;
String employee = row["EMPLOYEE_ID"].ToString();
if (count > 0)
listEmployees.Append(",");
listEmployees.Append("'").Append(employee).Append("'");
count++;
}
}
countEmployeess++;
employeesid = listEmployees.ToString();
return _requestDAO.countRequestCreatedByTypeDefaulPage(employeesid);
Also this is my query in Data Access Object
public List<GraphModel> countRequestCreatedByTypeDefaulPage(string employeesIds)
{
String sql = " select NVL(TO_CHAR(RR.REASON_NM_NEW), 'Total') as SERIES1, count(*) AS VAL" +
" from REQUEST R, REQUEST_PERSON RP, REQUEST_REASON RR " +
" WHERE R.STATUS IN ('CREATED', 'PENDING APPROVAL', 'APPROVED BY MANAGER', 'APPROVED', 'IN PROCESS') " +
" AND R.REQUEST_ID = RP.REQUEST_ID" +
" AND RP.REQUEST_ROLE = 'REQUESTOR' " +
" AND RR.REASON_ID = R.REASON_ID" +
" AND RP.EMPLOYEE_ID IN (" + employeesIds + ") " +
" group by rollup (RR.REASON_NM_NEW) " +
" ORDER BY count(*) DESC";
OracleCommand cmd = new OracleCommand(sql);
try
{
DataTable dataTable = Data_base_Access.executeSQL(cmd, ConfigurationManager.ConnectionStrings["stage"].ToString());
return (GraphModel.convertToList(dataTable));
}
catch (Exception ex)
{
Log.writeError("Request DAO", ex);
throw new DataAccessException("There was an error counting the open requests");
}
}
Also this query get the count to list called GraphModel
public static List<GraphModel> convertToList(System.Data.DataTable dataTable)
{
List<GraphModel> list = new List<GraphModel>();
foreach (DataRow dtRow in dataTable.Rows)
{
list.Add(convertToGraphModel(dtRow));
}
return list;
}
public static GraphModel convertToGraphModel(DataRow dtRow)
{
GraphModel graphModel = new GraphModel();
if (dtRow.Table.Columns.Contains("SERIES1") && dtRow["SERIES1"] != DBNull.Value)
{
graphModel.SERIES1 = Convert.ToString(dtRow["SERIES1"]);
}
if (dtRow.Table.Columns.Contains("SERIES2") && dtRow["SERIES2"] != DBNull.Value)
{
graphModel.SERIES2 = Convert.ToString(dtRow["SERIES2"]);
}
if (dtRow.Table.Columns.Contains("VAL") && dtRow["VAL"] != DBNull.Value)
{
graphModel.VAL = Convert.ToInt32(dtRow["VAL"]);
}
return graphModel;
}
}
I really appreciate your help because I am research a lot and I dont know what can I do
Split the list into 1000 item lists and change the query into this:
" AND (RP.EMPLOYEE_ID IN (" + ids_1_1000 + ") OR RP.EMPLOYEE_ID IN (" + ids_1001_2000 + "))" +
One of the features I love most about Oracle is the Oracle Call Interface (OCI) that lets you access some of the more powerful features or Oracle with programming languages. In particular, for this example, the ability to do bulk inserts should prove very helpful.
If, instead of the approach you have above, which is trying to insert thousands of literals into a single SQL statement, you put those values into a table and do a join, I think you will:
Spare the shared pool from having to compile a really nasty SQL statement
Eliminate the need to escape strings or worry about SQL Injection
Have a lightning fast query that substitutes a join (bread and butter for a database) for a giant in-list
Step 1: Create a GTT:
create global temporary table employee_list (
employee_id varchar2(100) not null
) on commit preserve rows;
The GTT is based on a session, so even if you have this code running in multiple instances, each GTT will act as a blank slate for each instance -- no possibility of collisions of data.
Step 2: Within your code, create a transaction to handle the fact that you need the insert to the table and the select on the data to occur as part of the same transaction:
OracleTransaction trans = conn.BeginTransaction(IsolationLevel.ReadCommitted);
Step 3: Use ODP.net's bulk insert capabilities to insert all of your employee Ids at once. I encourage you to benchmark this versus inserting them one at a time. You'll be amazed. If you have more than 50,000, then maybe you need to break the up into chunks, but with a single field, I think this should be more than adequate:
// string[] employeesIds
OracleCommand cmd = new OracleCommand("insert into employee_list values (:EMPLOYEE)",
conn);
cmd.Transaction = trans;
cmd.Parameters.Add(new OracleParameter("EMPLOYEE", OracleDbType.Varchar2));
cmd.Parameters[0].Value = employeesIds;
cmd.ArrayBindCount = employeesIds.Length;
cmd.ExecuteNonQuery();
Note employeeIds should be an array.
Step 4: Change your SQL from an in-list to a join:
select NVL(TO_CHAR(RR.REASON_NM_NEW), 'Total') as SERIES1, count(*) AS VAL
from
REQUEST R,
REQUEST_PERSON RP,
REQUEST_REASON RR,
employee_list e -- added this
WHERE R.STATUS IN ('CREATED', 'PENDING APPROVAL', 'APPROVED BY MANAGER',
'APPROVED', 'IN PROCESS')
AND R.REQUEST_ID = RP.REQUEST_ID
AND RP.REQUEST_ROLE = 'REQUESTOR'
AND RR.REASON_ID = R.REASON_ID
AND RP.EMPLOYEE_ID = e.employee_id -- changed this
group by rollup (RR.REASON_NM_NEW)
ORDER BY count(*) DESC
And here's what it would all look like together:
public List<GraphModel> countRequestCreatedByTypeDefaulPage(string[] employeesIds)
{
OracleTransaction trans = conn.BeginTransaction(IsolationLevel.ReadCommitted);
OracleCommand cmd = new OracleCommand("insert into employee_list values (:EMPLOYEE)",
conn);
cmd.Transaction = trans;
cmd.Parameters.Add(new OracleParameter("EMPLOYEE", OracleDbType.Varchar2));
cmd.Parameters[0].Value = employeesIds;
cmd.ArrayBindCount = employeesIds.Length;
cmd.ExecuteNonQuery();
String sql = ""; // code from above goes here
cmd = new OracleCommand(sql, conn);
cmd.Transaction = trans;
DataTable dataTable = null;
try
{
dataTable = Data_base_Access.executeSQL(cmd,
ConfigurationManager.ConnectionStrings["stage"].ToString());
return (GraphModel.convertToList(dataTable));
}
catch (Exception ex)
{
Log.writeError("Request DAO", ex);
throw new DataAccessException("There was an error counting the open requests");
}
finally
{
trans.Rollback();
}
return dataTable;
}
I resolved my problem with this code..
public List<GraphModel> countRequestCreatedByTypeDefaulPage(int year, int month, String employeeID)
{
int count = 0;
int countEmployees = 0;
Dictionary<string, int> dataChart = new Dictionary<string, int>();
DataView dv = _employeeOverrideBO.getRelatedEmployeesRequests(year, month, employeeID);
StringBuilder listEmployees = new StringBuilder();
foreach (DataRowView rowView in dv)
{
if (countEmployees == 500)
{
List<GraphModel> listReturn = _requestDAO.countRequestCreatedByTypeDefaulPage(listEmployees.ToString());
foreach(GraphModel model in listReturn){
if (dataChart.ContainsKey(model.SERIES1))
{
dataChart[model.SERIES1] = dataChart[model.SERIES1] + model.VAL;
}
else
{
dataChart[model.SERIES1] = model.VAL;
}
}
listEmployees = new StringBuilder();
count = 0;
countEmployees = 0;
}
DataRow row = rowView.Row;
String employee = row["EMPLOYEE_ID"].ToString();
if (count > 0)
listEmployees.Append(",");
listEmployees.Append("'").Append(employee).Append("'");
count++;
countEmployees++;
}
//Last Call
List<GraphModel> listReturnLast = _requestDAO.countRequestCreatedByTypeDefaulPage(listEmployees.ToString());
foreach (GraphModel model in listReturnLast) {
if (dataChart.ContainsKey(model.SERIES1))
{
dataChart[model.SERIES1] = dataChart[model.SERIES1] + model.VAL;
}
else
{
dataChart[model.SERIES1] = model.VAL;
}
}
List<GraphModel> list = new List<GraphModel>();
foreach (KeyValuePair<string, int> entry in dataChart)
{
GraphModel model = new GraphModel();
model.SERIES1 = entry.Key;
model.VAL = entry.Value;
list.Add(model);
}
return list;
}

Inserting multiple rows into MS SQL Server and retrieve all the new table ID's back

Looking at the example given here:
https://stackoverflow.com/a/452934
I understand that I will need to iterate through a loop and append value clauses, but what I am missing is how to amend the query to return all the ID's for the newly created records and retrieve them in C#?
For Example my current code can be seen below, I would like to change it to insert multiple rows in one query and retrieve the newly created Id's as a List of integers ideally.
in_new_id = -1;
String query = "INSERT INTO " + DB_Base.DBTable_Customer_Order_Table + "(" + DB_Base.DBTable_Customer_Order_Table_Customer_ID + "," + DB_Base.DBTable_Customer_Order_Table_ProductId+")";
query += " OUTPUT INSERTED." + DB_Base.DBTable_Customer_Order_Table_ID;
query += " VALUES ( #customerId, #productId);";
using (SqlConnection conn = new SqlConnection(GeneralConfig.DB_STR()))
{
SqlCommand sql_command = new SqlCommand(query, conn);
sql_command.Parameters.AddWithValue("#customerId", data_obj.customerId);
sql_command.Parameters.AddWithValue("#productId", data_obj.productId);
if (!String.IsNullOrEmpty(query) && sql_command != null && conn != null)
{
sql_command.Connection.Open();
if (sql_command.Connection.State == System.Data.ConnectionState.Open)
{
object out_new_id = sql_command.ExecuteScalar();
if (out_new_id != null)
{
in_new_id = (int)out_new_id;
}
sql_command.Connection.Close();
return ENUM_DB_Status.DB_SUCCESS;
}
else
{
in_new_id = -1;
return ENUM_DB_Status.DB_CONNECTION_COULD_NOT_OPEN;
}
}
}
return ENUM_DB_Status.DB_FAIL;
Use this:
List<int> ids = new List<int>();
using (SqlCommand command = new SqlCommand(#"declare #T TABLE(Id int)
INSERT INTO YourTableName(YourTableColumnNames)
OUTPUT Inserted.Id into #T VALUES
(YourValues1),
(YourValues2),
(YourValues3),
(etc...) select Id from #T ", con))
{
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
int id = int.Parse(reader[0].ToString());
ids.Add(id);
}
}
}
Warning!!! This will work only if you're using SQLServer 2008 R2 or higher.
Edit: As Damien said in the comments : "There is no guarantee that the order in which the changes are applied to the table and the order in which the rows are inserted into the output table or table variable will correspond."

Categories

Resources