SQL Server understand SCOPE_IDENTITY() - sql-server

I have this piece of code in a stored procedure:
BEGIN
SET #UserId = NULL;
IF (#Username IS NOT NULL)
BEGIN
EXECUTE SP_ADD_USER #Username, #UserId OUTPUT;
END
EXECUTE SP_ADD_ALERT #Name, #AlertType, #AlertId OUTPUT;
INSERT INTO AlertLogs (Datastamp, AlertID, UserID, NotificationMessage)
VALUES (#Datastamp, #AlertId, #UserId, #EmailMessage);
SET #AlertLogId = SCOPE_IDENTITY();
END
#AlertLogId is an output parameter that I want to be assigned to the result of the last insert in AlertLogs table. Do I have to include
INSERT INTO AlertLogs (Datastamp, AlertID, UserID, NotificationMessage)
VALUES (#Datastamp, #AlertId, #UserId, #EmailMessage);
in a new block (a new begin/end scope) in order for SCOPE_IDENTITY() to work correctly ?
(and not report for example the last ID of an inserted record done in SP_ADD_ALERT for example ?)

In your query, SCOPE_IDENTITY() is going to return the last entered identity value into the database, for this scope.
In this instance, it will be the identity for the AlertLogs table, if this has an identity.
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.
http://msdn.microsoft.com/en-us/library/ms190315.aspx

You can also use an OUTPUT clause in your insert statement. This means you don't need to worry about scope and you make other (non-identity) information available from the inserted table.
Consider this simple table:
CREATE TABLE [dbo].[SampleTable](
[ID] [int] IDENTITY(1,1) NOT NULL,
[InsertDate] [datetime] NOT NULL,
[Name] [nvarchar](100) NULL
) ON [PRIMARY]
With this default added:
ALTER TABLE [dbo].[SampleTable]
ADD CONSTRAINT [DF_SampleTable_Inserted]
DEFAULT (getdate()) FOR [InsertDate]
You can get values for both the default and the identity from the insert operation.
DECLARE #InsertedDetails TABLE (ID int, InsertDate DateTime);
INSERT INTO SampleTable ([Name])
OUTPUT inserted.ID, inserted.InsertDate
INTO #InsertedDetails
VALUES ('Fred');
DECLARE #ID int;
DECLARE #InsertDate datetime;
SELECT #ID = ID, #InsertDate = InsertDate FROM #InsertedDetails;
PRINT #ID;
PRINT #InsertDate;
Here I've just pulled the values out of the table variable and printed them.

Related

CURSOR with a stored procedure with OUTPUT parameters

I have to insert one record at a time into a table and then execute a stored procedure by passing output variables.
The insert part actually does do the this and I see two different records into the destination table. But the stored procedure seems to use the same out parameters that were passed for the very first record inserted into destination table. So basically when a stored procedures is being called in loops over the same output parameters over and over again for each distinct ID that is being inserted into the destination table.
In my pseudocode below it prints 3 times the following 5, 10, 15 . Which is correct since it takes each new ID in the dbo.Table_Test. But in my actual code actually it does take only only very first ID that repeats looping over the same ID three times.
-------- CREATING STORED PROCEDURE --------
USE MyDB;
GO
DROP PROCEDURE IF EXISTS dbo.sp_Testing
USE MyDB;
GO
CREATE PROCEDURE dbo.sp_Testing
#QueueId INT,
-- response
#MainId INT OUT, -- this allows null
#MessageTx VARCHAR(500) OUT,
#SuccessIn BIT OUT
AS
BEGIN
SET NOCOUNT ON;
IF NOT EXISTS (SELECT * FROM MyDB.sys.tables WHERE name = 'Table_Test') --print 1
CREATE TABLE dbo.Table_Test
(
ID INT NOT NULL PRIMARY KEY IDENTITY(5,5),
name VARCHAR(10) NULL,
Phone INT NULL,
category VARCHAR(10) NULL
)
INSERT INTO dbo.Table_Test (name)
VALUES ('Andrew')
SET #MainId = SCOPE_IDENTITY()
PRINT #MainId
END
-------- END OR STORED PROCEDURE --------
GO
-------- INSERT STATEMENTS ---------
USE MyDB;
IF OBJECT_ID('tempdb..#MainTable') IS NOT NULL
DROP TABLE #MainTable
IF OBJECT_ID('tempdb..#Queue') IS NOT NULL
DROP TABLE #Queue
DECLARE #MessageTx VARCHAR(30)
DECLARE #SuccessIn BIT
DECLARE #QueueId INT
DECLARE #MainId INT
DECLARE #ParentId INT
SET #MainId = NULL
SET #SuccessIn = 1
CREATE TABLE #MainTable
(
ID INT NOT NULL PRIMARY KEY IDENTITY(1,1),
name VARCHAR(10) NULL,
Phone INT NOT NULL,
category VARCHAR(10) NULL
)
INSERT INTO #MainTable (name, Phone, category)
VALUES ('Adam', 123433, 'new'),
('John', 222222, 'new'),
('Samuel', 123123313, 'new')
-- SELECT * FROM #MainTable
-- SELECT * FROM #Queue
-- SELECT * FROM #Test
DECLARE Cursor_test CURSOR LOCAL FOR
SELECT id
FROM #MainTable
-- get relationships for next level
OPEN Cursor_test
FETCH NEXT FROM Cursor_test INTO #ParentId
WHILE ##FETCH_STATUS = 0
BEGIN
IF OBJECT_ID('tempdb..#Queue') IS NOT NULL
DROP TABLE #Queue
CREATE TABLE #Queue
(
PK INT NOT NULL PRIMARY KEY identity (3,2),
ID INT NOT NULL
)
INSERT INTO #Queue (id)
SELECT id
FROM #MainTable
SET #QueueId = SCOPE_IDENTITY()
-- real-time creation
EXEC dbo.sp_Testing #QueueId, #MainId, #MessageTx OUT, #SuccessIn OUT
FETCH NEXT FROM Cursor_test INTO #ParentId
END
CLOSE Cursor_test
DEALLOCATE Cursor_test
This is a bit too long to be in the comment.
Firstly you must understand that the temp table #test only exists within the stored procedure. It is created in your stored procedure and dropped once the stored procedure exits.
So every time you execute the stored procedure, it creates the temp table, when you insert the row, it return the same identity seed value which is 5.

Read INSERT output from stored procedure

I have a following stored procedure
CREATE PROCEDURE [dbo].[InsertCategory]
#Name nvarchar(100)
AS
BEGIN
INSERT INTO [dbo].[Category]([Name])
OUTPUT INSERTED.CategoryId, INSERTED.[Timestamp]
VALUES (#Name)
END
And I call it like this:
EXEC [dbo].[InsertCategory] #Name= #Name
I would like to know what the id of inserted Category is (it is output in insert statement). It is written to the output, but I can't figure out how to assign it to a variable without modifying stored procedure. In C# I can use command.ExecuteReader and I get it, but I do not know how to get it in SQL Server.
I also cannot use SCOPE_IDENTITY as we have our own system of generating ids.
Try this:
-- count use a temp table as well
-- syntax: CREATE TABLE #t(CategoryId int,[Timestamp] datetime)
DECLARE #t table(CategoryId int,[Timestamp] datetime)
INSERT #t(CategoryId, [TimeStamp])
EXEC [dbo].[InsertCategory] #Name= #Name
SELECT CategoryId, [TimeStamp]
FROM #t
You can Declare a table and insert output into it.
CREATE PROCEDURE [dbo].[InsertCategory]
#Name nvarchar(100)
AS
BEGIN
DECLARE #Result AS TABLE (
CategoryId int,
TimeStamp varchar(50)
)
INSERT INTO [dbo].[Category]([Name])
OUTPUT INSERTED.CategoryId, INSERTED.[Timestamp]
INTO #Result(CategoryId, TimeStamp)
VALUES (#Name)
SElect * from #Result
END

How to copy exactly same schema of one table to another table using stored procedure

Initially, I'll be storing all columns of source table to single variable using stored procedure(SP1). This SP1 is called inside SP2 and that variable is inserted into destination table.
I know the approach but stuck in implementing it.
-- SP1
Create procedure insert_data
AS
Declare #data nvarchar(60)
Begin
EXEC get
--Here i have to insert
END
Go
--SP2
Create procedure get_column
AS
Declare
#e_id int , #e_name nvarchar(20) , #d_id int,#res nvarchar(50)
Begin
SET #e_id =(select Emp_ID from Dim_Employee where Emp_Name='Cathy');
SET #e_name =(select Emp_Name from Dim_Employee where emp_id=101);
SET #d_id =(select Dept_ID from Dim_Employee where emp_name='Cathy');
SET #res ='#e_id , #e_name , #d_id';
return #res
END
Go
if you already have destination table:
insert into DestTbl(emp_id, dept_id, ...)
select emp_id, dept_id, ...
from Dim_Employee d
where ...
if you dont and wish to create it
select emp_id, dept_id, ...
into DestTbl
from Dim_Employee d
where ...
if you still want to use variables
insert into DestTbl(emp_id, dept_id)
values (#emp_id, #dept_id, ...)
is this what you're asking for?
INSERT statement on msdn:
https://technet.microsoft.com/en-us/library/ms174335.aspx

Returning Scope_Identity from 2 insert statements simultaneously in SQL Server

I am having problem with my stored procedure:
CREATE PROCEDURE [dbo].[Project]
#Code as nvarChar(255) = null,
#Id as nvarChar(255) = null,
#Status as nvarChar(max) = null,
#Project as nvarChar(max) = null,
#ClientSystem as nvarchar(max) = null,
#UserId as bigint = 0,
#ProjectId as bigint = 0,
#ProjectDetailsId bigint = 0 Output
AS
SET NOCOUNT OFF;
IF NOT EXISTS (SELECT [Code]
FROM [dbo].[Project]
WHERE Project.Code = #Code)
BEGIN
INSERT INTO [dbo].[Project]([Code], [Id], [Status], [Project])
VALUES(#Code, #Id, #Status, #Project)
SELECT #ProjectId = SCOPE_IDENTITY()
INSERT INTO [dbo].[ProjectDetails]([FK_ProjectId], [ClientSystem], [UserId])
VALUES(#ProjectId, #ClientSystem, #UserId)
SELECT #ProjectDetailsId = SCOPE_IDENTITY()
END
ELSE
BEGIN
SELECT [ProjectId] AS 'ProjectId'
FROM [dbo].[Project]
WHERE Project.Code = #Code
END
I want to return Scope_Identity from both Insert statements and pass the values of first insert as parameter to 2nd Insert and return the Scope_Identity of 2nd Insert statement also.
I am getting error is when I get the identity of first Insert, the identity in the specific table increases 2 times like in db table it will be inserted 2 but in coding it will return 1. And that return when i pass to other insert it s giving conflict.
Solution: Instead of using SCOPE IDENTITY(), you need to make use of he OUTPUTclause of the INSERT statement, like this:
INSERT INTO [dbo].[Project]([Code], [Id], [Status], [Project])
OUTPUT inserted.ID into #ProjectID
SELECT ...
Explanation: SCOPE_IDENTITY() returns the value of the last insert, regardless where the insert takes place. So, when when another insert is running in parallel, then your call to SCOPE_IDENTITY() will return the value from the other parallel running procedure. This then leads to an error.
However, the usage of the OUTPUT clause will guarantee to return the value from the current INSERT.
Here is an interesting article regarding SCOPE_IDENTITY and parallel plans:
http://blog.sqlauthority.com/2009/03/24/sql-server-2008-scope_identity-bug-with-multi-processor-parallel-plan-and-solution/
You need use OUTPUT clause at the procedure parameter
#ProjectId as bigint = 0 output,

How to insert values in to 2 tables, where the 2nd table need an Id from the 1st table?

I have an ordering system where when a new order is placed it is inserted into my table Orders. From there I want to insert the new id into another table Importance which also needs an id from a third table called ImportanceRating.
Table structures:
Order
OrderId uniqueidentifier
TimeOrderPlaced datetime
ProductId uniqueidentifier
EstimatedDeliveryTime datetime
Importance
FK_OrderId uniqueidentifier
FK_ImpRatingId uniqueidentifier
ImportanceRating
ImpRatingId uniqueidentifier
RatingTitle varchar(50)
All of this I want merged in 1 stored procedure. How would I go about with this?
Links to good guides on the subject is more than welcome.
I'm a SPROC newbie
Could you try this?:
CREATE PROCEDURE AddOrderAndRatingSample
-- These are the values you want to insert
#paramTimeOrderPlaced DATETIME
, #paramProductId INT
, #paramEstimatedDeliveryTime DATETIME
, #paramRatingTitle VARCHAR(50)
AS
BEGIN
DECLARE #siOrderId INT
DECLARE #siImpRatingId INT
-- Assuming that `OrderId` in table `Order` is an `identity column`:
INSERT INTO Order (TimeOrderPlaced, ProductId, EstimatedDeliveryTime)
VALUES(#paramTimeOrderPlaced, #paramProductId, #paramEstimatedDeliveryTime)
SET #siOrderId = SCOPE_IDENTITY()
-- Assuming `ImpRatingId` in table `ImportanceRating` is an `identity column`:
INSERT INTO ImportanceRating (RatingTitle)
VALUES(#paramRatingTitle)
SET #siImpRatingId = SCOPE_IDENTITY()
-- And that both `FK_OrderId` and `FK_ImpRatingId`
-- in table `Importance` are not `identity columns`:
INSERT INTO Importance (FK_OrderId, FK_ImpRatingId)
SELECT #siOrderId, #siImpRatingId
END
Could you please try this way:
DECLARE #OrderId INT
INSERT INTO Order (TimeOrderPlaced, ProductId, EstimatedDeliveryTime)
VALUES(#paramTimeOrderPlaced, #paramProductId, #paramEstimatedDeliveryTime)
SET #OrderId = ##IDENTITY -- Last orderId
INSERT INTO ImportanceRating (RatingTitle)
VALUES(#paramRatingTitle)
INSERT INTO Importance (FK_OrderId, FK_ImpRatingId)
SELECT #OrderId, ##IDENTITY -- Here ##IDENTITY returns last ID of ImportanceRating
-- Each inserting time the global variable ##IDENTITY is set with last IDENTITY value

Resources