I have the following bit of code:
string updatestr= "UPDATE t1 SET c3= #v3,c4=#v4,c5=#v5,c6=#v6 WHERE c2 = 'test' SELECT SCOPE_IDENTITY()";
SqlCommand updatecmd= new SqlCommand(updatestr, conn);
updatecmd.Parameters.AddWithValue("#v3",value3)
updatecmd.Parameters.AddWithValue("#v4",value3)
updatecmd.Parameters.AddWithValue("#v5",value3)
updatecmd.Parameters.AddWithValue("#v6",value3)
object ind = updatecmd.ExecuteScalar();
int distindex = int.Parse(ind.ToString());
Now I would expect the output of the scope identity to be an int (namely 29) however I get a format exception. at int.Parse(ind.ToString());. I printed the value of ind.tostring and for some reason it's test. Note that the first colum and the primairy key of the database (called id) is an autoincrementing int and I was hoping to select that. What am I doing wrong?
Oh and here is a better formated version of the query:
UPDATE t1
SET c3= #v3,c4=#v4,c5=#v5,c6=#v6
WHERE c2 = "test"
SELECT SCOPE_IDENTITY()
SELECT SCOPE_IDENTITY()
Is for getting the auto-generated Id when a new row is INSERTED, in your case its an update so its probably returning null, you should use
select CAST(##rowcount as int)
instead
As I have said in comment:
You are updating based on non primary key so there can be more that
1 updated row. Which one do you want in result? You are looking for
OUTPUT clause I think.
So the solution is to use OUTPUT clause with Inserted or Deleted virtual tables in case of update statement:
string updatestr= "UPDATE t1 SET c3= #v3,c4=#v4,c5=#v5,c6=#v6
OUTPUT Inserted.id
WHERE c2 = 'test'";
try this:
int distindex = 0;
object ind = updatecmd.ExecuteScalar();
if(ind != null)
int.TryParse(ind.ToString(), out distindex);
Related
I used the below query:
select id from foo order by id DESC limit 1
to return the last id used.
I had in my application something to add plus one if it wasn't zero. It was working fine. But I decided to do this in the sql instead of the C# code. But when I do this:
SELECT IF(id = 0, 0, id + 1) as id from foo order by id DESC limit 1
But I get a System.InvalidCastException error and to my surprise the data type printed in below code is int64. But did it changed?
Here's the c# code:
public int GetLastID()
{
using (MySqlConnection con = Open())
{
using (MySqlCommand cmd = new MySqlCommand())
{
cmd.CommandText = "SELECT IF(id = 0, 0, id + 1) as id from foo order by id DESC limit 1";
cmd.Connection = con;
con.Open();
MySqlDataReader data = cmd.ExecuteReader();
if (!data.Read())
return 0;
System.Windows.Forms.MessageBox.Show(data["id"].GetType().ToString()); // output: int64
return (int)data["id"];
}
}
}
Don't blame my bad mysql. I don't use it much. If you know any way better to do this, please tell me!
The alias assigned to the column (AS id) does not change the datatype.
The datatype of the expression is the result of the expression.
In MySQL, an integer addition operation (e.g. id + 1, where id is integer type) results in BIGINT.
This behavior is documented in the (readily available) MySQL Reference Manual here: https://dev.mysql.com/doc/refman/5.6/en/arithmetic-functions.html
excerpt:
The usual arithmetic operators are available. The result is determined according to the following rules:
In the case of -, +, and *, the result is calculated with BIGINT (64-bit) precision if both operands are integers.
So, the return of BIGINT datatype (64-bit integer) is expected behavior.
If your requirement is to return a 32-bit integer, the only workaround (that I know of) is to create a user-defined function, e.g.
DELIMITER $$
CREATE FUNCTION udf_bigint_to_int(n BIGINT)
RETURNS INTEGER
BEGIN
RETURN n;
END$$
DELIMITER ;
As a demonstration that this works, and also as a warning about the behavior, passed in a value larger than can be supported by a 32-bit integer, we can run this:
SELECT udf_bigint_to_int(9876543210+9)
returns the maximum value for a 32-bit integer:
udf_bigint_to_int(9876543210+9)
-------------------------------
2147483647
If the intent is to get the largest id value from the table, and add 1 to it, and return that as a 32-bit integer, after creating the user-defined function, you could use a query like this:
SELECT udf_bigint_to_int( MAX(id) + ABS(SIGN( MAX(id) ))) AS id FROM foo
I think you are wanting to get the next id or last id, but have the function return a zero or a one if the table is empty. This will give you the last inserted id without you having to write the code. It is a built in function:
select last_insert_id();
or, if you need to do it for a specific table, and not the last table you inserted into just now, then this will give you the highest id + 1, or just 1 if the table is empty:
select ifnull(max(id),0) + 1 from mytable
This just says - give me the maximum id, but if that is null (table is empty), then give me a zero. Then whichever of those two comes back, add 1 to it. Or if you want to start from zero, then do:
select ifnull(max(id) + 1, 0) from mytable
Using max(id) is much better than selecting the first row by descending orde - much more efficient. Of course, much better to use autoincrement and let the database assign the next id, if what you are trying to do is to assign an id to the next record you are going to insert.
Direct answer: No, aliasing won't change your data type, it'll change the name displayed for the column returned by the query
I have a method to insert user information into the SQL Server database. I have my combobox populate on pageload event. The user selects the input they want and hit update if they are updating a old record or insert if creating a new record. When they do so my database is not storing the right value if they select 4 it stores 3. Here is my insert method and populate method.
Insert method: I have to join the StockID because it is a primary key.
using (dbConn2 = new SqlConnection(Properties.Settings.Default["tville"].ToString()))
{
SqlCommand addNewFormat = new SqlCommand(#"INSERT INTO PackLabelFormat ( PackFormatID, Description, PrintWeight, PrintPlantCode, PrintPrice, StockID) VALUES (#PackFormatID, #Description, #PrintWeight, #PrintPlantCode, #PrintPrice, (SELECT #StockID from LabelStockReference LSR WHERE LSR.StockID = #StockID))", dbConn2);
addNewFormat.Parameters.AddWithValue("#PackFormatID", Convert.ToInt32(IDselect));
addNewFormat.Parameters.AddWithValue("#Description", rTxtBoxDescription.Text);
addNewFormat.Parameters.AddWithValue("#PrintPrice", rChkBoxPrintPrice.Checked);
addNewFormat.Parameters.AddWithValue("#PrintWeight", rChkBoxWeight.Checked);
addNewFormat.Parameters.AddWithValue("#PrintPlantCode", rChkBoxPlantCode.Checked);
addNewFormat.Parameters.AddWithValue("#StockID", Convert.ToInt32(cmboBoxStock.SelectedIndex));
dbConn2.Open();
addNewFormat.ExecuteNonQuery();
Populate method:
if (labelList != null)
{
foreach (LabelData l in labelList)
{
cmboBoxStock.Items.Add(string.Format("{0} - {1}", l.PackFormatID, l.Description));
}
}
If there is anything else I'm leaving out just let me know. Thanks.
There are two options for your INSERT statement:
(1) you can use INSERT ... VALUES .... and in this case, you must supply as many values as you have columns to insert data into, and you have to supply literal values or SQL Server variables only - you cannot use a SELECT to provide a value:
DECLARE #ASqlServerVariable VARCHAR(100)
SET #ASqlServerVariable = 'any possible value that's legal for this datatype'
INSERT INTO dbo.YourTable(ID, Col1, Col2, Col3)
VALUES (42, 'Some fixed value', #ASqlServerVariable, 'Another literal value')
What you could do is use a SELECT to store the value you're interested in into a SQL Server variable:
DECLARe #StockID INT
SELECT #StockID = ID
FROM dbo.LabelStockReference LSR
WHERE LSR.StockID = 4711
(2) if you can't provide all literal values or variables, then you must use the INSERT .... SELECT ... option, which requires you to provide as many columns in your SELECT as the INSERT expects to insert a row into the target table:
DECLARE #ASqlServerVariable VARCHAR(100)
SET #ASqlServerVariable = 'any possible value that's legal for this datatype'
INSERT INTO dbo.YourTable(ID, Col1, Col2, Col3)
SELECT
42, 'Some fixed value', #ASqlServerVariable, aTableColumn
FROM
dbo.SomeOtherTable
See the official TechNet documentation for INSERT for all the details and exact syntax of all possible options etc.
The first SelectedIndex is 0, and your first ID is 1 ,so just make this change:
addNewFormat.Parameters.AddWithValue("#StockID", Convert.ToInt32(cmboBoxStock.SelectedIndex)+1);
I have a code as bellow where I try to insert a data into a table and return the ID (given by auto increment) of the new element.
int newEquipmentID = new int();
query = database.ParameterizedQueryString("INSERT INTO Equipment (EquipmentTypeID) VALUES ({0})", "equipmenttypeID");
newEquipmentID = (int)database.Connection.ExecuteScalar(query, DefaultTimeout, equipment.EquipmentTypeID);
But it fails and returns null, as if the new item wasn't added yet. But actually I can see the new item making a simple consult at the DB.
My question is "when" the data is actually added into the DB and how can I get the ID of the new added item.
Thanks!
You don't need two queries to create the new record and retrieve the new identity value:
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();
}
}
Side-Note: I wouldn't use such a Database-Class since it's prone to errors.
Your SQL query does not return the newly generated Id. To return it, use an OUTPUT clause:
INSERT INTO Equipment (<list of fields>)
OUTPUT inserted.EquipmentTypeID
VALUES (<list of values>)
Some things to be careful about:
<list of fields> represents a comma separated list of columns for which you will supply values
the list of fields should not include the auto-increment ID column (that will be automatically assigned a value)
<list of values> represents a comma separated list of values that will be inserted in the above specified fields. The number of values should be equal to the number of fields and the data types must match
To return the id of just inserted row you need to select it, because ExecuteScalar() returns
the first column of the first row in the result set returned by the query
and INSERT doesn't select/return anything.
insert ...
select ...
See #Tim's answer for more details.
OUTPUT Clause will help you to get the ID of the new added item. For more information please see the link below :
Click here! for more detail
I have a question regarding Oracle bind variables and select statements.
What I would like to achieve is do a select on a number different values for the primary key. I would like to pass these values via an array using bind values.
select * from tb_customers where cust_id = :1
int[] cust_id = { 11, 23, 31, 44 , 51 };
I then bind a DataReader to get the values into a table.
The problem is that the resulting table only contains a single record (for cust_id=51). Thus it seems that each statement is executed independently (as it should), but I would like the results to be available as a collective (single table).
A workaround is to create a temporary table, insert all the values of cust_id and then do a join against tb_customers. The problem with this approach is that I would require temporary tables for every different type of primary key, as I would like to use this against a number of tables (some even have combined primary keys).
Is there anything I am missing?
Not asking the question as to why you would want to do this to begin with. Shouldn't the sql statement be something like
select * from tb_customers where cust_id = 11 or 23 or ...
Edit:
I am limited in Oracle but when I look at the documentation I think that you might have to do something like this:
variable i number
exec :i := 11
select * from tb_customers where cust_id = :i
This would allow you to take advantage of binding. You will have to add each record return to your own collection since it will still only return one at a time.
I know this was asked a while ago but not a brilliant answer.
I would do something like this - please excuse the crude psudo code
string bindList = "";
for(int ii=0;ii<cust_id.count;++ii)
{
if(ii == 0)
{
bindList += ":" + ii;
}
else
{
bindList += ",:" + ii;
}
OracleParameter param = new OracleParameter();
param.dbType = types.int;
param.value = cust_id[ii];
command.Parameters.Add(param);
}
query = "select * from tb_customers where cust_id in(" + bindList + ")";
So then query ends up having in(:1,:2,:3,etc) and each of these are bound separately.
There is also a similar question here: OracleParameter and IN Clause
I'm using an SqlCommand object to insert a record into a table with an autogenerated primary key. How can I write the command text so that I get the newly created ID when I use the ExecuteScalar() method?
INSERT INTO YourTable(val1, val2, val3 ...)
VALUES(#val1, #val2, #val3...);
SELECT SCOPE_IDENTITY();
Don't forget the semicolons at the end of each statement.
Add the following line to the end of the Sql Query...
SELECT SCOPE_IDENTITY()
And then use the ExecuteScalar method on the SqlCommand object...
var rowCount = command.ExecuteScalar()
insert into Yourtable()
values()
SELECT SCOPE_IDENTITY()
I just ran a test and verified that the semi-colons are optional using SQL Server 2005 SP2, and .Net 3.5
Add an output parameter to the command object and then set the value to the new ID in the stored procedure.
Stored Procedure:
#ID AS INT OUTPUT
[Insert Command]
SET #ID = SCOPE_IDENTITY()
.NET:
cmd.CommandText = "stored_procedure";
SqlParameter pID = new SqlParameter("ID", DBType.Int32, 4);
pID.Direction = ParameterDirection.Output;
cmd.ExecuteScalar();
int id = Convert.ToInt32(cmd.Parameters["ID"].Value.ToString());
Don't use ##IDENTITY, however simple it may seem. It can return incorrect values.
SELECT SCOPE_IDENTITY()
appears to be the obvious choice.
Although I like Dave Markle's answer, ( and I see you did too, since you marked it as your answer ), that method can fail if you have triggers on your database, that audit CUD operations, and your audit table has an IDENTITY column. It would return the value of the Audit table's identity, not the table you just inserted into, since the audit table actualy happen after.
In that case, a more generic method can be used that will work in both cases, regardless of any auditing. Its a bit more wordy, but you get what you pay for.
example:
#"DECLARE #tmp AS TABLE ( id int )
INSERT INTO case
(
caseID,
partID,
serialNumber,
hardware,
software,
firmware
)
OUTPUT Inserted.ID into #tmp
VALUES
(
#caseID,
#partItemID,
#serialNumber,
#hardware,
#software,
#firmware
)
Select ID from #tmp" )
Immediately after your insert stmt, use
SELECT CAST(scope_identity() AS bigint) ---- incase you have a return result as int64
This will return the column created id/identity.
If your id is a Guid, then I found this solution to be best:
INSERT INTO YourTable (val1, val2, val3)
OUTPUT inserted.id
VALUES (#val1, #val2, #val3)
Thanks #Scott Ivey
Full demo:
internal static Guid InsertNote(Note note)
{
Guid id;
using (
var connection =
new SqlConnection(ConfigurationManager.ConnectionStrings["dbconn"].ConnectionString))
{
connection.Open();
using (
var command =
new SqlCommand(
"INSERT INTO Notes ([Title],[Text]) " +
"OUTPUT inserted.id " +
$"VALUES ('{title}','{text}');", connection))
{
command.CommandType = CommandType.Text;
var reader = command.ExecuteReader();
reader.Read();
id = reader.GetGuid(reader.GetOrdinal("id"));
}
connection.Close();
}
return id;
}
I would recommend using a Stored Procedure, but this is for unit testing our repository.
Straight out of the Whirlpool:
If you're using MS SQL you can use "SELECT ##IDENTITY as Value" after your insert to get the last ID generated
and:
##IDENTITY and SCOPE_IDENTITY return the last identity value generated in any table in the current session. However, SCOPE_IDENTITY returns the value only within the current scope; ##IDENTITY is not limited to a specific scope.
Edit: As pointed out in the comments, you should always use SCOPE_IDENTITY, not ##IDENTITY.