How to obtain a DB2 Sequence Value in a Multithreaded Application - sql

I am working on a multithreaded application that uses DB2 for its primary database. In the past we've mostly used Identity columns for tables where we needed an auto-generated unique identifier. To do that we would run the below 2 queries in the same transaction:
INSERT INTO tbname (IDENTITY_COL, ...) VALUES (DEFAULT, ...);
SELECT IDENTITY_VAL_LOCAL() FROM SYSIBM.SYSDUMMY1;
We are now being pressured to switch to Sequence instead. I know you can use "NEXT VALUE FOR colname" in both INSERT and SELECT statements, but I can't figure out how to both INSERT and SELECT with the same value without risking a race condition in a multithreaded application. For example, if I use:
INSERT INTO tbname (SEQUENCE_COL, ...) VALUES (NEXT VALUE FOR SEQUENCE_COL, ...);
SELECT PREVIOUS VALUE FOR SEQUENCE_COL;
Then there's a possibility another INSERT was run between the above INSERT and SELECT, hence providing me the incorrect value. If I try:
SELECT NEXT VALUE FOR SEQUENCE_COL;
store the value in a variable and pass that in to the INSERT:
INSERT INTO tbname (SEQUENCE_COL, ...) VALUES (variable_value, ...);
Then there's a possibility another thread got the same NEXT VALUE and tries to insert the same value, resulting in a DB2 -803 error. Is it possible to use SEQUENCE columns in a multithreaded environment, or do I need to fight to keep my IDENTITY columns?

In addition to what Michael Sharek (correctly) said:
INSERT INTO tbname (SEQUENCE_COL, ...) VALUES (NEXT VALUE FOR SEQUENCE_COL, ...);
SELECT PREVIOUS VALUE FOR SEQUENCE_COL;
Your assumption Then there's a possibility another INSERT was run between the above INSERT and SELECT, hence providing me the incorrect value" regarding the above sequence of statements is incorrect.
The "next value" and "previous value" are connection specific.
Access to a sequence from different threads will never create a "race" condition. Each connection has a completely isolated "environment" for the sequence.

You've got a mistaken assumption in your question.
If I try:
SELECT NEXT VALUE FOR SEQUENCE_COL;
store the value in a variable and pass that in to the INSERT:
INSERT INTO tbname (SEQUENCE_COL, ...) VALUES (variable_value, ...);
Then there's a possibility another thread got the same NEXT VALUE and tries to insert the same value
That's not correct. The second thread would get a different NEXTVAL and not the same value as the first thread.
I also want to add my opinion on this part:
We are now being pressured to switch to Sequence instead.
I can't imagine there being a really good reason to switch to sequences from identity. They're basically the same thing.

In addition to the other correct answers, you can also just use a single statement to insert a row and return inserted values as follows:
SELECT SEQUENCE_COL FROM NEW TABLE (
INSERT INTO tbname (SEQUENCE_COL, ...) VALUES (NEXT VALUE FOR MY_SEQUENCE, ...)
)

Related

New to SQL - INCORRECT INTEGER. Laments terms, what does it mean?

Error: Incorrect integer value: 'Hammer Mill' for column 'supplier_name' at row 1
Error Code: ER_TRUNCATED_WRONG_VALUE_FOR_FIELD
Context, I'm trying to build a database. I've been able to run the prior tables. I get to this one: -- BRANCH SUPPLIER
INSERT INTO branch_supplier VALUES(2, 'Hammer Mill', 'Paper'); and I get the errors mentioned above.
Seems self-explanatory to me, whatever the database you are using...
You are trying to insert a string value into an integer column and there are only two possible causes:
Either the table has been created with the wrong data types (or you are not respecting data types). I think this is your case as [supplier_name] as an int column sounds wrong to me
The "column order" is not the one you expect, if not specified it will be positional, meaning the first value goes into the first column... it's not wise for several reasons and I suggest you always use the below format for your insert
INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value, ...);

Getting INSERT errors when I do UPDATE?

