How to execute Multiple Insert statements in a single query? - c#

I'm making a form where a user answers some questions to make a pricehold. My problem is I can't store the data from the questions into more than one sql table.
I have tried inserting the other table into the sql command (shown below) and I have tried making another sql command that basically says the same thing with a different name but splitting the name and phone number into the first one and the date created and pick up date into the second one but that only runs the first sql command and then stops so data is never stored into the second table
private void AddPhBttn_Click(object sender, RoutedEventArgs e)
{
SqlConnection furniture = new SqlConnection("Data Source=LAPTOP-F4QFMPFD\\MSSQLSERVER1;Initial Catalog=Furniture;Integrated Security=True");
furniture.Open();
SqlCommand add = new SqlCommand("insert into Customers(Name, Phone) PriceHold(DateCreated, PickUpDate) values ('" + nameTxtBox.Text + "', '" + phoneTxtbox.Text + "', '" + dateTxtBox.Text + "', '" + puDateTxtBox.Text + "')", furniture);
int i = add.ExecuteNonQuery();
if (i != 0)
{
MessageBox.Show("saved");
}
else MessageBox.Show("error");
}

As #Caius Jard said, you can't do this with an ad-hoc query.
So what is an option to do so?
Step 1: Create a Stored Procedure in the Database:
CREATE PROCEDURE usp_InsertData
#Name NVARCHAR(200),
#Phone NVARCHAR(100),
#DateCreated Date,
#PickUpDate Date
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO Customers(Name, Phone) VALUES (#Name,#Phone)
INSERT INTO PriceHold(DateCreated, PickUpDate) VALUES (#DateCreated,#PickUpDate)
END
Step 2: Call above Stored procedure in C# Code:
private void AddPhBttn_Click(object sender, RoutedEventArgs e)
{
var furniture = new SqlConnection("Data Source=LAPTOP-F4QFMPFD\\MSSQLSERVER1;Initial Catalog=Furniture;Integrated Security=True");
SqlCommand add = new SqlCommand("usp_InsertData", furniture);
add.CommandType = System.Data.CommandType.StoredProcedure;
add.Parameters.AddWithValue("#Name", nameTxtBox.Text);
add.Parameters.AddWithValue("#Phone", phoneTxtbox.Text);
add.Parameters.AddWithValue("#DateCreated", dateTxtBox.Text);
add.Parameters.AddWithValue("#PickUpDate", puDateTxtBox.Text);
furniture.Open();
int i = add.ExecuteNonQuery();
if (i != 0)
{
MessageBox.Show("saved");
}
else
{
MessageBox.Show("error");
}
furniture.Dispose();
}

You can't do this in SQL
INSERT INTO
myfirsttable(column1, column2)
mysecondtable(column3, column4, column5)
VALUES(value1, value2, value3, value4)
It's flat out a syntax error. Only one table may appear in an insert. The number of values inserted must match the number of columns
If you want to insert into two tables, run two separate inserts from your c# code
Finally, have a long read of http://bobby-tables.com - your code is currently highly insecure and while this may not matter right now because it's just some small test app, it is best to avoid embarking on a learning path that includes coding in this way. As a recruiter I've turned down many job candidates who have written SQL like this and I'd never employ someone who demonstrated this style to me

When working with data in more than one table, if you want to ensure either all insert/update/delete complete successfully or none of them are applied on your data to ensure data integrity, use transactions. I think SqlTransaction is what you're after. Read about it here.
For your specific case, this is one possibility:
private void AddPhBttn_Click(object sender, RoutedEventArgs e)
{
// Necessary input validation to collect and data from input fields. Good practice to avoid SQL injection.
AddFurniture(nameTxtBox.Text, phoneTxtbox.Text, dateTxtBox.Text, puDateTxtBox.Text);
}
private void AddFurniture(string name, string phoneNumber, string createdDate, string pickupDate)
{
string connectionString = "Data Source=LAPTOP-F4QFMPFD\\MSSQLSERVER1;Initial Catalog=Furniture;Integrated Security=True"; // This should ideally come from some configuration.
using(SqlConnection connection = new SqlConnection(connectionString))
{
SqlCommand command = connection.CreateCommand();
SqlTransaction transaction = connection.BeginTransaction("Add Furniture");
command.Connection = connection;
command.Transaction = transaction;
try
{
connection.Open();
command.CommandText = $"insert into Customers (Name, Phone) values ({name}, {phoneNumber});";
command.ExecuteNonQuery();
command.CommandText = $"insert into PriceHold (DateCreated, PickUpDate) values ({createdDate}, {pickupDate});";
command.ExecuteNonQuery();
// Try to commit to database.
// Both the above queries are executed at this point. If any one of them fails, 'transaction' will throw an exception.
transaction.Commit();
}
catch (Exception ex1)
{
// Considering the statements executed using the 'transaction' for this 'connection',
// one of the insert operations have clearly failed.
// Attempt to roll back the change which was applied.
MessageBox.Show($"Insert failed. Trying to roll back {ex1.Message}");
try
{
transaction.RollBack();
}
catch (Exception ex2)
{
// Rollback also failed. Possible data integrity issue. Handle it in your application.
MessageBox.Show($"Roll back failed. {ex2.Message}");
}
}
}
}

Related

Insert new row to Database table if two fields does not match, otherwise sum value in certain column

I have data in Database table:
Here is the method for adding data:
public static void AddRecordToDatatable(string WindowTitle, int TimeSpent,
DateTime DateToday, string Project, string Username)
{
string sql = #"INSERT INTO dbo.Log (WindowTitle,TimeSpent,DateToday,Project,Username)" +
" VALUES (#WindowTitle,#TimeSpent,#DateToday,#Project,#Username)";
// Create the connection (and be sure to dispose it at the end)
using (SqlConnection cnn = new SqlConnection(DBconnectionString))
{
try
{
// Open the connection to the database.
// This is the first critical step in the process.
// If we cannot reach the db then we have connectivity problems
cnn.Open();
// Prepare the command to be executed on the db
using (SqlCommand cmd = new SqlCommand(sql, cnn))
{
// Create and set the parameters values
cmd.Parameters.Add("#WindowTitle", SqlDbType.NVarChar).Value = WindowTitle;
cmd.Parameters.Add("#TimeSpent", SqlDbType.Int).Value = TimeSpent;
cmd.Parameters.Add("#DateToday", SqlDbType.DateTime).Value = DateTime.Now.Date;
cmd.Parameters.Add("#Project", SqlDbType.NVarChar).Value = Project;
cmd.Parameters.Add("#Username", SqlDbType.NVarChar).Value = Username;
// Let's ask the db to execute the query
int rowsAdded = cmd.ExecuteNonQuery();
if (rowsAdded > 0)
{
//MessageBox.Show("Row inserted");
}
else
{
// This should never really happen, but let's leave it here
//MessageBox.Show("No row inserted");
}
}
cnn.Close();
}
catch (Exception ex)
{
// We should log the error somewhere,
// for this example let's just show a message
MessageBox.Show("ERROR:" + ex.Message);
}
}
}
How it is possible to check for existing record before inputting data to Database table and sum on certain value if it exists?
So basically check if WindowTitle = WindowTitle and DateToday = DateToday, if these two match, then take TimeSpent and sum it to existing TimeSpent in Database Table without inputting a new row.
I have tried to test ON DUPLICATE KEY UPDATE WindowTitle = #WindowTitle, DateToday = #DateToday after INSERT but Visual Studio is giving an error in Debugger for such a command pointing to ON (Incorrect syntax near ON). Also I am not sure if ON DUPLICATE is the best approach for this kind of case.
You need to expand your SQL to check for the existence of the record you think could exist.
IF EXISTS (SELECT 1 FROM dbo.Log WHERE WindowTitle = #WindowTitle AND DateToday = #DateToday)
BEGIN
--UPDATE HERE
END
ELSE
BEGIN
-- INSERT HERE
END
Alternatively you can create a query method and call that first, before calling AddRecordToDatatable
Personally I would do all of these CRUD operations using an ORM such as EF Core or preferably, NHibernate. But this all depends on requirements, limitations etc.

sql syntax error - not able to append string to a database

I am currently working on a dummy project in which I am making a login screen. I don't have any big intentions with the project, beside learning some C# and sql.
I am currently trying append a new user to the database which contains each username and their password, but I am for some reason getting an error message.
The entry written in the textbox should be stored in the database, but for some reason is this not happening..
I am getting an error stating I have a syntax error which I am not sure i understand.
private void create_user_username_box_Leave(object sender, EventArgs e)
{
// Add user/password to database when when someone leaves the area.
using (DbConnection connection = new SqlConnection(#"Server=localhost\SQLEXPRESS01;Database=master;Trusted_Connection=True;"))
{
connection.Open();
using (DbCommand command = new SqlCommand("INSERT INTO [dbo].[information] (id,password) VALUES ("+create_user_username_textbox.Text+","+create_user_password_textbox.Text+");"))
{
command.Connection = connection;
command.ExecuteNonQuery(); // System.Data.SqlClient.SqlException: 'Incorrect syntax near ')'.'
}
}
}
Do not do the following, ever
"INSERT INTO [dbo].[information] (id,password)
VALUES (" + someStringVariable + "," + someOtherStringVariable + ")"
Just think about what you're doing here - you're putting whatever text the user entered directly into your query string. This is the easiest way to have your database dropped or all the information it contains stolen.
Instead, use prepared statements
var commandText = "INSERT INTO [dbo].[information] (id,password) VALUES (#Username, #Password)"
using (var command = new SqlCommand(commandText, connection))
{
command.Parameters.Add("#Username", SqlDbType.VarChar).Value = create_user_username_textbox.Text
command.Parameters.Add("#Password", SqlDbType.VarChar).Value = create_user_password_textbox.Text
command.ExecuteNonQuery();
}
You should also strongly consider NOT storing passwords in plain text
Updated with suggestion to replace Parameters.AddWithValue - obviously if the column type on your database is different, set it accordingly
The values are strings so the resulting SQL command text should enclose them within single quotes.
VALUES ('"+create_user_username_textbox.Text+"','"...
However, you should really parameterise the query to prevent the potential for Sql injection attacks.
Change the string to:
VALUES (#id,#pw)"))
Add parameters to the command:
command.Parameters.Add(new SqlParameter("#id", create_user_username_textbox.Text));
command.Paramaters.Add(new SqlParameter("#pw", create_user_password_textbox.Text));
try this -
private void create_user_username_box_Leave(object sender, EventArgs e)
{
// Add user/password to database when when someone leaves the area.
using (SqlConnection connection = new SqlConnection(#"Server=localhost\SQLEXPRESS01;Database=master;Trusted_Connection=True;"))
{
connection.Open();
using (SqlCommand command = new SqlCommand("INSERT INTO [dbo].[information] (id,password) VALUES ("+create_user_username_textbox.Text+","+create_user_password_textbox.Text+");"))
{
command.Connection = connection;
command.ExecuteNonQuery(); // System.Data.SqlClient.SqlException: 'Incorrect syntax near ')'.'
}
}
}

ASP.NET Not sure if reading from SQL Server database correctly

I am trying to make a user register page that uploads the user data to a sql server database. I want to have the capability to check if a username already exists and prevent it from being made. I am able to create a new user with first name, last name, username, etc and it updates the database, but it doesn't stop me from creating a user with a username that already exists in the database. Here is my code:
protected void Page_Load(object sender, EventArgs e)
{
if (IsPostBack)
{
//connect registration form to database
SqlConnection conn = new SqlConnection(ConfigurationManager.ConnectionStrings["sandboxConnectionStringUserData"].ConnectionString);
conn.Open(); //open connection
//SQL Query
string checkUserName = "select count(*) from UserData where UserName='"+TextBoxUserName.Text+"'";//checks if username is already there
SqlCommand command = new SqlCommand(checkUserName, conn);
int temp = Convert.ToInt32(command.ExecuteScalar().ToString());
if(temp == 1)
{
Response.Write("User name already exists");
}
conn.Close(); //close the database
}
}
I tried debugging and temp's value never changes to 1.
Thanks!
Just add a UNIQUE constraint to the username column and handle the sql exception in your app. Additionally you can/should write an SP that takes username as an argument and checks for existence in the table, and use that in your server-side validation after the form is sorted but before the insert (save()) occurs. That way you reduce the probability of encountering a sql exception but can still deal with it if it occurs.
Your current method of appending the form data to a raw sql query is going to open you up to sql injection. Use a parameterized query instead.
Creating a UNIQUE constraint on the UserName column is a good start. I would also create a stored procedure that checks the existence of the user and inserts or updates as well structure your code a bit more efficiently. The username should be passed in as a parameter and you should properly dispose of the connection object.
As an example your stored procedure may look like:
CREATE PROCEDURE dbo.uspUserData #userName VARCHAR(50)
AS
BEGIN
IF EXISTS(SELECT 1 FROM dbo.UserData WITH(NOLOCK)
WHERE UserName = #userName)
BEGIN
-- update
END
ELSE
BEGIN
-- insert
END
END
And your .NET code may look like:
using (
SqlConnection conn =
new SqlConnection(
ConfigurationManager.ConnectionStrings["sandboxConnectionStringUserData"].ConnectionString))
{
using (SqlCommand cmd = new SqlCommand("uspUserData", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("#UserName", SqlDbType.VarChar).Value = TextBoxUserName.Text;
conn.Open();
cmd.ExecuteNonQuery();
}
}

Issues with Increment MS-SQL, C#

I am having an issue with the increment for the ID. The ID would increase by one every time I click insert, but the problem occurs when the ID 2, it would insert the values twice, if ID 3, it would insert the values three times, and so on.
There are couple of options that I have been trying. One is Max and the other one is finding the last inserted value and add one to the ID just.
I would appreciate if anyone can help me out with this. Thanks
public partial class LoginInfo : System.Web.UI.Page
{
static string myConnectionString = ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString;
private void GenerateID()
{
SqlConnection myConnection = new SqlConnection(myConnectionString);
string myQuery1 = "Select Count(S_ID) from Student_Name";
SqlCommand cmd = new SqlCommand(myQuery1, myConnection);
myConnection.Open();
int addOneS_ID_Table1 = Convert.ToInt32(cmd.ExecuteScalar());
myConnection.Close();
addOneS_ID_Table1++;
lblstdID.Text = addOneS_ID_Table1.ToString();
myConnection.Open();
cmd.CommandText = "Select Count(P_ID) from Student_Pass";
int addOneP_ID_Table2 = Convert.ToInt32(cmd.ExecuteScalar());
myConnection.Close();
addOneP_ID_Table2++;
lblstdPass.Text = addOneP_ID_Table2.ToString();
/*-----------------------------------------------------------------*/
//SqlConnection myConnection = new SqlConnection(myConnectionString);
//SqlCommand cmd = new SqlCommand("SELECT MAX(S_ID) as max_S_ID from Student_Name",myConnection);
//cmd.CommandType = CommandType.Text;
//myConnection.Open();
//lblstdID.Text = Convert.ToString(cmd.ExecuteScalar());
//cmd.CommandText = "SELECT MAX(P_ID) as max_P_ID FROM Student_Pass";
//lblstdPass.Text = Convert.ToString(cmd.ExecuteScalar());
//myConnection.Close();
}
protected void Page_Load(object sender, EventArgs e)
{
if(!IsPostBack)
{
GenerateID();
}
}
protected void btnInsert_Click(object sender, EventArgs e)
{
SqlConnection myConnection = new SqlConnection(myConnectionString);
string myQuery = "Insert into Student_Name(S_ID,STUDENT_NAME) VALUES" + "(#S_ID,#STUDENT_NAME)";
SqlCommand cmd = new SqlCommand(myQuery,myConnection);
cmd.Parameters.Add("#S_ID", SqlDbType.Int).Value = lblstdID.Text;
cmd.Parameters.Add("#STUDENT_NAME", SqlDbType.VarChar).Value = txtstdName.Text;
if(myConnection.State == ConnectionState.Closed)
{
myConnection.Open();
}
cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
//Second Table
cmd.CommandText = "Insert into Student_Pass(P_ID,PASSWORD) VALUES" + "(#P_ID,#PASSWORD)";
cmd.CommandType = CommandType.Text;
cmd.Parameters.Add("#P_ID", SqlDbType.Int).Value = lblstdPass.Text;
cmd.Parameters.Add("#PASSWORD", SqlDbType.VarChar).Value = txtStdPass.Text;
cmd.ExecuteNonQuery();
cmd.Parameters.Clear();
myConnection.Close();
GenerateID();
lblResult.Text = "Successfully Saved";
GridView1.DataBind();
}
}
Problem is with your query since you are getting COUNT(S_ID) which is going to get you count of records doesn't necessarily will give exact ID number. You should rather try MAX(S_ID) or ORDER BY clause saying
Select MAX(S_ID) from Student_Name
(OR)
Select TOP 1 S_ID from Student_Name ORDER BY S_ID DESC;
But recommended, You should actually go with SQL Server ##IDENTITY or SCOPE_IDENTITY() to get the last inserted record ID (assuming that S_ID is an IDENTITY column)
It's highly recommended to not use max or top in order to determine the "next" identifier to use, simply because of the cost associated with it.
However, there are some other pitfalls to using max and top especially if there is a chance that nolock is used (which is a whole other conversation). I've seen a lot of web applications use max and has proven to be a performance killer.
Rahul is right, ##identity or scope_identity are good alternatives. However, I think this calls for using a native SQL Server sequence, which was introduced in SQL Server 2012. It was something that application developers have been waiting for and Microsoft finally delivered.
The issue with using ##identity or scope_identity is that you actually have to write rows to some table before you can even contemplate doing something.
This makes it a bit more costly and messier than what it may need to be. In the case of using a sequence, you can issue a new sequence number and then decide what to do and once you decide what to do you're still guaranteed that you're the only one with that sequence number.
You would create a sequence like this. You should check out the documentation as well.
create sequence dbo.StudentIdSeq
as int -- this can be any integer type
start with 1 -- you can start with any valid number in the int, even negative
increment by 1;
go
Then you issue new sequence numbers by doing this ...
select next value for StudentIdSeq;
It may still be good to create a stored procedure with an output parameter that you can call from C# (which is what I would do). In fact you may want to take it a step further, in the case that you have a bunch of sequences, and create a slick stored procedure that will get a new sequence based on the type that is being requested from the caller.

How to execute a TSQL input command in C# which includes a string with "-", "\" or ";"

I have a SqlCommand that attempts to insert a row in a SQL Server database table. The column of interest is a nvarchar(100) and the data that needs to be input will include characters such as "-", ";" and "\". When I insert a string without these characters everything works fine. When I attempt to insert a string that includes these characters the code fails because these characters are literally understood by the code and thus reports a syntax error. I have resolved such an issue in TSQL alone using dynamic sql, however I cannot find any good references to perform this action in C#. I suppose I could create a stored procedure and pass the values, but is there a way in which I could efficiently perform this using C# alone? If so, How? Or is passing values to a Stored Procedure a better approach?
Here is a simplified version of the code:
String SQLServerInstanceNames = "ussqlclus-db43\ussqlclusdb43; ussqlclus-db44\ussqltrysdb44; ussqltrys-db45\ussqltrysdb45;"
//Create Connection (Get Connection string from Server Explorer)
SqlConnection myConnection = new SqlConnection("Data Source=SERVER1;Initial Catalog=Database1;Integrated Security=True");
//Open connection
try { myConnection.Open(); }
catch (Exception e) { Console.WriteLine(e.ToString()); }
SqlCommand myCommand = new SqlCommand("INSERT INTO [dbo].[Table1]" +
"([SQLServerInstanceNames])" +
"VALUES (SQLServerInstanceNames);", myConnection);
//Execute command
myCommand.ExecuteNonQuery();
//Close connection
try { myConnection.Close(); }
catch (Exception e) { Console.WriteLine(e.ToString()); }
Try with SqlParameters. It will save you from Sql Injection as well as from your current problem.
myCommand.Parameters.AddWithValue("#param1", myValueWithCharacters);
C# uses \ as a control character. You can ignore those by prepending the string with an # character:
String SQLServerInstanceNames = #"ussqlclus-db43\ussqlclusdb43; ussqlclus-db44\ussqltrysdb44; ussqltrys-db45\ussqltrysdb45;"
Just update your code like this to include parmeters in INSERT statement
SqlCommand myCommand = new SqlCommand("INSERT INTO [dbo].[Table1]" +
"([SQLServerInstanceNames])" + "VALUES (#SQLServerInstanceNames);", myConnection);
myCommand.Parameters.AddWithValue("#SQLServerInstanceNames", "instance name");
Notice I updated VALUES part and added #SQLServerInstanceNames – this is how you add parameters to your query.
Now that you use parameters you won’t have to worry about special characters. These will be handled automatically.

Categories

Resources