Using ##identity or output when inserting into SQL Server view? - sql-server

(forgive me - I'm new to both StackOverflow & SQL)
Tl;dr - When using ##identity (or any other option such as scope_identity or output variable), is it possible to also use a view? Here is an example of a stored procedure using ##identity:
--SNIP--
DECLARE #AID INT
DECLARE #BID INT
INSERT INTO dbo.A (oct1)
VALUES
(#oct1)
SELECT #AID = ##IDENTITY;
INSERT INTO dbo.B (duo1)
VALUES
(#duo2)
SELECT #BID = ##IDENTITY
INSERT INTO dbo.tblAB (AID, BID)
VALUES
(#AID, #BID)
GO
Longer:
When inserting into a table, you can capture the current value of the identity seed using ##identity. This is useful if you want to insert into table A and B, capture the identity value, then insert into table AB relating A to B. Obviously this is for purposes of data normalization.
Let's say you were to abstract the DB Schema with a few that performs inner joins on your tables to make the data easier to work with. How would you populate the cross reference tables properly in that case? Can it be done the same way, if so, how?

Avoid using ##IDENTITY or SCOPE_IDENTITY() if your system is using Parallel plans as there is a nasty bug. Please refer -
http://connect.microsoft.com/SQL/feedback/ViewFeedback.aspx?FeedbackID=328811
Better way to fetch the inserted Identity ID would be to use OUTPUT clause.
CREATE TABLE tblTest
(
Sno INT IDENTITY(1,1) NOT NULL,
FirstName VARCHAR(20)
)
DECLARE #pk TABLE (ID INT)
INSERT INTO tblTest(FirstName)
OUTPUT INSERTED.Sno INTO #pk
SELECT 'sample'
SELECT * FROM #pk
EDIT:
It would work with Views as well. Please see the sample below. Hope this is what you were looking for.
CREATE VIEW v1
AS
SELECT sno, firstname FROM tbltest
GO
DECLARE #pk TABLE (ID INT)
INSERT INTO v1(FirstName)
OUTPUT INSERTED.Sno INTO #pk
SELECT 'sample'
SELECT ID FROM #pk

##IDENTITY returns the last IDENTITY value produced on a connection, regardless of the table that produced the value, and regardless of the scope of the statement that produced the value.
SCOPE_IDENTITY() returns the last IDENTITY value produced on a connection and by a statement in the same scope, regardless of the table that produced the value. SCOPE_IDENTITY(), like ##IDENTITY, will return the last identity value created in the current session, but it will also limit it to your current scope as well
Although the issue with either of these is fixed by microsoft , I would suggest you should go with "OUTPUT", and yes, it can be used with view as well

Related

SQL Server : multiple queries with Auto ID identity

I have a stored procedure that inserts some values to a specific table:
CREATE PROCEDURE AccessoiresAddOrder
#AccessoireID int,
#Qte int,
#ClientID int,
#Price Varchar(50),
#Totalprice Varchar(50),
#Date DateTime,
#ValueInt
AS
INSERT INTO [dbo].[Accessoires_Orders] ([AccessoireID], [Qte], [ClientID], [Price], [Totalprice],[Date])
VALUES (#AccessoireID, #Qte, #ClientID, #Price, #Totalprice, #Date)
INSERT INTO [dbo].[Accessoires_ordervalue] (Value)
VALUES (#Value)
The Acccessoires_Orders table has a column ID which is "auto identity". Is there any way to get that value during the procedure execution?
int ID = Max(Accessoires_Orders.ID) + 1
Why ?
Because this procedure can be executed by multiple users at the same exact time.
You can use either:
SELECT #MyNewID = SCOPE_IDENTITY()
SELECT #MyNewID = ##IDENTITY
SCOPE_IDENTITY() will always give you the value created by the query itself. ##IDENTITY can give you that value as well, but if a trigger executes after your insert, it will return the ID generated by the trigger instead.
Since you want the ID you created in the query, you'd be best off using SCOPE_IDENTITY().
You can use ##IDENTITY variable that returns you the last identity value returned within the current session - see: MSDN ##IDENTITY. However, if there are e.g. some triggers behind the table you can receive an identity value created by this trigger in a completely different table...
So generally recommended way is to rather use the SCOPE_IDENTITY() - see: MSDN SCOPE_IDENTITY()
After inserting a record to a able with an Identity column, the latest value inserted in the last row will be available in ##Identity variable.
You can select value of ##Identity and return is from your stored procedure. Following is a sample code for that.
INSERT INTO Production.Location (Name, CostRate, Availability, ModifiedDate)
VALUES ('Damaged Goods', 5, 2.5, GETDATE());
GO
SELECT ##IDENTITY AS 'Identity';
GO

SQL Server delete trigger returns select statement, how to capture that data?

On a table, there's a delete trigger that performs some operations and then at the end, executes a select statement, so when you do something like...
delete from mytable where id=1
it returns a recordset.
Is there a way to save the results of that recordset into a temp table or something? I tried something like this:
declare #temptable table (returnvalue int);
insert into #temptable (returnvalue)
delete from mytable where id=1;
But apparently that syntax doesn't work.
Well,
I can not imagine a situation that you need to return the recordset of the line you will delete using a trigger returning a recordset. But I am not here to judge your requests.
Well, you can use the OUTPUT to show the row data that will be excluded and enter this data into a temporary table. Follow the example below.
However you should know that: SQL Server does not guarantee the order in Which rows are processed and returned by DML statements using the OUTPUT clause. It is up to the application to include an WHERE clause Appropriate que can guarantee the Desired semantics, or Understand que When multiple rows may qualify for the DML operation, there is guaranteed in order. The Following example uses the subquery and you assume uniqueness is a characteristic of the column in order to DatabaseLogID in Place the Desired ordering semantics. See the link.
Example:
CREATE TABLE Person
(
PersonID int,
LastName varchar(255),
FirstName varchar(255)
);
GO
--DECLARE #MyTablePerson TABLE
--(
-- PersonID int,
-- LastName varchar(255),
-- FirstName varchar(255)
--);
--GO
--CREATE TRIGGER TRG_DLT_Person
--ON Person
--INSTEAD OF DELETE
--AS
--BEGIN
-- Some code you want to do before delete
-- DELETE Person
-- FROM DELETED D
--END
--GO
insert into Person
(PersonID,
LastName,
FirstName)
values
(1,
'Kilmister',
'Lemmy');
GO
insert into Person
(PersonID,
LastName,
FirstName)
values
(2,
'Gilmour',
'David');
GO
insert into Person
(PersonID,
LastName,
FirstName)
values
(3,
'Rose',
'Axl');
GO
insert into Person
(PersonID,
LastName,
FirstName)
values
(4,
'Bullock',
'Sandra');
GO
--
select * from Person;
GO
delete from Person
--output deleted.* INTO #MyTablePerson
output deleted.*
WHERE PersonID = 4 OR PersonID = 2;
GO
select * from Person;
GO
select * from #MyTablePerson;
GO
I put the example I'm showing in a this environment, but in this environment believe that are not supported for temporary tables.
SQL Fiddle
Regardless of this being a bad practice due to it being difficult for anyone interacting with the table to know that it will happen and deal with it when it does, and regardless of it being possible to capture, one pretty solid reason to not return result sets from a trigger is that doing so will be disallowed as of one of the next versions of SQL Server, so you would have to re-code the functionality anyway. The MSDN page for the disallow results from triggers Server Configuration Option states:
Important
This feature will be removed in the next version of Microsoft SQL Server. Do not use this feature in new development work, and modify applications that currently use this feature as soon as possible. We recommend that you set this value to 1.
If you are merely returning something like SELECT IdField FROM deleted; from the trigger, then you should (well, really need to) use the OUTPUT clause instead.
That being said, doing the following will do what you want:
CREATE TABLE #TempResults
(
ReturnValue INT
);
INSERT INTO #TempResults (ReturnValue)
EXEC('DELETE FROM mytable WHERE id = 1;');
You can test with the following:
SET NOCOUNT ON;
IF (OBJECT_ID('dbo.DeleteTriggerWithResults') IS NOT NULL)
BEGIN
DROP TABLE dbo.DeleteTriggerWithResults;
END;
CREATE TABLE dbo.DeleteTriggerWithResults
(
Col1 INT NOT NULL IDENTITY(1, 1),
Col2 DATETIME DEFAULT (GETDATE())
);
GO
CREATE TRIGGER dbo.tr_DeleteTriggerWithResults_d
ON dbo.DeleteTriggerWithResults
AFTER DELETE
AS
BEGIN
SELECT Col1
FROM deleted;
END;
GO
INSERT INTO dbo.DeleteTriggerWithResults DEFAULT VALUES;
GO 30
SELECT * FROM dbo.DeleteTriggerWithResults;
And then run the test:
DECLARE #TempResults TABLE (Col1 INT);
INSERT INTO #TempResults (Col1)
EXEC('
DELETE TOP (10)
FROM dbo.DeleteTriggerWithResults;
');
SELECT * FROM #TempResults;
Returns:
Col1
-------
10
9
8
7
6
5
4
3
2
1

TSQL ID generation

I have a question regarding locking in TSQL. Suppose I have a the following table:
A(int id, varchar name)
where id is the primary key, but is NOT an identity column.
I want to use the following pseudocode to insert a value into this table:
lock (A)
uniqueID = GenerateUniqueID()
insert into A values (uniqueID, somename)
unlock(A)
How can this be accomplished in terms of T-SQL? The computation of the next id should be done with the table A locked in order to avoid other sessions to do the same operation at the same time and get the same id.
If you have custom logic that you want to apply in generating the ids, wrap it up into a user defined function, and then use the user defined function as the default for the column. This should reduce concurrency issue similarly to the provided id generators by deferring the generation to the point of insert and piggy backing on the insert locking behavior.
create table ids (id int, somval varchar(20))
Go
Create function GenerateUniqueID()
returns int as
Begin
declare #ret int
select #ret = max(isnull(id,1)) * 2 from ids
if #ret is null set #ret = 2
return #ret
End
go
alter table ids add Constraint DF_IDS Default(dbo.GenerateUniqueID()) for Id
There are really only three ways to go about this.
Change the ID column to be an IDENTITY column where it auto increments by some value on each insert.
Change the ID column to be a GUID with a default constraint of NEWID() or NEWSEQUENTIALID(). Then you can insert your own value or let the table generate one for you on each insert.
On each insert, start a transaction. Then get the next available ID using something like select max(id)+1 . Do this in a single sql statement if possible in order to limit the possibility of a collision.
On the whole, most people prefer option 1. It's fast, easy to implement, and most people understand it.
I tend to go with option 2 with the apps I work on simply because we tend to scale out (and up) our databases. This means we routinely have apps with a multi-master situation. Be aware that using GUIDs as primary keys can mean your indexes are routinely trashed.
I'd stay away from option 3 unless you just don't have a choice. In which case I'd look at how the datamodel is structured anyway because there's bound to be something wrong.
You use the NEWID() function and you do not need any locking mechanism
You tell a column to be IDENTITY and you do not need any locking mechanism
If you generate these IDs manually and there is a chance parallel calls could generate the same IDs then something like this:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE
#NextID = GenerateUniqueID()
WHILE EXISTS (SELECT ID FROM A WHERE ID = #NextID)
BEGIN
#NextID = GenerateUniqueID()
END
INSERT INTO A (ID, Text) VALUES (#NextID , 'content')
COMMIT TRANSACTION
#Markus, you should look at using either IDENTITY or NEWID() as noted in the other answers. if you absolutely can't, here's an option for you...
DECLARE #NewID INT
BEGIN TRAN
SELECT #NewID = MAX(ID) + 1
FROM TableA (tablockx)
INSERT TableA
(ID, OtherFields)
VALUES (#NewID, OtherFields)
COMMIT TRAN
If you're using SQL2005+, you can use the OUTPUT clause to do what you're asking, without any kind of lock (The table Test1 simulates the table you're inserted into, and since OUTPUT requires a temp table and not a variable to hold the results, #Result will do that):
create table test1( test INT)
create table #result (LastValue INT)
insert into test1
output INSERTED.test into #result(test)
select GenerateUniqueID()
select LastValue from #result
Just to update an old post. It is now possible with SQL Server 2012 to use a feature called Sequence. Sequences are created in much the same way a function and it is possible to specify the range, direction(asc, desc) and rollover point. After which it's possible to invoke the NEXT VALUE FOR method to generate the next value in the range.
See the following documentation from Microsoft.
http://technet.microsoft.com/en-us/library/ff878091.aspx

SQL: How to get the id of values I just INSERTed?

I inserted some values into a table. There is a column whose value is auto-generated. In the next statement of my code, I want to retrieve this value.
Can you tell me how to do it the right way?
##IDENTITY is not scope safe and will get you back the id from another table if you have an insert trigger on the original table, always use SCOPE_IDENTITY()
This is how I do my store procedures for MSSQL with an autogenerated ID.
CREATE PROCEDURE [dbo].[InsertProducts]
#id INT = NULL OUT,
#name VARCHAR(150) = NULL,
#desc VARCHAR(250) = NULL
AS
INSERT INTO dbo.Products
(Name,
Description)
VALUES
(#name,
#desc)
SET #id = SCOPE_IDENTITY();
This works very nicely in SQL 2005:
DECLARE #inserted_ids TABLE ([id] INT);
INSERT INTO [dbo].[some_table] ([col1],[col2],[col3],[col4],[col5],[col6])
OUTPUT INSERTED.[id] INTO #inserted_ids
VALUES (#col1,#col2,#col3,#col4,#col5,#col6)
It has the benefit of returning all the IDs if your INSERT statement inserts multiple rows.
If your using PHP and MySQL you can use the mysql_insert_id() function which will tell you the ID of item you Just instered.
But without your Language and DBMS I'm just shooting in the dark here.
Again no language agnostic response, but in Java it goes like this:
Connection conn = Database.getCurrent().getConnection();
PreparedStatement ps = conn.prepareStatement(insertSql, Statement.RETURN_GENERATED_KEYS);
try {
ps.executeUpdate();
ResultSet rs = ps.getGeneratedKeys();
rs.next();
long primaryKey = rs.getLong(1);
} finally {
ps.close();
}
If you are working with Oracle:
Inset into Table (Fields....) values (Values...) RETURNING (List of Fields...) INTO (variables...)
example:
INSERT INTO PERSON (NAME) VALUES ('JACK') RETURNING ID_PERSON INTO vIdPerson
or if you are calling from... Java with a CallableStatement (sry, it's my field)
INSERT INTO PERSON (NAME) VALUES ('JACK') RETURNING ID_PERSON INTO ?
and declaring an autput parameter for the statement
There's no standard way to do it (just as there is no standard way to create auto-incrementing IDs). Here are two ways to do it in PostgreSQL. Assume this is your table:
CREATE TABLE mytable (
id SERIAL PRIMARY KEY,
lastname VARCHAR NOT NULL,
firstname VARCHAR
);
You can do it in two statements as long as they're consecutive statements in the same connection (this will be safe in PHP with connection pooling because PHP doesn't give the connection back to the pool until your script is done):
INSERT INTO mytable (lastname, firstname) VALUES ('Washington', 'George');
SELECT lastval();
lastval() gives you the last auto-generated sequence value used in the current connection.
The other way is to use PostgreSQL's RETURNING clause on the INSERT statement:
INSERT INTO mytable (lastname) VALUES ('Cher') RETURNING id;
This form returns a result set just like a SELECT statement, and is also handy for returning any kind of calculated default value.
An important note is that using vendor SQL queries to retrieve the last inserted ID are safe to use without fearing about concurrent connections.
I always thought that you had to create a transaction in order to INSERT a line and then SELECT the last inserted ID in order to avoid retrieving an ID inserted by another client.
But these vendor specific queries always retrieve the last inserted ID for the current connection to the database. It means that the last inserted ID cannot be affected by other client insertions as long as they use their own database connection.
For SQL 2005:
Assuming the following table definition:
CREATE TABLE [dbo].[Test](
[ID] [int] IDENTITY(1,1) NOT NULL,
[somevalue] [nchar](10) NULL,
)
You can use the following:
INSERT INTO Test(somevalue)
OUTPUT INSERTED.ID
VALUES('asdfasdf')
Which will return the value of the ID column.
From the site i found out the following things:
SQL SERVER – ##IDENTITY vs SCOPE_IDENTITY() vs IDENT_CURRENT – Retrieve Last Inserted Identity of Record
March 25, 2007 by pinaldave
SELECT ##IDENTITY
It returns the last IDENTITY value produced on a connection, regardless of the table that produced the value, and regardless of the scope of the statement that produced the value.
##IDENTITY will return the last identity value entered into a table in your current session. While ##IDENTITY is limited to the current session, it is not limited to the current scope. If you have a trigger on a table that causes an identity to be created in another table, you will get the identity that was created last, even if it was the trigger that created it.
SELECT SCOPE_IDENTITY()
It returns the last IDENTITY value produced on a connection and by a statement in the same scope, regardless of the table that produced the value.
SCOPE_IDENTITY(), like ##IDENTITY, will return the last identity value created in the current session, but it will also limit it to your current scope as well. In other words, it will return the last identity value that you explicitly created, rather than any identity that was created by a trigger or a user defined function.
SELECT IDENT_CURRENT(‘tablename’)
It returns the last IDENTITY value produced in a table, regardless of the connection that created the value, and regardless of the scope of the statement that produced the value.
IDENT_CURRENT is not limited by scope and session; it is limited to a specified table. IDENT_CURRENT returns the identity value generated for a specific table in any session and any scope.
Remember that ##IDENTITY returns the most recently created identity for your current connection, not necessarily the identity for the recently added row in a table. You should always use SCOPE_IDENTITY() to return the identity of the recently added row.
What database are you using? As far as I'm aware, there is no database agnostic method for doing this.
This is how I've done it using parameterized commands.
MSSQL
INSERT INTO MyTable (Field1, Field2) VALUES (#Value1, #Value2);
SELECT SCOPE_IDENTITY();
MySQL
INSERT INTO MyTable (Field1, Field2) VALUES (?Value1, ?Value2);
SELECT LAST_INSERT_ID();
sql = "INSERT INTO MyTable (Name) VALUES (#Name);" +
"SELECT CAST(scope_identity() AS int)";
SqlCommand cmd = new SqlCommand(sql, conn);
int newId = (int)cmd.ExecuteScalar();
Ms SQL Server: this is good solution even if you inserting more rows:
Declare #tblInsertedId table (Id int not null)
INSERT INTO Test ([Title], [Text])
OUTPUT inserted.Id INTO #tblInsertedId (Id)
SELECT [Title], [Text] FROM AnotherTable
select Id from #tblInsertedId
Rob's answer would be the most vendor-agnostic, but if you're using MySQL the safer and correct choise would be the built-in LAST_INSERT_ID() function.
SELECT ##Scope_Identity as Id
There is also ##identity, but if you have a trigger, it will return the results of something that happened during the trigger, where scope_identity respects your scope.
insert the row with a known guid.
fetch the autoId-field with this guid.
This should work with any kind of database.
An Environment Based Oracle Solution:
CREATE OR REPLACE PACKAGE LAST
AS
ID NUMBER;
FUNCTION IDENT RETURN NUMBER;
END;
/
CREATE OR REPLACE PACKAGE BODY LAST
AS
FUNCTION IDENT RETURN NUMBER IS
BEGIN
RETURN ID;
END;
END;
/
CREATE TABLE Test (
TestID INTEGER ,
Field1 int,
Field2 int
)
CREATE SEQUENCE Test_seq
/
CREATE OR REPLACE TRIGGER Test_itrig
BEFORE INSERT ON Test
FOR EACH ROW
DECLARE
seq_val number;
BEGIN
IF :new.TestID IS NULL THEN
SELECT Test_seq.nextval INTO seq_val FROM DUAL;
:new.TestID := seq_val;
Last.ID := seq_val;
END IF;
END;
/
To get next identity value:
SELECT LAST.IDENT FROM DUAL
In TransactSQL, you can use OUTPUT clause to achieve that.
INSERT INTO my_table(col1,col2,col3) OUTPUT INSERTED.id VALUES('col1Value','col2Value','col3Value')
FRI: http://msdn.microsoft.com/en-us/library/ms177564.aspx
Simplest answer:
command.ExecuteScalar()
by default returns the first column
Return Value
Type: System.Object
The first column of the first row in the result set, or a null reference (Nothing in Visual Basic) if the result set is empty. Returns a maximum of 2033 characters.
Copied from MSDN

In SQL Server is it possible to get "id" of a record when Insert is executed?

In SQL Server 2005 I have an "id" field in a table that has the "Is Identity" property set to 'Yes'. So, when an Insert is executed on that table the "id" gets set automatically to the next incrementing integer. Is there an easy way when the Insert is executed to get what the "id" was set to without having to do a Select statement right after the Insert?
duplicate of:
Best way to get identity of inserted row?
In .Net at least, you can send multiple queries to the server in one go. I do this in my app:
command.CommandText = "INSERT INTO [Employee] (Name) VALUES (#Name); SELECT SCOPE_IDENTITY()";
int id = (int)command.ExecuteScalar();
Works like a charm.
If you're inserting multiple rows, the use of the OUTPUT and INSERTED.columnname clause on the insert statement is a simple way of getting all the ids into a temp table.
DECLARE #MyTableVar table( ID int,
Name varchar(50),
ModifiedDate datetime);
INSERT MyTable
OUTPUT INSERTED.ID, INSERTED.Name, INSERTED.ModifiedDate INTO #MyTableVar
SELECT someName, GetDate() from SomeTable
Scope_identity() is the preferred way, see: 6 Different Ways To Get The Current Identity Value
SCOPE_IDENTITY(); is your best bet. And if you are using .NET just pass an our parameter and check the value after the procedure is run.
CREATE PROCEDURE [dbo].[InsertProducts]
#id INT = NULL OUT,
#name VARCHAR(150) = NULL,
#desc VARCHAR(250) = NULL
AS
INSERT INTO dbo.Products
(Name,
Description)
VALUES
(#name,
#desc)
SET #id = SCOPE_IDENTITY();
You have to select the scope_identity() function.
To do this from application code, I normally encapsulate this process in a stored procedure so it still looks like one query to my application.
I tend to prefer attaching a trigger to the table using enterprise manager. That way you don't need to worry about writing out extra sql statements in your code. Mine look something like this:
Create Trigger tblName
On dbo.tblName
For Insert
As
select new_id = ##IDENTITY
Then, from within your code, treat your insert statements like select statements- Just execute and evaluate the results. the "newID" column will contain the identity of the row you just created.
This is probably the best working solution I found for SQL Server..
Sql Server return the value of identity column after insert statement

Resources