I am adding a trigger to an existing database table, which is used by an application I did not code, and cannot change the code in.
I want to insert some information from TableA into TableB on INSERT into TableA.
Application runs INSERT INTO TableA <-- this updates ##identity
An ON INSERT trigger that runs on TableA then inserts data into TableB <-- this also updates the ##identity with a new value
Application reads ##identity <-- which is from TableB not from TableA as the application is expecting
Is there any way to not update the ##identity from within the trigger?
....since ##identity has no scope you could create your own scope which carries the ##identity value at the end of the trigger
create table tableA(idA int identity(100, 1), colA int)
go
create table tableB(idB int identity(1000, 1), colB int)
go
create trigger triggerA on tableA
for insert
as
begin
if not exists(select * from inserted)
begin
return;
end
declare #tableA##identity int = ##identity;
select ##identity as [##identity_triggerA_in];
--add rows to tableB
insert into tableB(colB)
select object_id
from sys.all_objects
select ##identity as [##identity_after_insert_in_tableB];
if #tableA##identity is not null
begin
declare #sql varchar(100) = concat('create table #t(id int identity(', #tableA##identity, ',1)); insert into #t default values');
exec (#sql);
end
select ##identity as [##identity_triggerA_out];
end
go
insert into tableA(colA) values (10);
select ##identity;
go
insert into tableA(colA)
select top (200) 1
from sys.all_objects;
select ##identity;
go
insert into tableA(colA)
select 1
where 1=2;
select ##identity;
go
drop table tableA;
go
drop table tableB;
go
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
I know that the SCOPE_IDENTITY() will get the last inserted row from insert statement. However, for the following case, I am not too sure is SCOPE_IDENTITY() is safe. As SELECT MAX(ID) FROM TableA will have go through scan the table to get the max id and it will have performance issue, even slightly, I believe.
Here is the case:
DECLARE #DaysInMonth INT
DECLARE #FirstID INT
DECLARE #SecondID INT
DECLARE #ThirdID INT
DECLARE #FourthID INT
SET #DaysInMonth = DAY(EOMONTH('2016-09-01'))
BEGIN TRY
BEGIN TRANSACTION
WHILE #DaysInMonth > 0
BEGIN
-- First Insert -- Begin
INSERT INTO tableA ('first insert - ' + #DaysInMonth)
-- First Insert -- End
SET #FirstID = SCOPE_IDENTITY()
-- Second Insert -- Begin
INSERT INTO tableB ('second insert - ' + #DaysInMonth)
-- Second Insert -- End
SET #SecondID = SCOPE_IDENTITY()
-- Third Insert -- Begin
INSERT INTO tableC ('third insert - ' + #DaysInMonth)
-- Third Insert -- End
SET #ThirdID = SCOPE_IDENTITY()
-- Fourth Insert -- Begin
INSERT INTO tableD ('fourth insert - ' + #DaysInMonth)
-- Fourth Insert -- End
SET #FourthID = SCOPE_IDENTITY()
SET #DaysInMonth = #DaysInMonth - 1
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION
THROW
END CATCH
As from the case above, I have to insert the records every loop for fourth times for how many days in the month that I have declared.
From what I know, there are 4 to get the last inserted ID:
SCOPE_IDENTITY
##IDENTITY
SELECT MAX(ID) FROM tableA
IDENT_CURRENT
From the following post:
Post
Is mentioned that SCOPE_IDENTITY() is what generally that you want to use.
What I mean with 'Safe' is, do the ID will be unique during the loop?
Thank you.
You can use OUTPUT column in the last insert statement, Ofcourse this is another option where you will get what exactly input statement executed.. Below is just an example
CREATE TABLE #tablea (
id int IDENTITY (1, 1),
val char(10)
)
DECLARE #outputtbl TABLE (
id int,
val char(10)
)
INSERT INTO #tablea (val)
OUTPUT INSERTED.* INTO #outputtbl
VALUES ('test')
SELECT id
FROM #outputtbl
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
I have a requirement to insert multiple rows into table1 and at the same time insert a row into table2 with a pkID from table1 and a value that comes from a SP parameter.
I created a stored procedure that performs a batch insert with a table valued parameter which contains the rows to be inserted into table1. But I have a problem with inserting the row into table2 with the corresponding Id (identity) from table1, along with parameter value that I have passed.
Is there anyone who implemented this, or what is the good solution for this?
CREATE PROCEDURE [dbo].[oSP_TV_Insert]
#uID int
,#IsActive int
,#Type int -- i need to insert this in table 2
,#dTableGroup table1 READONLY -- this one is a table valued
AS
DECLARE #SQL varchar(2000)
DECLARE #table1Id int
BEGIN
INSERT INTO dbo.table1
(uID
,Name
,Contact
,Address
,City
,State
,Zip
,Phone
,Active)
SELECT
#uID
,Name
,Contact
,Address
,City
,State
,Zip
,Phone
,Active
,#G_Active
FROM #dTableGroup
--the above query will perform batch insert using the records from dTableGroup which is table valued
SET #table1ID = SCOPE_IDENTITY()
-- this below will perform inserting records to table2 with every Id inserted in table1.
Insert into table2(#table1ID , #type)
You need to temporarily store the inserted identity values and then create a second INSERT statement - using the OUTPUT clause.
Something like:
-- declare table variable to hold the ID's that are being inserted
DECLARE #InsertedIDs TABLE (ID INT)
-- insert values into table1 - output the inserted ID's into #InsertedIDs
INSERT INTO dbo.table1(ID, Name, Contact, Address, City, State, Zip, Phone, Active)
OUTPUT INSERTED.ID INTO #InsertedIDs
SELECT
#ID, Name, Contact, Address, City, State, Zip, Phone, Active, #G_Active
FROM #dTableGroup
and then you can have your second INSERT statement:
INSERT INTO dbo.table2(Table1ID, Type)
SELECT ID, #type FROM #InsertedIDs
See the MSDN docs on the OUTPUT clause for more details on what you can do with the OUTPUT clause - one of the most underused and most "unknown" features of SQL Server these days!
Another approach using OUTPUT clause and only one statement for inserting data in both destination tables:
--Parameters
DECLARE #TableGroup TABLE
(
Name NVARCHAR(100) NOT NULL
,Phone VARCHAR(10) NOT NULL
);
DECLARE #Type INT;
--End Of parameters
--Destination tables
DECLARE #FirstDestinationTable TABLE
(
FirstDestinationTableID INT IDENTITY(1,1) PRIMARY KEY
,Name NVARCHAR(100) NOT NULL
,Phone VARCHAR(10) NOT NULL
);
DECLARE #SecondDestinationTable TABLE
(
SecondDestinationTable INT IDENTITY(2,2) PRIMARY KEY
,FirstDestinationTableID INT NOT NULL
,[Type] INT NOT NULL
,CHECK([Type] > 0)
);
--End of destination tables
--Test1
--initialization
INSERT #TableGroup
VALUES ('Bogdan SAHLEAN', '0721200300')
,('Ion Ionescu', '0211002003')
,('Vasile Vasilescu', '0745600800');
SET #Type = 9;
--execution
INSERT #SecondDestinationTable (FirstDestinationTableID, [Type])
SELECT FirstINS.FirstDestinationTableID, #Type
FROM
(
INSERT #FirstDestinationTable (Name, Phone)
OUTPUT inserted.FirstDestinationTableID
SELECT tg.Name, tg.Phone
FROM #TableGroup tg
) FirstINS
--check records
SELECT *
FROM #FirstDestinationTable;
SELECT *
FROM #SecondDestinationTable;
--End of test1
--Test2
--initialization
DELETE #TableGroup;
DELETE #FirstDestinationTable;
DELETE #SecondDestinationTable;
INSERT #TableGroup
VALUES ('Ion Ionescu', '0210000000')
,('Vasile Vasilescu', '0745000000');
SET #Type = 0; --Wrong value
--execution
INSERT #SecondDestinationTable (FirstDestinationTableID, [Type])
SELECT FirstINS.FirstDestinationTableID, #Type
FROM
(
INSERT #FirstDestinationTable (Name, Phone)
OUTPUT inserted.FirstDestinationTableID
SELECT tg.Name, tg.Phone
FROM #TableGroup tg
) FirstINS
--check records
DECLARE #rc1 INT, #rc2 INT;
SELECT *
FROM #FirstDestinationTable;
SET #rc1 = ##ROWCOUNT;
SELECT *
FROM #SecondDestinationTable;
SET #rc2 = ##ROWCOUNT;
RAISERROR('[Test2 results] #FirstDestinationTable: %d rows; ##SecondDestinationTable: %d rows;',1,1,#rc1,#rc2);
--End of test1
Since you need all inserted identity values, look at the output clause of the insert statement: http://msdn.microsoft.com/en-us/library/ms177564.aspx