Should I execute every query or single query? - c#

I'm trying to Insert multiple records to table using ado.net, and print id inserted.
My code is like this:
List<string> listQuery = new List<string>()
{
"INSERT INTO Students (Name) VALUES ('student1');SELECT ##Identity;",
"INSERT INTO Students (Name) VALUES ('student2');SELECT ##Identity;",
"INSERT INTO Students (Name) VALUES ('student3');SELECT ##Identity;",
};
using (SqlConnection connection = new SqlConnection(_connectionString))
{
try
{
connection.Open();
using (SqlCommand command = new SqlCommand())
{
command.Connection = connection;
foreach (var myQuery in listQuery)
{
command.CommandText = myQuery;
int id = Convert.ToInt32((decimal)command.ExecuteScalar());
Console.WriteLine("Inserted: " + id);
}
}
}
catch (Exception ex)
{
throw ex;
}
finally
{
//Close and dispose
connection.Close();
}
}
I wondering, whether should I execute every command like that? Or concatenate all query and execute just a times?.
If I should execute one times. How can i get all id of records inserted?

you can use the OUTPUT clause to return the identity id
INSERT INTO Students (Name)
OUTPUT INSERTED.id
VALUES ('student1'), ('student2'), ('student3');

Don't go like this, and DON'T call database in ANY loop.
For resolving your problem, you should write stored procedure that take a DataTable as input of your student list (tutorial) or use JSON string as input. In that stored procedure you use OUTPUT clause to retrieve id: OUTPUT INSERTED.id . On C# code, you only need ExecuteReader to get all id.

Related

Insert to MySql from c# failed

I have a problem with a MySql insert ... this is my Code:
public class struc
{
public string Product;
public string Underproduct;
public string Version;
}
static void DatabaseConection(List<struc> Data)
{
string connString = "right connection info";
string insertQuery = "Insert into freigabedaten (produktname,unterprodukt,version,freigabestatus) values (productInfo.Product,productInfo.Underproduct,productInfo.Version,'4')";
MySqlConnection conn = new MySqlConnection(connString);
conn.Open();
foreach (var productInfo in Data)
{
MySql.Data.MySqlClient.MySqlCommand Command = new MySql.Data.MySqlClient.MySqlCommand(insertQuery, conn);
try
{
Command.ExecuteNonQuery();
}
catch (Exception)
{
throw;
}
});
conn.Close();
}
But i get always the Exeption:
MySql.Data.MySqlClient.MySqlException: "Unknown column
'productInfo.Product' in 'field list'"
My Database table structure is:
Databasetablescreen
can someone help me please?
Seems that you're passing all INSERT query arguments as part of query string, not as reference to productInfo object which contains column names (which they're treated as table names instead).
Use a parameterized MySQL query like this:
string insertQuery = "Insert into freigabedaten (produktname,unterprodukt,version,freigabestatus) values (#produktname,#underprodukt,#version,'4')";
And then declare input parameters for MySqlCommand inside foreach loop before using ExecuteNonQuery method:
MySql.Data.MySqlClient.MySqlCommand Command = new MySql.Data.MySqlClient.MySqlCommand(insertQuery, conn);
Command.Parameters.AddWithValue("#produktname", productInfo.Product);
Command.Parameters.AddWithValue("#unterprodukt", productInfo.Underproduct);
Command.Parameters.AddWithValue("#version", productInfo.Version);
Command.ExecuteNonQuery();
What are you trying to do?
If productInfo is a C# Struct/Class you need to add the values manually to the Query string.
string insertQuery = "Insert into freigabedaten (produktname,unterprodukt,version,freigabestatus) values ('"+productInfo.Product+"','"+productInfo.Underproduct+"','"+productInfo.Version+"','4')";
If productInfo is another table you'll need to query these values beforehand.

How do i display a select stored procedure result in a textbox?