At work we have a SQL Server database. I don't know the db that well. I have created a new column in the table for some new functionality....straight away I have started seeing errors
My statement was this:
ALTER TABLE users
ADD locked varchar(50) NULL
GO
The error is:
Insert Error: Column name or number of supplied values does not match table definition
I have read that the error message appears when during an INSERT operation either the number of supplied column names or the number of supplied values does not match the table definition.
But I have checked so many times and i have changed the PHP code to include this columns data yet I still receive the error.
I have run the SQL query directly on the db and still get the error.
Funny enough the query which gets the error is an Update.
UPDATE "users"
SET "users"."date_last_login" = GETDATE()
WHERE id = 1
Have you considered it could be a trigger causing it? 
This is the error message you would get.
If its an Update action causing it check trigger actions that Updates on that table run.
Do it with:
#sp_helptrigger Users, 'UPDATE';
This will show triggers occuring with ‘update’ actions.
If there is a trigger, grab the triggers name and run the below (but replace TriggerNameHere with real trigger):
#sp_helptext TriggerNameHere;
This will give you any SQL that the trigger runs and could be the INSERT the error message is referring to.
Hope this helps
Aside from TRIGGERS,
the reason for that is because you are using implicit type of INSERT statement. Let's say your previous number of columns on the table is 3. You have this syntax of INSERT statement,
INSERT INTO tableName VALUES ('val1','val2','val3')
which executes normally fine. But then you have altered the table to add another column. All of your INSERT queries are inserting only three values on the table which doesn't matches to the total number of columns.
In order to fix the problem, you have to update all INSERT statements to insert 4 values on the table,
INSERT INTO tableName VALUES ('val1','val2','val3', 'val4')
and it will normally work fine.
I'll advise you to use the EXPLICIT type of INSERT wherein you have to specify the columns you want to insert values with. Eg,
INSERT INTO tableName (col1, col2, col3) VALUES ('val1','val2','val3')
in this ways, even if you have altered your tables by adding additional columns, your INSERT statement won't be affected unless the column doesn't have a default value and which is non-nullable.

SQL Server - Return value after INSERT

I'm trying to get a the key-value back after an INSERT-statement.
Example:
I've got a table with the attributes name and id. id is a generated value.
INSERT INTO table (name) VALUES('bob');
Now I want to get the id back in the same step. How is this done?
We're using Microsoft SQL Server 2008.
No need for a separate SELECT...
INSERT INTO table (name)
OUTPUT Inserted.ID
VALUES('bob');
This works for non-IDENTITY columns (such as GUIDs) too
Use SCOPE_IDENTITY() to get the new ID value
INSERT INTO table (name) VALUES('bob');
SELECT SCOPE_IDENTITY()
http://msdn.microsoft.com/en-us/library/ms190315.aspx
INSERT INTO files (title) VALUES ('whatever');
SELECT * FROM files WHERE id = SCOPE_IDENTITY();
Is the safest bet since there is a known issue with OUTPUT Clause conflict on tables with triggers. Makes this quite unreliable as even if your table doesn't currently have any triggers - someone adding one down the line will break your application. Time Bomb sort of behaviour.
See msdn article for deeper explanation:
http://blogs.msdn.com/b/sqlprogrammability/archive/2008/07/11/update-with-output-clause-triggers-and-sqlmoreresults.aspx
Entity Framework performs something similar to gbn's answer:
DECLARE #generated_keys table([Id] uniqueidentifier)
INSERT INTO Customers(FirstName)
OUTPUT inserted.CustomerID INTO #generated_keys
VALUES('bob');
SELECT t.[CustomerID]
FROM #generated_keys AS g
JOIN dbo.Customers AS t
ON g.Id = t.CustomerID
WHERE ##ROWCOUNT > 0
The output results are stored in a temporary table variable, and then selected back to the client. Have to be aware of the gotcha:
inserts can generate more than one row, so the variable can hold more than one row, so you can be returned more than one ID
I have no idea why EF would inner join the ephemeral table back to the real table (under what circumstances would the two not match).
But that's what EF does.
SQL Server 2008 or newer only. If it's 2005 then you're out of luck.
There are many ways to exit after insert
When you insert data into a table, you can use the OUTPUT clause to
return a copy of the data that’s been inserted into the table. The
OUTPUT clause takes two basic forms: OUTPUT and OUTPUT INTO. Use the
OUTPUT form if you want to return the data to the calling application.
Use the OUTPUT INTO form if you want to return the data to a table or
a table variable.
DECLARE #MyTableVar TABLE (id INT,NAME NVARCHAR(50));
INSERT INTO tableName
(
NAME,....
)OUTPUT INSERTED.id,INSERTED.Name INTO #MyTableVar
VALUES
(
'test',...
)
IDENT_CURRENT: It returns the last identity created for a particular table or view in any session.
SELECT IDENT_CURRENT('tableName') AS [IDENT_CURRENT]
SCOPE_IDENTITY: It returns the last identity from a same session and the same scope. A scope is a stored procedure/trigger etc.
SELECT SCOPE_IDENTITY() AS [SCOPE_IDENTITY];
##IDENTITY: It returns the last identity from the same session.
SELECT ##IDENTITY AS [##IDENTITY];
##IDENTITY Is a system function that returns the last-inserted identity value.
There are multiple ways to get the last inserted ID after insert command.
##IDENTITY : It returns the last Identity value generated on a Connection in current session, regardless of Table and the scope of statement that produced the value
SCOPE_IDENTITY(): It returns the last identity value generated by the insert statement in the current scope in the current connection regardless of the table.
IDENT_CURRENT(‘TABLENAME’) : It returns the last identity value generated on the specified table regardless of Any connection, session or scope. IDENT_CURRENT is not limited by scope and session; it is limited to a specified table.
Now it seems more difficult to decide which one will be exact match for my requirement.
I mostly prefer SCOPE_IDENTITY().
If you use select SCOPE_IDENTITY() along with TableName in insert statement, you will get the exact result as per your expectation.
Source : CodoBee
The best and most sure solution is using SCOPE_IDENTITY().
Just you have to get the scope identity after every insert and save it in a variable because you can call two insert in the same scope.
ident_current and ##identity may be they work but they are not safe scope. You can have issues in a big application
declare #duplicataId int
select #duplicataId = (SELECT SCOPE_IDENTITY())
More detail is here Microsoft docs
You can use scope_identity() to select the ID of the row you just inserted into a variable then just select whatever columns you want from that table where the id = the identity you got from scope_identity()
See here for the MSDN info http://msdn.microsoft.com/en-us/library/ms190315.aspx
Recommend to use SCOPE_IDENTITY() to get the new ID value, But NOT use "OUTPUT Inserted.ID"
If the insert statement throw exception, I except it throw it directly. But "OUTPUT Inserted.ID" will return 0, which maybe not as expected.
This is how I use OUTPUT INSERTED, when inserting to a table that uses ID as identity column in SQL Server:
'myConn is the ADO connection, RS a recordset and ID an integer
Set RS=myConn.Execute("INSERT INTO M2_VOTELIST(PRODUCER_ID,TITLE,TIMEU) OUTPUT INSERTED.ID VALUES ('Gator','Test',GETDATE())")
ID=RS(0)
You can append a select statement to your insert statement.
Integer myInt =
Insert into table1 (FName) values('Fred'); Select Scope_Identity();
This will return a value of the identity when executed scaler.
* Parameter order in the connection string is sometimes important. * The Provider parameter's location can break the recordset cursor after adding a row. We saw this behavior with the SQLOLEDB provider.
After a row is added, the row fields are not available, UNLESS the Provider is specified as the first parameter in the connection string. When the provider is anywhere in the connection string except as the first parameter, the newly inserted row fields are not available. When we moved the the Provider to the first parameter, the row fields magically appeared.
After doing an insert into a table with an identity column, you can reference ##IDENTITY to get the value:
http://msdn.microsoft.com/en-us/library/aa933167%28v=sql.80%29.aspx

