I have the following code . I can extract all data from all data existing in sheets in a workbook and the name of the sheet
foreach (var sheetName in GetExcelSheetNames(connectionString))
{
using (OleDbConnection con = new OleDbConnection(connectionString))
{
var dataTable = new DataTable();
string query = string.Format("SELECT {0} as sheetName, * FROM [{0}]", sheetName);
con.Open();
OleDbDataAdapter adapter = new OleDbDataAdapter(query, con);
adapter.Fill(dataTable);
ds.Tables.Add(dataTable);
}
}
Then I write the following code to store data in a table in sql server
if (ds.Tables.Count > 0)
{
foreach (DataTable dt in ds.Tables)
{
using (SqlConnection con = new SqlConnection(consString))
{
con.Open();
for (int i = 2; i < dt.Rows.Count; i++)
{
for (int j = 1; j < dt.Columns.Count; j += 3)
{
//Here the problem
SqlCommand command = new SqlCommand( what should I write? ) ;
command.ExecuteNonQuery();
}
}
con.Close();
}
}
My problem is how to store sheetname in a table [Obj CA]?
I think your
"...VALUES('sheetname" + dt.Rows[0][j].ToString() + "' )
should be
"...VALUES('sheetname', '" + dt.Rows[0][j].ToString() + "' )
since you try to insert two values but you didn't seperate them with a comma.
But as a better way, use parameterized queries. This kind of string concatenations are open for SQL Injection attacks.
var command = new SqlCommand(#"INSERT INTO [Obj CA] (sheetname, [Rayon])
VALUES('sheetname', #rayon"), con);
for (int i = 2; i < dt.Rows.Count; i++)
{
for (int j = 1; j < dt.Columns.Count; j += 3)
{
command.Parameters.Clear();
command.Parameters.AddWithValue("#rayon", dt.Rows[0][j].ToString());
command.ExecuteNonQuery();
}
}
By the way, since I didn't know your column types, I used AddWithValue as an example but you don't. This method may generate unexpected results sometimes. Use Add overload to specify your parameter type and it's size.
Also I strongly suspect you should change your column definition order as well like (sheetname, [Rayon]) in your INSERT statement.
Related
I have a dataGridView that displays data from a table, after I export these data as a xml file, I add the unique field to another table so I can display only non-exported data.
How I display only data that are not yet exported :
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
SqlCommand queryLocal = new SqlCommand("SELECT *uniqueField* FROM myTable
WHERE *uniqueField* = " + dataGridView1.Rows[i].Cells[3].Value.ToString().Trim().Replace("'","''"), con);
var reader = queryLocal.ExecuteReader();
if (reader.Read())
{
dataGridView1.Rows.RemoveAt(i);
i--;
}
reader.Close();
}
The problem is that it takes more than 20 seconds to filter less than 400 rows.
How could I improve the performance here ?
You could try this:
SqlCommand queryLocal = new SqlCommand("SELECT DISTINCT *uniqueField* FROM myTable");
var reader = queryLocal.ExecuteReader();
List<string> uniqueFields = new List<string>();
while (reader.Read())
uniqueFields.Add(reader[0]);
reader.Close();
for (int i = 0; i < dataGridView1.Rows.Count; i++)
{
if (uniqueFields.Contains(dataGridView1.Rows[i])
{
dataGridView1.Rows.RemoveAt(i);
i--;
}
}
Well I produce code which takes the values from the database and creates XML files, but I stack on how to modify my code in order to avoid the empty cells from the column VALUES, also I need to select the data from the column VALUES where the column SELECT is 1. Can you please help me with examples? Thank you in advance .
code
public DataSet produceFieldsXml(int langID, int presID)
{
SqlConnection con = new System.Data.SqlClient.SqlConnection();
con.ConnectionString = ConfigurationManager.ConnectionStrings["beta"].ConnectionString;
SqlDataAdapter da = new SqlDataAdapter(#"SELECT IsubField.ID,IsubField.SUBJECT_ID as SUBJECTID,IsubField.VALUE AS DSCR,IsubField.FIELD_TYPE_ID, PFTT.VALUE AS TITLE
FROM I_SUBJECT_FIELD IsubField, P_FIELD_TYPE_TITLE PFTT
WHERE IsubField.PRESENTATION_ID = " + presID + #"
AND IsubField.LANGUAGE_ID = " + langID + #"
AND PFTT.FIELD_TYPE_ID = IsubField.FIELD_TYPE_ID
AND PFTT.LANGUAGE_ID = " + langID + #"
ORDER BY IsubField.SUBJECT_ID", con);
DataSet ds = new DataSet();
da.Fill(ds, "FieldItem");
ds.Tables[0].Columns.Add("SEQ", typeof(Int64));
DataSet dsNew = new DataSet();
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
SqlDataAdapter daII = new SqlDataAdapter(#"SELECT CATEGORY_ID FROM I_SUBJECT WHERE ID = " + Convert.ToInt32(ds.Tables[0].Rows[i]["SUBJECTID"].ToString().Trim()) + "", con);
DataSet dsa = new DataSet();
daII.Fill(dsa, "FieldItem");
dsNew.Merge(dsa);
}
DataSet dsaa = new DataSet();
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
SqlDataAdapter daII = new SqlDataAdapter(#"SELECT SEQ FROM P_CATEGORY_FIELD WHERE CATEGORY_ID = " + Convert.ToInt32(dsNew.Tables[0].Rows[i]["CATEGORY_ID"].ToString().Trim()) + " AND FIELD_ID = " + Convert.ToInt32(ds.Tables[0].Rows[i]["FIELD_TYPE_ID"].ToString().Trim()) + "", con);
daII.Fill(dsaa, "FieldItem");
ds.Tables[0].Rows[i]["SEQ"] = dsaa.Tables[0].Rows[0]["SEQ"];
}
for (int i = 0; i > dsaa.Tables[0].Rows.Count; i++)
{
ds.Tables[0].Rows[i]["SEQ"] = dsaa.Tables[0].Rows[0]["SEQ"];
}
dsaa.WriteXml(Server.MapPath("~/") + "ZipFiles\\" + "new.xml");
return ds;
}
If I were you I'd really try to learn more SQL, because I'm pretty sure those three queries above can be one query given the right joins. Unfortunately we cannot help you right now, unless you show us what I_SUBJECT_FIELD, P_FIELD_TYPE_TITLE, I_SUBJECT and P_CATEGORY_FIELD look like and how they're related.
Because this is simply very bad:
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
SqlDataAdapter daII = new SqlDataAdapter(#"SELECT CATEGORY_ID FROM I_SUBJECT WHERE ID = " + Convert.ToInt32(ds.Tables[0].Rows[i]["SUBJECTID"].ToString().Trim()) + "", con);
}
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
{
SqlDataAdapter daII = new SqlDataAdapter(#"SELECT SEQ FROM P_CATEGORY_FIELD WHERE CATEGORY_ID = " + Convert.ToInt32(dsNew.Tables[0].Rows[i]["CATEGORY_ID"].ToString().Trim()) + " AND FIELD_ID = " + Convert.ToInt32(ds.Tables[0].Rows[i]["FIELD_TYPE_ID"].ToString().Trim()) + "", con);
}
Those queries get executed numerous times and at most they should be executed once (bit trickier in the second case, but not impossible), and in fact they really should be part of the main query (provided the tables are set up correctly).
An even bigger headscratcher is this:
for (int i = 0; i > dsaa.Tables[0].Rows.Count; i++)
{
ds.Tables[0].Rows[i]["SEQ"] = dsaa.Tables[0].Rows[0]["SEQ"];
}
...since that is exactly the same line as in the previous for-loop.
Once you manage to create a single query it will be far easier to convert that data into XML.
Also look into encapsulating code in using statements.
I have this function in c#. When the FOR is call, a error appears in ExecuteNonQuery. The error is "ExecuteNonQuery requires the command to have a transaction when the connection assigned to the command is in a pending local transaction. The Transaction property of the command has not been initialized."
SqlConnection cnn = new SqlConnection(WebConfigurationManager.ConnectionStrings["strCnn"].ToString());
cnn.Open();
SqlTransaction trx = cnn.BeginTransaction();
try
{
SqlCommand cmd= new SqlCommand();
for (int j = 0; j < arr.Length; j++) {
cmd.CommandText = "UPDATE rc SET nc= " + arr[j].Col3 + " WHERE cr = " + arr[j].Col1;
cmd.Connection = cnn;
cmd.ExecuteNonQuery();
}
trx.Commit();
return 1;
}
catch (SqlException ex)
{
try
{
trx.Rollback();
return 0;
}
catch (Exception exRollback)
{
return 0;
}
}
This error message shows that you have opened a transaction and it is still open at the point of executing ExecuteNonQuery,
You are executing ExecuteNonQuery before commitment of transaction.
Define
comando.Transaction = trx;
So that ExecuteNonQuery will be executed on the same transaction.
Use
// Create command on transaction and automatically assign open transaction
var comando = conexao.CreateCommand()
or assign the transaction to the command.
// Create command
var comando = new SqlCommand();
// and assign transaction manually
comando.Transaction = trx;
you forgot to set transaction
comando.Transaction = trx;
You need to assign the transaction to the command, like so:
SqlCommand comando = new SqlCommand();
comando.Transaction = trx;
I would also recommend setting the Connection property outside of the for loop too, so you code would look like this:
SqlCommand comando = new SqlCommand();
comando.Transaction = trx;
comando.Connection = conexao;
for (int j = 0; j < arr.Length; j++) {
comando.CommandText = "UPDATE RECURSO_CLIENTE SET NM_CLIENTE = " + arr[j].Col3 + " WHERE CD_RECURSO = " + arr[j].Col1;
comando.ExecuteNonQuery();
}
trx.Commit();
You need to set SqlCommand's transaction property.
SqlCommand comando = new SqlCommand();
comando.Transaction = trx;
Your sqlCommand doesn't know about your transaction.
Here's a copy-paste fix:
SqlConnection conexao = new SqlConnection(WebConfigurationManager.ConnectionStrings["strConexao"].ToString());
conexao.Open();
SqlTransaction trx = conexao.BeginTransaction();
try
{
for (int j = 0; j < arr.Length; j++) {
var commandText = "UPDATE RECURSO_CLIENTE SET NM_CLIENTE = " + arr[j].Col3 + " WHERE CD_RECURSO = " + arr[j].Col1;
SqlCommand comando = new SqlCommand(commandText, conexao, trx);
comando.ExecuteNonQuery();
}
trx.Commit();
return 1;
}
catch (SqlException ex)
{
try
{
trx.Rollback();
return 0;
}
catch (Exception exRollback)
{
return 0;
}
}
As has been pointed out, you never assign your transaction to the command. However there are a few other points I have picked up on.
First and foremost USE PARAMETRISED QUERIES, they will improve performace, type safety and most importantly save you from SQL Injection Attacks.
So instead of:
comando.CommandText = "UPDATE RECURSO_CLIENTE SET NM_CLIENTE = " + arr[j].Col3 + " WHERE CD_RECURSO = " + arr[j].Col1;
You would use:
comando.CommandText = "UPDATE RECURSO_CLIENTE SET NM_CLIENTE = #Col3 WHERE CD_RECURSO = #Col1";
comando.Parameters.AddWithValue("#Col3", arr[j].Col3);
comando.Parameters.AddWithValue("#Col1", arr[j].Col1);
Secondly, wrap your sql command object with a using wrapper to ensure it is properly dispose of, there is no benefit from reusing the same object over and over again (and this can cause problems):
for (int j = 0; j < arr.Length; j++)
{
using (var comando = new SqlCommand("UPDATE RECURSO_CLIENTE SET NM_CLIENTE = #Col3 WHERE CD_RECURSO = #Col1", conexao))
{
comando.Transaction = trx;
comando.Parameters.AddWithValue("#Col3", arr[j].Col3);
comando.Parameters.AddWithValue("#Col1", arr[j].Col1);
comando.ExecuteNonQuery();
}
}
Finally, if you are using SQL-Server 2008+ you can use Table valued Parameters to do this update in a single query:
You first need a type
CREATE TABLE YourTypeName AS TABLE (Col1 INT, Col3 INT);
Then your update statement would be something like:
DECLARE #UpdateValues AS YourTypeName;
MERGE RECURSO_CLIENTE rc
USING #UpdateValues u
ON rc.CD_RECURSO = u.Col1
WHEN MATCHED UPDATE
SET NM_CLIENTE = u.Col3;
This means a single statement and you don't need to use explicit transactions. (You might wonder why I have used merge instead of UPDATE, here is why). So putting it all together you would get:
var dataTable = new DataTable();
dataTable.Columns.Add("Col1", typeof(int));
dataTable.Columns.Add("Col3", typeof(int));
for (int j = 0; j < arr.Length; j++)
{
var newRow = dataTable.NewRow();
newRow[0] = arr[j].Col1;
newRow[1] = arr[j].Col3;
dataTable.Rows.Add(newRow);
}
string sql = #" MERGE RECURSO_CLIENTE rc
USING #UpdateValues u
ON rc.CD_RECURSO = u.Col1
WHEN MATCHED UPDATE
SET NM_CLIENTE = u.Col3;";
using (var conexao = new SqlConnection(WebConfigurationManager.ConnectionStrings["strConexao"].ToString()))
using (var comando = new SqlCommand(sql, conexao))
{
conexao.Open();
var tableParam = new SqlParameter("#UpdateValues", SqlDbType.Structured);
tableParam.TypeName = "#YourTypeName";
tableParam.Value = dataTable;
comando.Parameters.Add(tableParam);
comando.ExecuteNonQuery();
}
For some reason when I try to read in numbers from an Access database using this code, I just get blank entries in my data grid. I can read strings in fine. Anyone know why this may be? And yes, the actual data type for the unread entries in Access is a NUMBER.
string strProvider = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=Employees.mdb";
string strSql = "SELECT * FROM tbl_employees";
OleDbConnection con = new OleDbConnection(strProvider);
OleDbCommand cmd = new OleDbCommand(strSql, con);
con.Open();
cmd.CommandType = CommandType.Text;
OleDbDataReader dr = cmd.ExecuteReader();
int columnCount = dr.FieldCount;
for (int i = 0; i < columnCount; i++)
{
dgv.Columns.Add(dr.GetName(i).ToString(), dr.GetName(i).ToString());
}
string[] rowData = new string[columnCount];
while (dr.Read())
{
for (int k = 0; k < columnCount; k++)
{
if (dr.GetFieldType(k).ToString() =="System.Int32")
{
rowData[k] = dr.GetInt32(k).ToString();
}
if (dr.GetFieldType(k).ToString() == "System.String")
{
rowData[k] = dr.GetString(k);
}
}
dgv.Rows.Add(rowData);
}
I suggest you try stepping through your code in the debugger so you can see what's happening. My first guess would be that your numeric fields aren't returned as Int32, perhaps they are floats or decimals instead.
If for some reason you can't step through it, try something like this:
if (dr.GetFieldType(k).ToString() =="System.Int32")
{
rowData[k] = dr.GetInt32(k).ToString();
}
else if (dr.GetFieldType(k).ToString() == "System.String")
{
rowData[k] = dr.GetString(k);
}
else
{
rowData[k] = dr.GetFieldType(k).ToString();
}
That will let you see what type of value is in the fields that didn't get displayed.
Respected Users,
I am extracting data using data set.
I want to put value in textbox. But value is not comming.
I have following Code
try
{
da = new SqlDataAdapter("select ID from Customer where Name='" + gvBkPendingSearch.SelectedRows[0].Cells[1].Value.ToString() + "'",con);
DataSet ds = new DataSet();
da.Fill(ds);
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
txtCustomerID.Text = ds.Tables[0].Rows[0].ToString();
}
catch (Exception ex)
{
}
finally
{
}
txtCustomerID is my textbox.
It is capturing value as>>>>>System.Data.DataRow
Error is in txtCustomerID.Text = ds.Tables[0].Rows[0].ToString();
but i am not able to understand it.
Please help me.
change it like this
for (int i = 0; i < ds.Tables[0].Rows.Count; i++)
txtCustomerID.Text = ds.Tables[0].Rows[i]["ID"].ToString();
The mistake you are doing is, you are accessing this
ds.Tables[0].Rows[0].ToString();
means 0th row, the whole row!! not the column value
And the datatable row is System.Data.DataRow in .Net
You need to select the column:
txtCustomerID.Text = ds.Tables[0].Rows[i][0].ToString();
Also note that you are overwriting the value of the textbox on each iteration of the loop. So what you will end up with is the ID of the last record in this textbox.
Also your query seems vulnerable to SQL injection. Personally I would recommend you scraping the DataSets in favor of an ORM or even plain old ADO.NET:
public static IEnumerable<int> GetIds(string name)
{
using (var conn = new SqlConnection("Your connection string comes here"))
using (var cmd = conn.CreateCommand())
{
conn.Open();
cmd.CommandText = "select ID from Customer where Name=#Name";
cmd.Parameters.AddWithValue("#Name", name);
using (var reader = cmd.ExecuteReader())
{
while (reader.Read())
{
yield return reader.GetInt32(reader.GetOrdinal("ID"));
}
}
}
}
And now you could happily use this function:
string name = gvBkPendingSearch.SelectedRows[0].Cells[1].Value.ToString();
int id = GetIds(name).FirstOrDefault();
txtCustomerID.Text = id.ToString();