I am trying to insert data using a stored procedure that has two tables. This first table is data is through text boxes the second data is through a grid which I stored in the database and passed to be inserted. The problem is when reading datatable and inserting it says there are too many parameter which happens to add in the for loop. Any suggestion how to handle this as the SP? Thanks in advance.
CODE:
try
{
SqlConnection conn = new SqlConnection();
conn.ConnectionString = strConnection;
SqlCommand cmd = new SqlCommand();
cmd.Connection = conn;
cmd.CommandTimeout = 120;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "insFamilyDetails";
cmd.Parameters.AddWithValue("#strHusbandName", strHusbandName);
cmd.Parameters.AddWithValue("#strRelation", strRelation);
....
....
// Child Details
for (int i = 0; i < strChildredDetails.Rows.Count; i++)
{
cmd.Parameters.AddWithValue("#strChildName", strChildredDetails.Rows[i][0].ToString());
cmd.Parameters.AddWithValue("#strDOB", strChildredDetails.Rows[i][1]);
cmd.Parameters.AddWithValue("#strBaptisedon", strChildredDetails.Rows[i][2]);
cmd.Parameters.AddWithValue("#strFirstComOn", strChildredDetails.Rows[i][3]);
cmd.Parameters.AddWithValue("#strConfirmedOn", strChildredDetails.Rows[i][4]);
cmd.Parameters.AddWithValue("#strMarried", "0");
cmd.Parameters.AddWithValue("#strAlive", "1");
}
conn.Open();
ReturnValue = Convert.ToBoolean(cmd.ExecuteNonQuery());
conn.Close();
}
catch (Exception e)
{
DL_LogAppErrors(e.ToString(), System.Reflection.MethodBase.GetCurrentMethod().Name, "Insert Family Details");
return ReturnValue;
}
return ReturnValue;
I assume from the code you're going to add into a main table, and Child table. For this case, you need to separate the process into two:
Add the data for in main table
Loop to add the child data
Note: you need to clear the parameters before adding a new set, OR instead of adding new parameters, change the value of existing parameters
EDIT: Using Transaction
con.Open();
SqlTransaction trans = con.BeginTransaction();
try {
// Execute the SP here
// After all SP executed, call the commit method
trans.Commit();
} catch (Exception ex) {
// An error happened, rollback
trans.RollBack();
}
con.Close();
You are adding parameters in command in each iteration of the loop. After first iteration you are trying to add same parameter name in parameter collection. You probably need to clear the collection of parameter on each iteration using SqlParameterCollection.Clear. Clear the parameter collection after executing command (In loop body).
conn.Open();
for (int i = 0; i < strChildredDetails.Rows.Count; i++)
{
cmd.Parameters.AddWithValue("#strChildName", strChildredDetails.Rows[i][0].ToString());
cmd.Parameters.AddWithValue("#strDOB", strChildredDetails.Rows[i][2]);
cmd.Parameters.AddWithValue("#strBaptisedon", strChildredDetails.Rows[i][2]);
cmd.Parameters.AddWithValue("#strFirstComOn", strChildredDetails.Rows[i][3]);
cmd.Parameters.AddWithValue("#strConfirmedOn", strChildredDetails.Rows[i][4]);
cmd.Parameters.AddWithValue("#strMarried", "0");
cmd.Parameters.AddWithValue("#strAlive", "1");
ReturnValue = Convert.ToBoolean(cmd.ExecuteNonQuery());
cmd.Parameters.Clear();
}
conn.Close();
If you have many records to insert in a table then you can send the comma separated values in SP and split then in SP and insert them. It will save db calls. This post will show how you can do that.
For each row you want to insert you have to call the ExecuteNonQuery() function ie, it should be inside the for loop and after that clear the parameter collection at the end of loop.
conn.Open();
// Child Details
for (int i = 0; i < strChildredDetails.Rows.Count; i++)
{
cmd.Parameters.AddWithValue("#strHusbandName", strHusbandName);
cmd.Parameters.AddWithValue("#strRelation", strRelation);
....
....
cmd.Parameters.AddWithValue("#strChildName", strChildredDetails.Rows[i][0].ToString());
cmd.Parameters.AddWithValue("#strDOB", strChildredDetails.Rows[i][1]);
cmd.Parameters.AddWithValue("#strBaptisedon", strChildredDetails.Rows[i][2]);
cmd.Parameters.AddWithValue("#strFirstComOn", strChildredDetails.Rows[i][3]);
cmd.Parameters.AddWithValue("#strConfirmedOn", strChildredDetails.Rows[i][4]);
cmd.Parameters.AddWithValue("#strMarried", "0");
cmd.Parameters.AddWithValue("#strAlive", "1");
ReturnValue = Convert.ToBoolean(cmd.ExecuteNonQuery());
cmd.Parameters.Clear();
}
As already said, you need to have ExecuteNonQuery inside for each loop, if you want to insert records of your grid.
Alternate option would be to Use Table Valued Paramter if you're using SQL Server 2008, that would make life more easy and you don't have to make round trip for each record of your gridview. Just pass the datatable.
Please check this link.
Edit:
For SQL Server 2005, you might want to use XML. Please check this link.
public string SerializeObject<T>(T Obj)
{
string strxml = string.Empty;
using (StringWriter sw = new StringWriter())
{
XmlSerializer xs = new XmlSerializer(typeof(T));
xs.Serialize(sw, Obj);
strxml = sw.ToString();
}
return strxml;
}
Link contains above function, pass your datatable to this function, check out the generated XML and use same casing in stored procedure for elements in XML as XML is case sensitive.
Related
I am trying to load a list of Menu Items into a DataGrid in WPF, and can sucessfully only return one row from the table. I have tried to create a for loop that would read into each row and load it into dtsizes, but I get an exception stating my procedure or function has too many arguments specified.
private DataTable GetSizesData()
{
//NOTE: compiler will through nullpointer error if datatable is null, so create an object
DataTable dtsizes = new DataTable();
//get and store the datatable through sql connection -> applicationsetting.cs
using (SqlConnection con = new SqlConnection(ApplicationSetting.ConnectionString()))
{
//runs sql command usp load pizzas (see .txt file)
using (SqlCommand cmd = new SqlCommand("usp_Pizzas_LoadAllPizzas", con))
{
for (int i = 0; i < 11; i++)
{
//load procedure and given params
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("#main_identifier", i);
con.Open();
SqlDataReader sdr = cmd.ExecuteReader();
con.Close();
dtsizes.Load(sdr);
}
//read data then load into dtsizes datatable
}
}
return dtsizes;
}
Here is my usp_PizzasAllPizzas SQL Procedure:
ALTER PROCEDURE [dbo].[usp_Pizzas_LoadAllPizzas]
(
#main_identifier INT
)
AS
BEGIN
--DECLARE #IsSelected BIT
--SET #IsSelected = 0
SELECT [main_identifier] AS 'ID'
,[main_name] AS 'Title'
--,#IsSelected AS 'Select'
FROM [dbo].[main_cata] WHERE
[main_identifier] = #main_identifier
END
I am sure the answer here is simple but I cannot figure out how to load all entries from the table into my dataGrid. I have tried defining a separate INT and looping through that in a separate part of the class but still have not got it working. Any feedback or comments helps and thank you so much!
Database Table
You are closing connection in for loop.
why you did not write query just like this
SELECT [main_identifier] AS 'ID'
,[main_name] AS 'Title'
FROM [dbo].[main_cata] WHERE
[main_identifier] < #main_identifier
and in C# code just remove for loop.
I am using that sqltransaction for the insert multiple tables each data.
But I have problem that have the database have the two same data.
What should I do for solve that problem?
please help me? Thanx
SqlConnection baglanti = system.baglan();
SqlCommand Trislem1_Ekle = new SqlCommand("Insert tblTr (Ad,TipID,BolgeID,Yerler,Resim) values(#Ad,#TipID,#BolgeID,#Yerler,#Resim) SELECT SCOPE_IDENTITY()", baglanti);
SqlCommand Tr2_TrAciklama = new SqlCommand("Insert tblTrAciklamaDetay (TrID,TrProgram) values((SELECT IDENT_CURRENT('tblTr')),#TrProgram)", baglanti);
Trislem1_Ekle.Parameters.AddWithValue("#Ad", txtTrAd.Text);
Trislem1_Ekle.Parameters.AddWithValue("#TipID", dlTrTip.SelectedValue);
Trislem1_Ekle.Parameters.AddWithValue("#BolgeID", BolgeID.SelectedValue);
Trislem1_Ekle.Parameters.AddWithValue("#Yerler", Yerler.Text);
Trislem1_Ekle.Parameters.AddWithValue("#Resim", Resim.SelectedValue);
Tr2_TrAciklama.Parameters.AddWithValue("#TrProgram", TrProgram.Text);
SqlTransaction sqlTrans = baglanti.BeginTransaction();
Trislem1_Ekle.Transaction = sqlTrans;
Tr2_TrAciklama.Transaction = sqlTrans;
try
{
Trislem1_Ekle.ExecuteNonQuery();
Tr2_TrAciklama.ExecuteNonQuery();
string SonIDGelen = Trislem1_Ekle.ExecuteScalar().ToString();
sqlTrans.Commit();
}
catch (Exception hata)
{
Response.Write("İşleminiz yapılamadı, Oluşan Hatanın Detayı<br />" + hata);
sqlTrans.Rollback();
}
finally
{
baglanti.Close();
baglanti.Dispose();
Trislem1_Ekle.Dispose();
Tr2_TrAciklama.Dispose();
}
As far as I see, you executing your Trislem1_Ekle command twice.
One with
Trislem1_Ekle.ExecuteNonQuery();
and the other one with;
string SonIDGelen = Trislem1_Ekle.ExecuteScalar().ToString();
Deleting the first one seems enough. Both ExecuteNonQuery and ExecuteScalar executes your query, and ExecuteScalar returns first column of the first row additionally.
Instead of disposing your database connections and commands manually, use using statement instead.
using(SqlConnection conn = new SqlConnection(conString))
{
using(SqlCommand cmd = conn.CreateCommand())
{
// Create your commands
// Add your parameter values
// Execute your commands
}
}
And don't use AddWithValue method. It may generate some unexptected results. Use .Add() method and overloads instead.
Can we stop using AddWithValue() already?
try like this
I think you are executing ExecuteScalar() twice on Command Trislem1_Ekle
Trislem1_Ekle.ExecuteNonQuery();
Tr2_TrAciklama.ExecuteNonQuery();
string SonIDGelen = Trislem1_Ekle.ExecuteScalar().ToString();
Replace with this:
string SonIDGelen = Trislem1_Ekle.ExecuteScalar().ToString();
Tr2_TrAciklama.ExecuteNonQuery();
I want to delete all rows in a datatable.
I use something like this:
foreach (DataRow row in dt.Rows)
{
row.Delete();
}
TableAdapter.Update(dt);
It works good but it takes lots of time if I have much rows.
Is there any way to delete all rows at once?
If you are running your code against a sqlserver database then
use this command
string sqlTrunc = "TRUNCATE TABLE " + yourTableName
SqlCommand cmd = new SqlCommand(sqlTrunc, conn);
cmd.ExecuteNonQuery();
this will be the fastest method and will delete everything from your table and reset the identity counter to zero.
The TRUNCATE keyword is supported also by other RDBMS.
5 years later:
Looking back at this answer I need to add something. The answer above is good only if you are absolutely sure about the source of the value in the yourTableName variable. This means that you shouldn't get this value from your user because he can type anything and this leads to Sql Injection problems well described in this famous comic strip. Always present your user a choice between hard coded names (tables or other symbolic values) using a non editable UI.
This will allow you to clear all the rows and maintain the format of the DataTable.
dt.Rows.Clear();
There is also
dt.Clear();
However, calling Clear() on the DataTable (dt) will remove the Columns and formatting from the DataTable.
Per code found in an MSDN question, an internal method is called by both the DataRowsCollection, and DataTable with a different boolean parameter:
internal void Clear(bool clearAll)
{
if (clearAll) // true is sent from the Data Table call
{
for (int i = 0; i < this.recordCapacity; i++)
{
this.rows[i] = null;
}
int count = this.table.columnCollection.Count;
for (int j = 0; j < count; j++)
{
DataColumn column = this.table.columnCollection[j];
for (int k = 0; k < this.recordCapacity; k++)
{
column.FreeRecord(k);
}
}
this.lastFreeRecord = 0;
this.freeRecordList.Clear();
}
else // False is sent from the DataRow Collection
{
this.freeRecordList.Capacity = this.freeRecordList.Count + this.table.Rows.Count;
for (int m = 0; m < this.recordCapacity; m++)
{
if ((this.rows[m] != null) && (this.rows[m].rowID != -1))
{
int record = m;
this.FreeRecord(ref record);
}
}
}
}
As someone mentioned, just use:
dt.Rows.Clear()
That's the easiest way to delete all rows from the table in dbms via DataAdapter. But if you want to do it in one batch, you can set the DataAdapter's UpdateBatchSize to 0(unlimited).
Another way would be to use a simple SqlCommand with CommandText DELETE FROM Table:
using(var con = new SqlConnection(ConfigurationSettings.AppSettings["con"]))
using(var cmd = new SqlCommand())
{
cmd.CommandText = "DELETE FROM Table";
cmd.Connection = con;
con.Open();
int numberDeleted = cmd.ExecuteNonQuery(); // all rows deleted
}
But if you instead only want to remove the DataRows from the DataTable, you just have to call DataTable.Clear. That would prevent any rows from being deleted in dbms.
Why dont you just do it in SQL?
DELETE FROM SomeTable
Just use dt.Clear()
Also you can set your TableAdapter/DataAdapter to clear before it fills with data.
TableAdapter.Update(dt.Clone());
//or
dt=dt.Clone();
TableAdapter.Update(dt);
//or
dt.Rows.Clear();
dt.AcceptChanges();
TableAdapter.Update(dt);
If you are really concerned about speed and not worried about the data you can do a Truncate. But this is assuming your DataTable is on a database and not just a memory object.
TRUNCATE TABLE tablename
The difference is this removes all rows without logging the row deletes making the transaction faster.
Here we have same question. You can use the following code:
SqlConnection con = new SqlConnection();
con.ConnectionString = ConfigurationManager.ConnectionStrings["yourconnectionstringnamehere"].ConnectionString;
con.Open();
SqlCommand com = new SqlCommand();
com.Connection = con;
com.CommandText = "DELETE FROM [tablenamehere]";
SqlDataReader data = com.ExecuteReader();
con.Close();
But before you need to import following code to your project:
using System.Configuration;
using System.Data.SqlClient;
This is the specific part of the code that can delete all rows is a table:
DELETE FROM [tablenamehere]
This must be your table name:tablenamehere
- This can delete all rows in table: DELETE FROM
I using MDaf just use this code :
DataContext db = new DataContext(ConfigurationManager.ConnectionStrings["con"].ConnectionString);
db.TruncateTable("TABLE_NAME_HERE");
//or
db.Execute("TRUNCATE TABLE TABLE_NAME_HERE ");
Here is a clean and modern way to do it using Entity FW and without SQL Injection or TSQL..
using (Entities dbe = new Entities())
{
dbe.myTable.RemoveRange(dbe.myTable.ToList());
dbe.SaveChanges();
}
Is there a Clear() method on the DataTable class??
I think there is. If there is, use that.
Datatable.clear() method from class DataTable
I've got a DataTable which has a number of records.
I've tried the following code for iterating through the DataTable and inserting into the database, but I am receiving an error message saying that the parameters have not been declared:
using (SqlCommand command = new SqlCommand(("My insert statement"), connection))
{
SqlParameter param1 = new SqlParameter();
SqlParameter param2 = new SqlParameter();
param1.ParameterName = "#ProductID";
param2.ParameterName = "#ID";
foreach (DataRow row in table.Rows)
{
param1.Value = row[0].ToString();
param2.Value = row[1].ToString();
command.ExecuteNonQuery();
}
}
Any ideas? Please do not suggest stored procedures - I'd like to do this through this method or something similar.
You need to add your parameters to the command.
http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.parameters.aspx
Caution though
The way you have your code if you put the add in the foreach your first execute will work and then the next will fail with an error message stating that you have a variable named #ProductID / #ID already.
You need to clear the parameters each time.
See this for an example.
Check out SqlBulkCopy, it will allow you to pass in your Datatable straight to the database. Link
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{
bulkCopy.DestinationTableName = "dbo.TableYouWantToInsertTheRowsTo";
try
{
// Write from the source to the destination.
bulkCopy.WriteToServer(newProductsDataTable);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
If you are using SQL2008 than entire List of object/datatable can be passed as parameter to store procedure. In this case you need to declare same class in sql also.
Replaces Question: Update multiple rows into SQL table
Here's a Code Snippet to update an exam results set.
DB structure is as given, but I can submit Stored Procedures for inclusion (Which are a pain to modify, so I save that until the end.)
The question: Is there a better way using SQL server v 2005.,net 2.0 ?
string update = #"UPDATE dbo.STUDENTAnswers
SET ANSWER=#answer
WHERE StudentID =#ID and QuestionNum =#qnum";
SqlCommand updateCommand = new SqlCommand( update, conn );
conn.Open();
string uid = Session["uid"].ToString();
for (int i= tempStart; i <= tempEnd; i++)
{
updateCommand.Parameters.Clear();
updateCommand.Parameters.AddWithValue("#ID",uid);
updateCommand.Parameters.AddWithValue("#qnum",i);
updateCommand.Parameters.AddWithValue("#answer", Request.Form[i.ToString()]);
try
{
updateCommand.ExecuteNonQuery();
}
catch { }
}
A few things stand out:
You don't show where the SqlConnection is instantiated, so it's not clear that you're disposing it properly.
You shouldn't be swallowing exceptions in the loop - better to handle them in a top level exception handler.
You're instantiating new parameters on each iteration through the loop - you could just reuse the parameters.
Putting this together it could look something like the following (if you don't want to use a transaction, i.e. don't care if some but not all updates succeed):
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand updateCommand = new SqlCommand(update, conn))
{
string uid = Session["uid"].ToString();
updateCommand.Parameters.AddWithValue("#ID", uid);
updateCommand.Parameters.AddWithValue("#qnum", i);
updateCommand.Parameters.Add("#answer", System.Data.SqlDbType.VarChar);
for (int i = tempStart; i <= tempEnd; i++)
{
updateCommand.Parameters["#answer"] = Request.Form[i.ToString()];
updateCommand.ExecuteNonQuery();
}
}
}
Or to use a transaction to ensure all or nothing:
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlTransaction transaction = conn.BeginTransaction())
{
using (SqlCommand updateCommand = new SqlCommand(update, conn, transaction))
{
string uid = Session["uid"].ToString();
updateCommand.Parameters.AddWithValue("#ID", uid);
updateCommand.Parameters.AddWithValue("#qnum", i);
updateCommand.Parameters.Add("#answer", System.Data.SqlDbType.VarChar);
for (int i = tempStart; i <= tempEnd; i++)
{
updateCommand.Parameters["#answer"] = Request.Form[i.ToString()];
updateCommand.ExecuteNonQuery();
}
transaction.Commit();
}
} // Transaction will be disposed and rolled back here if an exception is thrown
}
Finally, another problem is that you are mixing UI code (e.g. Request.Form) with data access code. It would be more modular and testable to separate these - e.g. by splitting your application into UI, Business Logic and Data Access layers.
For 30 updates I think you're on the right track, although the comment about the need for a using around updateCommand is correct.
We've found the best performing way to do bulk updates (>100 rows) is via the SqlBulkCopy class to a temporary table followed by a stored procedure call to populate the live table.
An issue I see is when you are opening your connection.
I would at least before every update call the open and then close the connection after the update.
If your loop takes time to execute you will have your connection open for a long time.
It is a good rule to never open your command until you need it.
You can bulk insert using OpenXML. Create an xml document containing all your questions and answers and use that to insert the values.
Edit: If you stick with your current solution, I would at least wrap your SqlConnection and SqlCommand in a using block to make sure they get disposed.
emit a single update that goes against a values table:
UPDATE s SET ANSWER=a FROM dbo.STUDENTAnswers s JOIN (
SELECT 1 as q, 'answer1' as a
UNION ALL SELECT 2, 'answer2' -- etc...
) x ON s.QuestionNum=x.q AND StudentID=#ID
so you just put this together like this:
using(SqlCommand updateCommand = new SqlCommand()) {
updateCommand.CommandType = CommandType.Text;
updateCommand.Connection = conn;
if (cn.State != ConnectionState.Open) conn.Open();
StringBuilder sb = new StringBuilder("UPDATE s SET ANSWER=a FROM dbo.STUDENTAnswers s JOIN (");
string fmt = "SELECT {0} as q, #A{0} as a";
for(int i=tempStart; i<tempEnd; i++) {
sb.AppendFormat(fmt, i);
fmt=" UNION ALL SELECT {0},#A{0}";
updateCommand.Parameters.AddWithValue("#A"+i.ToString(), Request.Form[i.ToString()]);
}
sb.Append(") x ON s.QuestionNum=x.q AND StudentID=#ID");
updateCommand.CommandText = sb.ToString();
updateCommand.Parameters.AddWithValue("#ID", uid);
updateCommand.ExecuteNonQuery();
}
This has the advantages of being an all other nothing operation (like if you'd wrapped several updates in a transaction) and will run faster since:
The table and associated indexes are looked at/updated once
You only pay for the latency between your application and the database server once, rather than on each update