I've created a UWP app with a sqlite database using the Microsoft.Data.Sqlite library. After inserting a new row, I need to know the autoincrement value assigned to the new row. The code below uses the additional query "select last_insert_rowid()", but is there a better way to obtain this value?
public static int AddMovie(Movie movie)
{
string dbpath = Path.Combine(ApplicationData.Current.LocalFolder.Path, DB_FILENAME);
using (SqliteConnection db = new SqliteConnection($"Filename={dbpath}"))
{
db.Open();
SqliteCommand cmd = new SqliteCommand();
cmd.Connection = db;
cmd.CommandText = "INSERT INTO Movie VALUES (NULL, #Title, #Rating)";
cmd.Parameters.AddWithValue("#Title", movie.Title);
cmd.Parameters.AddWithValue("#Rating", movie.Rating);
cmd.ExecuteReader();
// Is there a better way to get this value?
cmd = new SqliteCommand("SELECT last_insert_rowid()", db);
SqliteDataReader query = cmd.ExecuteReader();
var newId = 0;
if (query.Read())
{
newId = query.GetInt32(0);
}
db.Close();
return newId;
}
}
You could combine both queries into a single command, and instead of calling cmd.ExecuteReader() call cmd.ExecuteScalar() and convert the returned value to int:
public static long AddMovie(Movie movie)
{
string dbpath = Path.Combine(ApplicationData.Current.LocalFolder.Path, DB_FILENAME);
using (SqliteConnection db = new SqliteConnection($"Filename={dbpath}"))
{
db.Open();
SqliteCommand cmd = new SqliteCommand();
cmd.Connection = db;
cmd.CommandText = "INSERT INTO Movie VALUES (NULL, #Title, #Rating); SELECT last_insert_rowid();";
cmd.Parameters.AddWithValue("#Title", movie.Title);
cmd.Parameters.AddWithValue("#Rating", movie.Rating);
return (long)cmd.ExecuteScalar();
}
}
Note: Raycoon's answer points out some drawbacks of this solution.
Your initial way of retrieving the rowid (by separating the insert and select command) is more appropriate and more "secure". Combining the INSERT and SELECT commands will ignore unique constraint violations when inserting data in your table (in case you have a unique index). You will simply get "0" as new rowid - and no exception will be thrown!
Using separate commands results in SqliteExceptions when data cannot properly be inserted in your table. Therefore I wouldn't use the second, "correct" approach. Better use your initial code - but ExecuteNonQuery for INSERT and ExecuteScalar for SELECT last_insert_rowid().
Related
I am able to insert a new row of data into my MS Access table ("Table1") from a desktop application (C#, WinForms), but I cannot figure out how to return the value of the primary key ("UniqueID") for the newly added row. I am using an OleDbConnection to perform all of my queries. I have tried using an OUTPUT clause, shown below, and also SCOPE_IDENTITY() to return the value, but I only get the exception: "Syntax error in INSERT INTO statement."
I have seen many example of how to do this using SQL Server, but is it possible to return the primary key value using Access and an OleDbConneciton?
int PrimaryKey;
using (OleDbConnection Connection = new OleDbConnection(CONNECTION_STRING))
{
Connection.Open();
OleDbCommand Command = new OleDbCommand();
Command.Connection = Connection;
Command.CommandText = String.Format(
"INSERT INTO Table1 (JobNumber, ItemNumber) " +
"OUTPUT INSERTED.UniqueID " +
"VALUES (#jobnumber, #itemnumber)");
Command.Parameters.AddRange(new OleDbParameter[]
{
new OleDbParameter("#jobnumber", JobNumber),
new OleDbParameter("#itemnumber", ItemNumber)
});
PrimaryKey = Convert.ToInt32(Command.ExecuteScalar());
}
The OUTPUT INSERTED is not a valid syntax for Jet Sql (the sql engine for Microsoft Access) and also SCOPE_IDENTITY() is not understood as well. Also Jet doesn't support batch statements. So you need to send two different commands to the engine.
int PrimaryKey;
using (OleDbConnection Connection = new OleDbConnection(CONNECTION_STRING))
{
Connection.Open();
OleDbCommand Command = new OleDbCommand();
Command.Connection = Connection;
Command.CommandText = #"INSERT INTO Table1
(JobNumber, ItemNumber)
VALUES (#jobnumber, #itemnumber)";
Command.Parameters.AddRange(new OleDbParameter[]
{
new OleDbParameter("#jobnumber", JobNumber),
new OleDbParameter("#itemnumber", ItemNumber)
});
Command.ExecuteNonQuery();
// Send the next command
Command.Parameters.Clear();
Command.CommandText = "SELECT ##IDENTITY";
PrimaryKey = Convert.ToInt32(Command.ExecuteScalar());
}
I'm beginner in SQL and C#. My question is: how do I check whether a row in the table exists or not? Here is my code that I am currently using:
var conn = new SqlConnection(#"Data Source");
conn.Open();
var cmd = new SqlCommand("INSERT INTO Results (PlayerName) VALUES (#PlayerName) " , conn);
cmd.Parameters.Add("#PlayerName",_playerName);
cmd.ExecuteNonQuery();
conn.Close();
I want to check if the player name exists or not, and if not, then add it to the table.
You can check it with Select statement first and then insert it if it is not exist.
Also use using statement to dispose your connection and commands automatically instead of calling Close method manually.
using(var conn = new SqlConnection(yourConnectionString))
using(var cmd = conn.CreateCommand())
{
cmd.CommandText = "Select Count(*) From Results Where PlayerName = #playerName";
cmd.Parameters.Add("#playerName", _playerName);
con.Open();
int count = (int)cmd.ExecuteScalar();
if(count == 0)
{
// It means it does not exist.
cmd.CommandText = "INSERT INTO Results(PlayerName) VALUES (#playerName)";
cmd.ExecuteNonQuery();
}
}
if not exists(select null from Results where PlayerName=#PlayerName) begin INSERT INTO Results (PlayerName) VALUES (#PlayerName) end
I have a table student (id, name). Then I have one textbox, for entering the name, when click on submit button, it inserts the data into the database. So how can I insert only to name, not id because id is auto increment?
I tried this
insert into student(id, name) values(,name)
but it is not insert to my table.
This is my code :
protected void Button1_Click(object sender, EventArgs e)
{
string test = txtName.Text;
SqlConnection conn = new SqlConnection(#"Data Source=.\SQLEXPRESS;AttachDbFilename=|DataDirectory|\Person.mdf;Integrated Security=True;User Instance=True");
string sql = "insert into student(name) values ('test')";
try
{
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.ExecuteNonQuery();
}
catch (System.Data.SqlClient.SqlException ex)
{
string msg = "Insert Error:";
msg += ex.Message;
}
finally
{
conn.Close();
}
}
INSERT INTO student (name) values ('name')
Omit the id column altogether, it will be populated automatically. To use your variable, you should parameterise your SQL query.
string sql = "INSERT INTO student (name) values (#name)";
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.Add("#name", SqlDbType.VarChar);
cmd.Parameters["#name"].Value = test;
cmd.ExecuteNonQuery();
You should never attempt to do this by constructing a SQL string containing the input value, as this can expose your code to SQL injection vulnerabilities.
You better use parameters when you insert data.
try
{
string sql = "insert into student(name) values (#name)";
using (SqlConnection conn = new SqlConnection(connectionString))
{
conn.Open();
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddWithValue("#name", test); // assign value to parameter
cmd.ExecuteNonQuery();
}
}
}
catch (SqlException ex)
{
string msg = "Insert Error:";
msg += ex.Message;
}
You don't need to mention the ID in first part.
insert into student(name) values('name')
I was facing this problem and after trying various solution found at stack overflow, i could summarize the experience as follows:
commands executed in command shell of mssql like:
insert into table_name (val1,val2,val3,val4) VALUES ("val1","val2",0,"val4")
go
or
insert into table_name VALUES ("val1","val2",0,"val4")
go
work when typed directly in the mssql database prompt,
But when it is required to use the the insert statement from c#, it is required to be kept in mind that string needs to be surrounded by an additional pair of single quites, around the strings, like in:
SqlConnection cnn;
string connetionString = "Data Source=server_name;Initial Catalog=database_name;User ID=User_ID;Password=Pass_word";
cnn = new SqlConnection(connetionString);
SqlCommand myCommand = new SqlCommand("insert into table_name (val1,val2,val3,val4) VALUES ('val1','val2',0,'val4');", cnn);
//or
//SqlCommand myCommand = new SqlCommand(insert into table_name VALUES ('val1','val2',0,'val4');", cnn);
cnn.Open();
myCommand.ExecuteNonQuery();
cnn.Close();
the problem here is that most people, like myself, try to use <\"> in the place of double quotes <">that is implemented as in the above command line case, and SQL executor fails to understand the meaning of this.
Even in cases where a string needs to be replace, ensure that strings are surrounded by single quotation, where a string concatination looks like a feasible solution, like in:
SqlCommand myCommand = new SqlCommand("insert into table_name (val1,val2,val3,val4) VALUES ('"+val1+"','val2',0,'val4');", cnn);
string sql = "INSERT INTO student (name) values (#name)";
conn.Open();
SqlCommand cmd = new SqlCommand(sql, conn);
cmd.Parameters.Add("#name", SqlDbType.VarChar);
cmd.Parameters["#name"].Value = test;
cmd.ExecuteNonQuery();
Try the following query,
insert into student(name) values(name)
SQL Server internally auto increments the id column when u insert the data since u said it is auto increment. If it is not working, the u have to check the identity column in the db.
use the key word "identity" to auto increment the id column
Refer : http://technet.microsoft.com/en-us/library/aa933196(v=sql.80).aspx
create table table_name( id int PRIMARY KEY IDENTITY )
and you no need to mention the "id" in the insert query
I've been writing a lot of web services with SQL inserts based on a stored procedure, and I haven't really worked with any SELECTS.
The one SELECT I have in mind is very simple.
SELECT COUNT(AD_SID) As ReturnCount FROM AD_Authorization
WHERE AD_SID = #userSID
However, I can't figure out based on my current INSERT code how to make that into a SELECT and return the value of ReturnCount... Can you help? Here is my INSERT code:
string ConnString = "Data Source=Removed";
string SqlString = "spInsertProgress";
using (OleDbConnection conn = new OleDbConnection(ConnString))
{
using (OleDbCommand cmd = new OleDbCommand(SqlString, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.AddWithValue("attachment_guid", smGuid.ToString());
cmd.Parameters.AddWithValue("attachment_percentcomplete", fileProgress);
conn.Open();
cmd.ExecuteNonQuery();
conn.Close();
}
}
Here is where you are going wrong:
cmd.ExecuteNonQuery();
You are executing a query.
You need to ExecuteReader or ExecuteScalar instead. ExecuteReader is used for a result set (several rows/columns), ExecuteScalar when the query returns a single result (it returns object, so the result needs to be cast to the correct type).
var result = (int)cmd.ExecuteScalar();
The results variable will now hold a OledbDataReader or a value with the results of the SELECT. You can iterate over the results (for a reader), or the scalar value (for a scalar).
Since you are only after a single value, you can use cmd.ExecuteScalar();
A complete example is as follows:
string ConnString = "Data Source=Removed";
string userSid = "SomeSid";
string SqlString = "SELECT COUNT(AD_SID) As ReturnCount FROM AD_Authorization WHERE AD_SID = #userSID;";
int returnCount = 0;
using (OleDbConnection conn = new OleDbConnection(ConnString))
{
using (OleDbCommand cmd = new OleDbCommand(SqlString, conn))
{
cmd.CommandType = CommandType.Text;
cmd.Parameters.AddWithValue("#userSID", userSid);
conn.Open();
returnCount = Convert.ToInt32(cmd.ExecuteScalar());
}
}
If you wanted to return MULTIPLE rows, you can use the ExecuteReader() method. This returns an IDataReader via which you can enumerate the result set row by row.
You need to use ExecuteScalar instead of ExecuteNonQuery:
String query = "SELECT COUNT(AD_SID) As ReturnCount FROM AD_Authorization WHERE AD_SID = #userSID ";
using (OleDbConnection conn = new OleDbConnection(ConnString)) {
using (OleDbCommand cmd = new OleDbCommand(query, conn))
{
cmd.Parameters.AddWithValue("userSID", userSID.ToString());
conn.Open();
int returnCount = (Int32) cmd.ExecuteScalar();
conn.Close();
}
}
cmd.executescalar will return a single value, such as your count.
You would use cmd.executereader when you are returning a list of records
runtime error 'there is already an open datareader associated with this command which must be closed first'
objCommand = new SqlCommand("SELECT field1, field2 FROM sourcetable", objConn);
objDataReader = objCommand.ExecuteReader();
while (objDataReader.Read())
{
objInsertCommand = new SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, '" + objDataReader[0] + "')", objConn);
objInsertCommand.ExecuteNonQuery();//Here is the error
}
objDataReader.Close();
I cannot define any stored procedure here.
Any help would we appreciated.
No need to do all that, just turn on MARS and your problem will get solved. In your connection string just add MultipleActiveResultSets=True;
You can't perform an action on that connection while it's still working on reading the contents of a data reader - the error is pretty descriptive.
Your alternatives are:
1) Retrieve all your data first, either with a DataSet or use the reader to populate some other collection, then run them all at once after the initial select is done.
2) Use a different connection for your insert statements.
How about pulling the data into a DataSet via Fill and then iterate through that to perform your insert via NonQuery?
IDbDataAdapter da;
IDbCommand selectCommand = connection.CreateCommand();
selectCommand.CommandType = CommandType.Text;
selectCommand.CommandText = "SELECT field1, field2 FROM sourcetable";
connection.Open();
DataSet selectResults= new DataSet();
da.Fill(selectResults); // get dataset
selectCommand.Dispose();
IDbCommand insertCommand;
foreach(DataRow row in selectResults.Tables[0].Rows)
{
insertCommand = connection.CreateCommand();
insertCommand.CommandType = CommandType.Text;
insertCommand.CommandText = "INSERT INTO tablename (field1, field2) VALUES (3, '" + row["columnName"].ToString() + "'";
}
insertCommand.Dispose();
connection.Close();
Your best bet would be to read the information you need into a list and then iterating the list to perform your inserts like so:
List<String> values = new List<String>();
using(SqlCommand objCommand = new SqlCommand("SELECT field1, field2 FROM sourcetable", objConn)) {
using(SqlDataReader objDataReader = objCommand.ExecuteReader()) {
while(objDataReader.Read()) {
values.Add(objDataReader[0].ToString());
}
}
}
foreach(String value in values) {
using(SqlCommand objInsertCommand = new SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, '" + value + "')", objConn)) {
objInsertCommand.ExecuteNonQuery();
}
}
INSERT INTO tablename (field1, field2)
SELECT 3, field1 FROM sourcetable
A single SQL statement instead of one per insert. Not sure if this will work for your real-life problem, but for the example you provided, this is a much better query than doing them one at a time.
On a side note, make sure your code uses parameterized queries instead of accepting strings as-is inside the SQL statement - your sample is open to SQL injection.
Several suggestions have been given which work great, along with recommendations for improving the implementation. I hit the MARS limit due to existing code not cleaning up a reader so I wanted to put together a more respectable sample:
const string connectionString = #"server=.\sqlexpress;database=adventureworkslt;integrated security=true";
const bool useMARS = false;
using (var objConn = new System.Data.SqlClient.SqlConnection(connectionString + (useMARS ? ";MultipleActiveResultSets=True" : String.Empty)))
using (var objInsertConn = useMARS ? null : new System.Data.SqlClient.SqlConnection(connectionString))
{
objConn.Open();
if (objInsertConn != null)
{
objInsertConn.Open();
}
using (var testCmd = new System.Data.SqlClient.SqlCommand())
{
testCmd.Connection = objConn;
testCmd.CommandText = #"if not exists(select 1 from information_schema.tables where table_name = 'sourcetable')
begin
create table sourcetable (field1 int, field2 varchar(5))
insert into sourcetable values (1, 'one')
create table tablename (field1 int, field2 varchar(5))
end";
testCmd.ExecuteNonQuery();
}
using (var objCommand = new System.Data.SqlClient.SqlCommand("SELECT field1, field2 FROM sourcetable", objConn))
using (var objDataReader = objCommand.ExecuteReader())
using (var objInsertCommand = new System.Data.SqlClient.SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, #field2)", objInsertConn ?? objConn))
{
objInsertCommand.Parameters.Add(new System.Data.SqlClient.SqlParameter("field2", String.Empty));
while (objDataReader.Read())
{
objInsertCommand.Parameters[0].Value = objDataReader[0];
objInsertCommand.ExecuteNonQuery();
}
}
}
Option 1:
Must execute query and load data before running another query.
Option 2:
Add MultipleActiveResultSets=true to the provider part of your connection string. See the example below:
<add name="DbContext" connectionString="Data Source=(LocalDb)\v11.0;Initial Catalog=dbName;Persist Security Info=True;User ID=userName;Password=password;MultipleActiveResultSets=True" providerName="System.Data.SqlClient" />
What version of SQL Server are you using? The problem might be with this:
(from http://msdn.microsoft.com/en-us/library/9kcbe65k.aspx)
When you use versions of SQL Server before SQL Server 2005, while the SqlDataReader is being used, the associated SqlConnection is busy serving the SqlDataReader. While in this state, no other operations can be performed on the SqlConnection other than closing it. This is the case until the Close method of the SqlDataReader is called.
So, if this is what's causing your problem, you should first read all the data, then close the SqlDataReader, and only after that execute your inserts.
Something like:
objCommand = new SqlCommand("SELECT field1, field2 FROM sourcetable", objConn);
objDataReader = objCommand.ExecuteReader();
List<object> values = new List<object>();
while (objDataReader.Read())
{
values.Add(objDataReader[0]);
}
objDataReader.Close();
foreach (object value in values)
{
objInsertCommand = new SqlCommand("INSERT INTO tablename (field1, field2) VALUES (3, '" + value + "')", objConn);
objInsertCommand.ExecuteNonQuery();
}
Adding this to connection string should fix the problem.
MultipleActiveResultSets=true
Try something like this:
//Add a second connection based on the first one
SqlConnection objConn2= new SqlConnection(objConn.connectionString))
SqlCommand objInsertCommand= new SqlCommand();
objInsertCommand.CommandType = CommandType.Text;
objInsertCommand.Connection = objConn2;
while (objDataReader.Read())
{
objInsertCommand.CommandText = "INSERT INTO tablename (field1, field2) VALUES (3, '" + objDataReader[0] + "')";
objInsertCommand.ExecuteNonQuery();
}
Best Solution:
There is only problem with your "CommandText" value. Let it be SP or normal Sql Query.
Check 1: The parameter value which you are passing in your Sql Query
is not changing and going same again and again in your ExecuteReader.
Check 2: Sql Query string is wrongly formed.
Check 3: Please create simplest code as follows.
string ID = "C8CA7EE2";
string myQuery = "select * from ContactBase where contactid=" + "'" + ID + "'";
string connectionString = ConfigurationManager.ConnectionStrings["CRM_SQL_CONN_UAT"].ToString();
SqlConnection con = new SqlConnection(connectionString);
con.Open();
SqlCommand cmd = new SqlCommand(myQuery, con);
DataTable dt = new DataTable();
dt.Load(cmd.ExecuteReader());
con.Close();
In order for it to be disposed easily i use the following coding-template :
`using (SqlConnection connection = new SqlConnection("your connection string"))
{
connection.Open();
using (SqlCommand cmd = connection.CreateCommand())
{
cmd.CommandText = "Select * from SomeTable";
using (SqlDataReader reader = cmd.ExecuteReader())
{
if(reader.HasRows)
{
while(reader.Read()){
// assuming that we've a 1-column(Id) table
int id = int.Parse(reader[0].ToString());
}
}
}
}
connection.Close()
}`