I need to display the result from a select statement in a stored procedure onto the textbox and I can't figure out how to do it. The select statement doesn't use a WHERE clause. The stored procedure goes
CREATE PROCEDURE NewCustomer
AS
BEGIN
SELECT MAX(ID) + 1 FROM Database
END
This is what I've tried
protected void btnNew_Click(object sender, EventArgs e)
{
Clear();
int num;
try
{
using (SqlCommand command = new SqlCommand("NewCustomer"))
{
command.Connection = conn;
command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("#CustID", SqlDbType.Int).Value = Int32.TryParse(txtCID.Text, out num); // Use tryparse if needed
conn.Open();
txtCID.Text = (string)command.ExecuteScalar();
}
}
catch (Exception ex)
{
lblMessage.Text = ex.Message;
}
}
It gives me a "Procedure NewCID has no parameters and arguments were supplied." Error
You are not executing the procedure that you ware given. The procedure is named as yadayada(The worst name that you can give) and you are executing the procedure NewCustomer as the command text. Both has to be same. Then you are using the Wrong statement for executing the query.
The ExecuteNonQuery to perform catalog operations (for example,
querying the structure of a database or creating database objects such
as tables), or to change the data in a database without using a
DataSet by executing UPDATE, INSERT, or DELETE statements.
But you are using it for executing the select query. Here you are selecting a single value from the table so the ExecuteScalar will be the best option for you. Your code will be like this: assume the procedure name is GetNewCustomerID;
using (SqlCommand exeCommand = new SqlCommand("GetNewCustomerID"))
{
exeCommand.Connection = conn;
exeCommand.CommandType = CommandType.StoredProcedure;
exeCommand.Parameters.Add("#CustID",SqlDbType.Int).Value=Convert.ToInt32(txtCID.Text); // Use tryparse if needed
conn.Open();
txtCID.Text = (string)exeCommand.ExecuteScalar();
}

Get last inserted Id returns 0 if the row already exists

When I use:
START TRANSACTION;
INSERT IGNORE INTO users (Name, ...) VALUES ('user1',..) ON DUPLICATE KEY UPDATE lastSeen=NOW();
SELECT LAST_INSERT_ID();
COMMIT;
When i run this query from my sql client I get the last inserted id as expected.
But what if the user already exists?(table uniquness). So no new id is created. But now when I run the same query from my sql client still i get the id as expected.
But! when I run it from my C# code and use mysql.reader to query the result I receive 0. Why is that??
If instead of SELECT LAST_INSERT_ID() I use SELECT id FROM users ORDER BY id DESC LIMIT 1 I get the right id again.
EDIT!
This is not a duplicate of the suggested topic. In that topic the answer says that the auto-incremented is not the primary key, thats not my case! also as I mark it does return the right id when i use the mysql client! The problem occures only when I run it from my c# code!
My C# code:
using (var conn = new MySqlConnection(connString))
{
try
{
conn.Open();
var command = conn.CreateCommand();
command.CommandText = strSQL;
MySqlDataReader reader;
string result = "";
try
{
reader = command.ExecuteReader();
if (reader.Read())
result = reader[0].ToString();
}
catch (Exception ex)
{
throw new MySqlException("SQL Error: " + ex.ToString());
}
reader.Close();
return result;
}
}
I have found a work around, simple CASE clause does the job:
START TRANSACTION;
INSERT IGNORE INTO users (Name, ...) VALUES ('user1',..) ON DUPLICATE KEY UPDATE lastSeen=NOW();
SELECT CASE WHEN LAST_INSERT_ID()=0 THEN (SELECT id FROM users WHERE Name = 'user1') ELSE LAST_INSERT_ID() END;
COMMIT;
Now in case the user exists, we simply query the id, If it doest not exists, the LAST_INSERT_ID() will give us the right id
From what I read there https://stackoverflow.com/a/15057619/4421474
Your code could be transformed to:
using (var conn = new MySqlConnection(connString))
{
conn.Open();
var command = conn.CreateCommand();
command.CommandText = strSQL; <-- just insert part like "START ... INSERT ... COMMIT;"
command.ExecuteNonQuery();
string result = command.LastInsertedId;
return result;
}
Sorry I am not c# expert so some syntax could be broken.
I guess this answer:
Just my 50 cents for this issue, I simply noticed that you won't get a LAST_INSERT_ID greater than 0 if your table has no AUTO_INCREMENT set to an index
that i found here MySQL: LAST_INSERT_ID() returns 0 is the good one.

Insert multiple entries in parameterized query

