Inserting only new data to SQL Table - c#

Right now I have a SQL Server Table that's populated with data from external .CSV file that gets updated occasionally by having new data appended to it. Originally I had my program set up to delete everything in the table every time its ran and it would upload everything from the .CSV file. Now I'm trying to reconfigure it so that it only inserts newly appended data from the file instead of deleting everything first.
The program works by first delimiting data from the .CSV file that gets uploaded to array. Then I basically go through the array and upload each element. Right now I have secondary array that contains all the current primary keys from the Table and so far I have tried to compare these to the ones in the .CSV file that's being read. So it checks if the the primary key from the .CSV file is not already in the table / secondary array it goes ahead and does the inserting. This seems like a sound logic to be but for some reason I can't get it to work.
Here is the meat of my code:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//Create new SQL connection and adapter using Windows Authentication
SqlConnection myConnection = new SqlConnection("Data Source=database; Initial Catalog=Dashboard; Integrated Security=SSPI; Persist Security Info=false; Trusted_Connection=Yes");
SqlDataAdapter da = new SqlDataAdapter();
try
{
myConnection.Open();
da.InsertCommand = new SqlCommand("INSERT INTO DashboardLibAnswer(Id,Date,Time,Question,Details,Answer,Notes,EnteredBy,WhereReceived,QuestionType,AnswerMethod,TransactionDuration)"
+ "VALUES(#Id,#Date,#Time,#Question,#Details,#Answer,#Notes,#EnteredBy,#WhereReceived,#QuestionType,#AnswerMethod,#TransactionDuration)", myConnection);
da.InsertCommand.Parameters.Add("#Id", SqlDbType.NVarChar);
da.InsertCommand.Parameters.Add("#Date", SqlDbType.Text);
da.InsertCommand.Parameters.Add("#Time", SqlDbType.Text);
da.InsertCommand.Parameters.Add("#Question", SqlDbType.Text);
da.InsertCommand.Parameters.Add("#Details", SqlDbType.Text);
da.InsertCommand.Parameters.Add("#Answer", SqlDbType.Text);
da.InsertCommand.Parameters.Add("#Notes", SqlDbType.Text);
da.InsertCommand.Parameters.Add("#EnteredBy", SqlDbType.NVarChar);
da.InsertCommand.Parameters.Add("#WhereReceived", SqlDbType.NVarChar);
da.InsertCommand.Parameters.Add("#QuestionType", SqlDbType.NVarChar);
da.InsertCommand.Parameters.Add("#AnswerMethod", SqlDbType.NVarChar);
da.InsertCommand.Parameters.Add("#TransactionDuration", SqlDbType.NVarChar);
//Using the global variable counter this loop will go through each valid entry and insert it into the specifed database/table
for (int i = 0; i < counter; i++)
{
//This is where I try to do the comparision.
//idS is the secondary array with all the current primary keys in the Table
//collection is the primary array that stores new data from the .CSV file
if (idS.ElementAt(i) != collection.getIdItems(i))
{
da.InsertCommand.Parameters["#Id"].Value = collection.getIdItems(i);
da.InsertCommand.Parameters["#Date"].Value = collection.getDateItems(i);
da.InsertCommand.Parameters["#Time"].Value = collection.getTimeItems(i);
da.InsertCommand.Parameters["#Question"].Value = collection.getQuestionItems(i);
da.InsertCommand.Parameters["#Details"].Value = collection.getDetailsItems(i);
da.InsertCommand.Parameters["#Answer"].Value = collection.getAnswerItems(i);
da.InsertCommand.Parameters["#Notes"].Value = collection.getNotesItems(i);
da.InsertCommand.Parameters["#EnteredBy"].Value = collection.getEnteredByItems(i);
da.InsertCommand.Parameters["#WhereReceived"].Value = collection.getWhereItems(i);
da.InsertCommand.Parameters["#QuestionType"].Value = collection.getQuestionTypeItems(i);
da.InsertCommand.Parameters["#AnswerMethod"].Value = collection.getAnswerMethodItems(i);
da.InsertCommand.Parameters["#TransactionDuration"].Value = collection.getTransactionItems(i);
da.InsertCommand.ExecuteNonQuery();
}
//Updates the progress bar using the i in addition to 1
_worker.ReportProgress(i + 1);
} // end for
//Once the importing is done it will show the appropriate message
MessageBox.Show("Finished Importing");
} // end try
catch (Exception exceptionError)
{
//To show exceptions thrown just uncomment bellow line
//rtbOutput.AppendText(exceptionError.ToString);
} // end catch
//Closes the SQL connection after importing is done
myConnection.Close();
} // end backgroundWorker1_DoWork
Right now the program executes but no new data is being inserted. It seems that program does not get out of the for loop because if it runs successfully it shows the "Finished Importing" message box.