SQL: is it possible to combine an INSERT and SELECT statement into one

I want to add a row using SQLs INSERT statement. Is it possible that as part of this statement I can somehow get the value of the column userId which I don't update but is the AUTO_INCREMENT primary key. I need this value to update another table, however I can't follow the Insert statement immediately with a SELECT statement as there is no other unique identifier in the table on which to select.
INSERT INTO objectUrl(disp_name, loggedIn) VALUES('please change this', true)
Is it possible to get the row number (column name userId) and if so how do you do it?
In MySQL it's called LAST_INSERT_ID(). I believe to be technically correct, the two statements should be wrapped in a transaction so that some other INSERT doesn't mess up what ID you get back.
In SQL Sever you have IDENT_CURRENT(‘tablename’) which will only grab it from that table (still need a transaction to be safe). You could also use SCOPE_IDENTITY() which theoretically will always return the one you expect as long as you aren't doing something weird with your connection.
For MySQL you have:
select last_insert_id()

How do I get the value of the autogenerated fields using Postgresql?

I have a postgres table like this:
CREATE SEQUENCE seq;
CREATE TABLE tbl (id INTEGER DEFAULT VALUE nextval('seq'), data VARCHAR);
When I insert into the table:
INSERT INTO tbl (data) VALUES ('something');
How can I get back the value of the id field for the record I just created?
(Note, I may have got some of the SQL syntax a bit off; the gist should be clear, though)
Suppose for the sake of argument that I'm not able to call currval on the same session because I don't control the transaction boundaries. I might be working in the same session with my next call, but I might not be, too.
You're looking for INSERT ... RETURNING. From The Manual's section on INSERT:
The optional RETURNING clause causes INSERT to compute and return value(s) based on each row actually inserted. This is primarily useful for obtaining values that were supplied by defaults, such as a serial sequence number. However, any expression using the table's columns is allowed. The syntax of the RETURNING list is identical to that of the output list of SELECT.
In the same session:
SELECT currval('seq');
EDIT: But if you can't use currval/nextval because you don't know if the inserting and selecting of the sequence value will occur in the same session, and if you're on postgresql 8.2 or later, you could probably write your insert statement like this.
INSERT INTO tbl (data) VALUES ('something') RETURNING id;
which should also return the last inserted id.
Use nextval and currval, take a look here: http://www.postgresql.org/docs/8.0/static/functions-sequence.html