I'm trying to do a multiple insert query using a parameterized command but I am getting a syntax exception thrown. The query in sql would look like:
CREATE TEMPORARY TABLE IF NOT EXISTS TEMP_PTO (
ng_id INT,
requested_hours DECIMAL(7,4)
);
INSERT INTO TEMP_PTO VALUES
(1, 0.0000), (2, 1.5000);
I generate it in C# via the following where ptoHours is an IEnumerable of a class containing the ng_id and the requested_hours. Queries is an resx file containing the query text, and Queries.PTOLoadTempCommand = INSERT INTO TEMP_PTO VALUES #v;:
using (var conn = new MySqlConnection(this.connString))
{
MySqlTransaction trans = null;
try
{
await conn.OpenAsync();
trans = conn.BeginTransaction();
using (var tempCommand = conn.CreateCommand())
{
tempCommand.CommandText = Queries.PTOTempTableCommand;
await tempCommand.ExecuteNonQueryAsync();
}
using (var loadCommand = conn.CreateCommand())
{
loadCommand.CommandText = Queries.PTOLoadTempCommand; //INSERT INTO TEMP_PTO VALUES #v;
loadCommand.Parameters.AddWithValue("#v", String.Join(", " ,ptoHours.Select(p => String.Format("({0}, {1:N4})", p.AgentId, p.UsedVacationHours))));
//Exception thrown here
var affected = await loadCommand.ExecuteNonQueryAsync();
if (affected != entryCount)
throw new Exception("Not All Entries Loaded");
}
trans.Commit();
}
catch (Exception e)
{
if(trans!= null) trans.Rollback();
}
}
Looking at the loadCommand object, I can see that the value of the parameter #v is (1, 0.0000), (2, 1.5000). Are you able to do an insert like this or do I need to modify the function to insert them one at a time? I know I could go the StringBuilder route, but then I cannot use the safety of the parameterization.
You will have to do it one at a time. The query parameters are parsed and it is not allowed to have a parameter in that position.
There shouldn't be a need to insert rows in one command, if you need both to succeed or fail, just use transactions.
And as you said, using a StringBuilder or other string catenations is not smart.

Return value from SQL Server Insert command using c#

Using C# in Visual Studio, I'm inserting a row into a table like this:
INSERT INTO foo (column_name)
VALUES ('bar')
I want to do something like this, but I don't know the correct syntax:
INSERT INTO foo (column_name)
VALUES ('bar')
RETURNING foo_id
This would return the foo_id column from the newly inserted row.
Furthermore, even if I find the correct syntax for this, I have another problem: I have SqlDataReader and SqlDataAdapter at my disposal. As far as I know, the former is for reading data, the second is for manipulating data. When inserting a row with a return statement, I am both manipulating and reading data, so I'm not sure what to use. Maybe there's something entirely different I should use for this?
SCOPE_IDENTITY returns the last identity value inserted into an identity column in the same scope. A scope is a module: a stored procedure, trigger, function, or batch. Therefore, two statements are in the same scope if they are in the same stored procedure, function, or batch.
You can use SqlCommand.ExecuteScalar to execute the insert command and retrieve the new ID in one query.
using (var con = new SqlConnection(ConnectionString)) {
int newID;
var cmd = "INSERT INTO foo (column_name)VALUES (#Value);SELECT CAST(scope_identity() AS int)";
using (var insertCommand = new SqlCommand(cmd, con)) {
insertCommand.Parameters.AddWithValue("#Value", "bar");
con.Open();
newID = (int)insertCommand.ExecuteScalar();
}
}
try this:
INSERT INTO foo (column_name)
OUTPUT INSERTED.column_name,column_name,...
VALUES ('bar')
OUTPUT can return a result set (among other things), see: OUTPUT Clause (Transact-SQL). Also, if you insert multiple values (INSERT SELECT) this method will return one row per inserted row, where other methods will only return info on the last row.
working example:
declare #YourTable table (YourID int identity(1,1), YourCol1 varchar(5))
INSERT INTO #YourTable (YourCol1)
OUTPUT INSERTED.YourID
VALUES ('Bar')
OUTPUT:
YourID
-----------
1
(1 row(s) affected)
I think you can use ##IDENTITY for this, but I think there's some special rules/restrictions around it?
using (var con = new SqlConnection("connection string"))
{
con.Open();
string query = "INSERT INTO table (column) VALUES (#value)";
var command = new SqlCommand(query, con);
command.Parameters.Add("#value", value);
command.ExecuteNonQuery();
command.Parameters.Clear();
command.CommandText = "SELECT ##IDENTITY";
int identity = Convert.ToInt32(command.ExecuteScalar());
}

Categories