The line
if (idS.ElementAt(i) != collection.getIdItems(i))
looks odd. Depending on what the getIdItems return, it looks to me as if you will match the first time the two lists are out of synch - so if items don't appear in the exact same order as you have already persisted them, you may get a false positive. Ie. the code will evaluate that the item doesn't exist and should be persisted, even if it exists at some other place in your collection.
Then, if you have a unique constraint the insert will fail, send you to the exception handler that is commented out - and then exit.
The first thing I would do would be to activate the exception handler to know what was going on. Second, I would probably change the code above so that you lookup, rather than iterate through, your idS collection. Ie. if you make idS a list or you include the Linq extension methods, you could do:
if (!idS.Contains(collection.getIdItems(i))
EDIT: Do note that for large idS, this is not particularly efficient and doesn't scale very well as your list grows. If you expect to handle a large number of idS, you might want to change the list to be a hash-collection or use the solution suggested here Check if list<t> contains any of another list.

Related

INSERT INTO c# to Microsoft access

I am trying to insert the text inside some text boxes into a database that I have in access. The code produces no errors but does not seem to add the items to the database.
The Database is called 'Database' the table is called 'TotalPlayerName' and the field is called 'Player Name'.
There are other fields in the table.
for(int i = 0; i < numberOfPlayers; i++){
using (OleDbConnection connection = new OleDbConnection(#"CONNECTION STRING"){
using (OleDbCommand command = new OleDbCommand(#"INSERT INTO TotalPlayerName ([Player Name]) VALUES(#p1)", connection)){
connection.Open();
command.Parameters.Add("#p1", OleDbType.VarWChar).Value = Convert.ToString(textBox[i].Text);
command.ExecuteNonQuery();
}
}
}
You might just need to declare #p1 because you call it in the INSERT statement, but it is never defined as a variable such as: varchar, int, ect, ect. This might work for what you are trying to do:
using (OleDbCommand command = new OleDbCommand(#"DECLARE #p1 VARCHAR(50) INSERT INTO TotalPlayerName ([Player Name]) VALUES(#p1)", connection)){
Also if at all possible i would definitely make it a stored procedure if you can. This works with SQL not sure if it will work with MS Access, but i would imagine so. The other thing you might want to do is make sure that it's finding the correct DB.
Database.dbo.TotalPlayerName
But that is probably not the issue, probably just the lack of variable declaration.
While I don't see what's specifically wrong with your code, I can tell you your methodology is off a bit. Specifically, for every iteration of your loop you are:
Establishing a connection to the database
Creating the insert command, creating a parameter and assigning the value
Executing the insert
It would be better all around if you did steps 1 and part of 2 once and then executed the statement within the loop like this:
using (OleDbConnection conn = new OleDbConnection(
#"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=c:\foo.accdb"))
{
conn.Open();
OleDbCommand command = new OleDbCommand(
#"INSERT INTO TotalPlayerName ([Player Name]) VALUES (#p1)", conn);
command.Parameters.Add(new OleDbParameter("#p1", OleDbType.VarChar));
for (int i = 0; i < numberOfPlayers; i++)
{
command.Parameters[0].Value = textbox[i].Text;
try
{
command.ExecuteNonQuery();
}
catch (Exception ex)
{
// do something
}
}
conn.Close();
}
I assume textbox is an array or list of actual Text Box controls. If that's the case, then textbox[i].Text is already a string, and you shouldn't need to do anything special to make OLE recognize it as such.
On a final note -- add that try/catch and put a breakpoint there. Are you SURE it's not failing? If you are running in debug mode, there is no guarantee that your program will halt -- it may just return back to the form without reporting any error. It may not be until you attempt to deploy the app that you see the actual error occurring.

How to get multiple rows from stored procedure?

I have a method for adding values to the database for all operations.
If this is selected from the database and this select return more rows from the database,
how can I get the rows and store in an array?
This is the method code :
public void ExcuteProcedure(string procName, List<SqlParameter> procparams)
{
try
{
SqlConnection mycon = new SqlConnection(connectionString);
mycon.Open();
SqlCommand mycom = new SqlCommand();
mycom.Connection = mycon;
mycom.CommandText = procName;
mycom.CommandType = CommandType.StoredProcedure;
foreach (var item in procparams)
{
SqlParameter myparm = new SqlParameter();
myparm.ParameterName = item.ParameterName;
// myparm.SqlDbType = item.SqlDbType;
myparm.Value = item.Value;
mycom.Parameters.Add(myparm);
}
var n= mycom.ExecuteScalar();
mycon.Close();
}
catch (SqlException e)
{
Console.WriteLine("Error Number is : " + e.Number);
Console.WriteLine("Error Message is : " + e.Message);
}
}
You need to call mycom.ExecuteReader(), which will give you a SqlDataReader which can read through the results.
Call Read() to advance through the rows.
It never ceases to amaze me the number of times I see devs trying to abstract away simple database connectivity; and the myriad of ways they inevitably screw it up.
The following may sound mean, but it needs said:
Clean up your code, it leaks like a sieve. Using clauses around the connection and command objects are pretty much mandatory. As it stands if you forget a single parameter or put in a bad value you will leak connections. Once the connection pool is filled up your app will crash in all sorts of interesting, and usually hard to debug, ways.
Next, if you aren't sure how to properly get records back from a database then you probably shouldn't try to abstract the code calling your procedures. Either use a lightweight ORM like Dapper or learn how what you are doing will ultimately involve a lot of extraneous code that the next developer on your project will want to rip out.
/rant over.
Getting back to the question: ExecuteScalar returns a single value. You need to use ExecuteReader. I'd suggest that you simply take the results of the reader, stuff it into a datatable and pass that back to the calling code.
var n = mycom.ExecuteScalar();
Scalar: an atomic quantity that can hold only one value at a time
Return a DataReader instead, and iterate through its rows
Fill a DataSet by using a DataAdapter (this is more appropriate if you have multiple tables in the result set).

how to check the duplicate entry in sql

Design of SQL Table Users:
Columns:
ID(Primary Key),
Name,
Age,
Zip.
I am displaying a grid view onto the asp.net application with the above table details which i am able to do now.Right now i am displaying only Name,age,zip on to the gridview.There is a form below the gridview where user can enter the data and save back to the DB.Here i am able to enter name,age,zip to the database and i am able to see the changes in the gridview.
So when i enter a duplicate name its not saving to the database as expected,at this point i have to show some thing to the user that the name already exists .
Here is my code in c#:
try
{
param[0] = new SqlParameter("#Name", SqlDbType.NVarChar);
param[0].Value = txtName.Text;
param[1] = new SqlParameter("#Age", SqlDbType.NVarChar);
param[1].Value = txtAge.Text;
param[2] = new SqlParameter("#Zip", SqlDbType.int);
param[2].Value = txtZip.Text;
DBHelper helper = new DBHelper();
helper.ExecuteNonQuery(CommandType.StoredProcedure, "Add_Users", param);
GridView1.DataBind();
}
catch (Exception ex)
{
}
I am expecting an sql exception in the above code where i am planning to notify the user .But i am unable to get any exception .
Can some one suggest where i am missing the catch here.
I just want to notify the user that the name is already taken eventually.
Using exceptions is a bad design in this case. This is not an error, but a predictable behaviour of your program.
One of the easiest ways to solve the problem is doing a select at the end of the Add_Users stored procedure that returns 1 or 0, depending on the success or failure, and using ExecuteScalar insead of ExecuteNonQuery to get the result.
You can also use a return in the stored procedure, and take back that value (geenrally as a special output parameter), but I don't know if the DbHelper allows you to do so.
You might also want to post your SQL code for "Add_Users".
I would think about doing a validation step rather than attempting to add it. So have one Stored Procedure for validation were you check the number of rows that contain the name you are trying to add. If the count is zero then proceed with the add. If there are records with that name then display a message to the user.
In your DBHelper use count to determine if your db should commit or not :
int count = SELECT COUNT(column_name) FROM table_name
and return it to your method which can then test it (instead of using Try/Catch)
if (helper.ExecuteNonQuery(CommandType.StoredProcedure, "Add_Users", param) >0))
{
// Show Error
}else
{
// all ok
}

SqlDataAdapter.Update - PART 2

NOTE: This is the simple version of my previous question that SHOULD have been written.
I have two tables in the same SQL Server database, TestTable and TestTable2. Neither has a primary key. Each have two columns,
TestInt (int)
TestString (varchar(50))
There is data in TestTable2, while TestTable is empty. I want to copy the contents of one into the other, via C#. (I know this can be done in SQL, but humor me here.)
Here is the code I've written to do the job, using a SqlDataAdapter object...
// Data in TestTable2, nothing in TestTable.
private static void Main(string[] args)
{
SqlConnection conn = /* CONNECTION CREATION CODE */
string sqlQuery = "SELECT * FROM TestTable2";
DataTable payload = /* SIMPLE QUERY CODE USING sqlQuery */
// CODE CHECKPOINT #1
sqlQuery = "SELECT * FROM TestTable";
SqlCommand sCmd = new SqlCommand(sqlQuery, conn);
SqlDataAdapter sDA = new SqlDataAdapter(sCmd);
DataSet dataSet = new DataSet();
conn.Open();
sDA.Fill(dataSet);
conn.Close();
for (int i = 0; i < payload.Rows.Count; i++)
{
dataSet.Tables[0].ImportRow(payload.Rows[i]);
}
// CODE CHECKPOINT #2
SqlCommandBuilder cb = new SqlCommandBuilder(sDA);
conn.Open();
sDA.Update(dataSet);
conn.Close();
}
The problem is that this code is not working. If I were to check the contents of DataTable 'payload' at Checkpoint #1, I see 4 rows, while there are 0 rows in dataSet.Tables[0]. If I then check the contents of 'dataSet' at Checkpoint #2, I see 4 rows in dataSet.Tables[0]! However, at the end of the program, none of the rows from TestTable2 have made it into TestTable.
In other words, C# is moving the data between the DataTables, but it is having no affect on the tables themselves.
I've found that adding newly created rows
DataRow row = DataSet.DataTable.NewRow(...);
...
dataSet.Tables[i].Rows.Add(row);
into the destination DataSet works with SqlDataAdapter.Update, but that's not appropriate in this case, as you cannot use DataTable.Rows.Add on a row held by a separate DataTable. Hence, my problem, as this has rendered me incapable of transfering data from one table to another, especially in cases where large numbers of column are involved, rendering explicit SQL commands very clunky.
Is there something I'm missing in this code that I'm not seeing? If so, what is it?
Thanks.
Try DataSet.AcceptChanges() before updating data to the DB. Also, check, what the DataSet.GetChanges() method returns. It should not be an empty DataSet.
Finally, I have found the solution here:
importrow --> update fails
The ultimate answer is a translation of the link provided by platon. However, I'm including the explicit code here for easier future reference.
Basically, ImportRow doesn't always set the dirty bit for the added rows. As a result, SqlDataAdapter.Update won't necessarily move the changes to SQL since it doesn't recognize the new rows as changed data. To insure that the dirty bit is set, you need to call DataTable.Rows.Add, but as mentioned this cannot be called using the row of another table as a parameter.
What to do? Don't use the row as a parameter to Add(). Instead, use the object[] ItemArray...
for(int i = 0 ; i < srcTable.Rows.Count ; i++)
{
destTable.Rows.Add(srcTable.Rows[i].ItemArray);
}
...
SqlCommandBuilder sCB = new SqlCommandBuilder(adapter);
adapter.Update(dataSet);

how to compare elements in a string with the database table values

In my project i have to give a string input through a text field, and i have to fill a database table with these values. I should first check the values of a specific table column, and add the input string only if it is not there in the table already.
I tried to convert the table values to a string array, but it wasn,t possible.
If anyone have an idea about this, your reply will be really valuable.
Thankx in advance.
Since you say your strings in the database table must be unique, just put a unique index on that field and let the database handle the problem.
CREATE UNIQUE INDEX UIX_YourTableName_YourFieldName
ON dbo.YourTableName(YourFieldName)
Whenever you will try to insert another row with the same string, SQL Server (or any other decent RDBMS) will throw an exception and not insert the value. Problem solved.
If you need to handle the error on the front-end GUI already, you'll need to load the existing entries from your database, using whatever technology you're familiar with, e.g. in ADO.NET (C#, SQL Server) you could do something like:
public List<string> FindExistingValues()
{
List<string> results = new List<string>();
string getStringsCmd = "SELECT (YourFieldName) FROM dbo.YourTableName";
using(SqlConnection _con = new SqlConnection("your connection string here"))
using(SqlCommand _cmd = new SqlCommand(getStringsCmd, _con)
{
_con.Open();
using(SqlDataReader rdr = _con.ExecuteReader())
{
while(rdr.Read())
{
results.Add(rdr.GetString(0));
}
rdr.Close();
}
_con.Close();
}
return results;
}
You would get back a List<string> from that method and then you could check in your UI whether a given string already exists in the list:
List<string> existing = FindExistingValues();
if(!existing.Contains(yournewstring))
{
// store the new value to the database
}
Or third option: you could write a stored procedure that will handle the storing of your new string. Inside it, first check to see whether the string already exists in the database
IF NOT EXISTS(SELECT * FROM dbo.YourTableName WHERE YourFieldName = '(your new string)')
INSERT INTO dbo.YourTableName(YourFieldName) VALUES(your-new-string-here)
and if not, insert it - you'll just need to find a strategy how to deal with the cases where the new string being passed in did indeed already exist (ignore it, or report back an error of some sorts).
Lots of options - up to you which one works best in your scenario!

Categories