I try to create a Trigger that will insert a Unique ID in a table and then get the created ID and use it on the table that "Triggered the trigger"
So I insert some data in my table and then I want the trigger to insert some data in another table (master ID table), it then needs to get the just created ID (auto increment INT) and add this ID to the table ID that the trigger fired on.
I tried quite some different options but it just does not insert the ID and it will give me an error.
Hope you can help me out.
CREATE TRIGGER [dbo].[TROidMemoDiaryHeader]
ON [dbo].[InvoiceDiaryHeader]
FOR INSERT
AS
DECLARE #TEMP uniqueidentifier, #Lock INT, #Object INT, #User V ARCHAR(MAX), #Delay NCHAR(12), #ID INT
SET #TEMP = NEWID()
SET #Lock = '0' --staat geen wijziging meer toe
SET #Object = '5' -- aanduiding bron
SET #User = CURRENT_USER
--SET #Delay = '00:00:00:125' -- Wacht voor een kwart seconde voor verder te gaan
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
--SET NOCOUNT ON;
INSERT INTO [dbo].[AbstractMBOBase] ([Uid], [OptimisticLockField], [ObjectType], [User])
SELECT #TEMP, #Lock, #Object, #User
IF EXISTS (SELECT [Uid] FROM [dbo].AbstractMBOBase WHERE [Uid] = #TEMP)
BEGIN
INSERT INTO [InvoiceDiaryHeader](Oid)
SELECT Oid FROM inserted
/*
SELECT * INTO #tmpOID FROM AbstractMBOBase WHERE [Uid] = #TEMP
UPDATE [dbo].InvoiceDiaryHeader SET Oid = (SELECT a.[Oid] FROM [dbo].[AbstractMBOBase] AS a
WHERE [Uid] = #TEMP)
--CREATE TABLE [dbo].#TempOID()
--SELECT * INTO #TempOID FROM INSERTED
*/
/*
--UPDATE [MemoDiaryHeader]
SET #ID = (SELECT a.[Oid] FROM [dbo].[AbstractMBOBase] AS a
WHERE [Uid] = #TEMP)
INSERT INTO [MemoDiaryHeader]([Oid])
SELECT #ID
--FROM inserted
--select [Oid]=i.[Oid] from inserted i
*/
END
--END
END
GO
Related
I created the following two test tables with a trigger to log all the action (Insert, Delete and Update).
Set up tables and trigger:
-- drop table test; drop table testLog
create table test (id int identity primary key, x int);
create table testLog (idx int identity primary key, Action varchar(10), id int not null,
x_deleted int, x_inserted int, uid uniqueidentifier);
go
-- Trigger to log the changes
create trigger trigger_test on test
after insert, delete, update
as
declare #id uniqueidentifier = context_info();
print #id;
insert testLog (id, Action, x_deleted, x_inserted, uid)
select isnull(d.id, i.id) ,
case when i.id is not null and d.id is not null then 'Updated'
when d.id is not null then 'Deleted'
when i.id is not null then 'Inserted'
end ,
d.x ,
i.x ,
#id
from Deleted d
full outer join inserted i on i.id = d.id;
set context_info 0;
go
Now insert some sample data
set context_info 0
insert test (x) values (10), (20), (30), (40), (50);
SELECT * FROM test;
SELECT * FROM testLog
go
The following statements work fine. The correct context_info() is saved in the log table.
begin tran
declare #newid uniqueidentifier = newid()
--
set context_info #newid
print #newid
insert test(x) values (1)
set context_info #newid
update test set x = 2 where id = 1
SELECT * FROM dbo.testLog;
rollback
go
However, only insert part of the Merge got the value in context_info()?
begin tran
declare #newid uniqueidentifier = newid()
--
set context_info #newid
print #newid;
with v as (select * from (values (1, 11), (2, 22), (6, 66)) v (id, x))
merge test as t using v on t.id = v.id
when matched then update set x = v.x
when not matched by target then insert (x) values (x);
SELECT * FROM dbo.testLog;
rollback
go
The uid of the last two updates got zeros.
Don't set context_info to zero in the trigger. Why would you do that in the first place - it is not the trigger's responsibility to "clean up". The merge statement will cause the trigger to execute for inserts separately from updates. Did you not notice the multiple "prints" in the results pane? That should have been a big clue.
I have created a trigger for a asset_verification. Whenever a new record is inserted in this table, the same record is inserted in the asset_verification_history table because of this trigger.
The trigger is as follows
Create trigger [dbo].[tr_insert_after_asset_verification] on [dbo].[asset_verification]
for insert
As
Begin
declare #verification_id int
declare #id int
declare #audit_id int
declare #date date
declare #status varchar(15)
declare #remarks varchar(200)
declare #creationDate datetime
declare #modificationDate datetime
declare #updatedBy int
declare #audit_action varchar(20)
Select #verification_id = i.verification_id from inserted i
If #verification_id IS NOT NULL
Begin
Select #id = i.id from inserted i
Select #audit_id = i.audit_id from inserted i
Select #date = i.date from inserted i
Select #status = i.status from inserted i
Select #remarks = i.remarks from inserted i
Select #creationDate = i.creationDate from inserted i
Select #modificationDate = i.modificationDate from inserted i
Select #updatedBy = i.updatedBy from inserted i
set #audit_action = 'Insert Record'
INSERT INTO [dbo].[asset_verification_history]
([verification_id]
,[id]
,[audit_id]
,[date]
,[status]
,[remarks]
,[creationDate]
,[modificationDate]
,[updatedBy]
,[audit_action])
VALUES
(#verification_id
,#id
,#audit_id
,#date
,#status
,#remarks
,#creationDate
,#modificationDate
,#updatedBy
,#audit_action)
End
End
When I insert the data in the asset_verification table using a procedure in which OPEN XML is used, then this trigger works only for the first record. For the rest of the records the trigger doesn't work
The procedure is as follows
Create procedure [dbo].[usp_AddVerificationBulkData]
(
#vXML XML
)
As
Begin
DECLARE #DocHandle INT
SET NOCOUNT ON
EXEC sp_xml_preparedocument #DocHandle OUTPUT, #vXML
Update asset_verification
set
audit_id = x.AuditId,
id = x.SerialId,
date = x.VerificationDate,
status = x.Status
,remarks = x.Remarks
,creationDate = x.CreatedOn
,modificationDate = x.ModifiedOn
,updatedBy = x.ModifiedBy
From
asset_verification a
Inner Join
OpenXml(#DocHandle,'/ArrayOfAssetVerificationModel/AssetVerificationModel',2)
With(SerialId int, AuditId int, VerificationDate datetime, Status int, Remarks varchar(200), CreatedOn datetime, ModifiedOn datetime, ModifiedBy int) x
On a.audit_id = x.AuditId where a.id = x.SerialId;
INSERT INTO [dbo].[asset_verification]
([id]
,[audit_id]
,[date]
,[status]
,[remarks]
,[creationDate]
,[modificationDate]
,[updatedBy])
select SerialId,AuditId,VerificationDate,Status,Remarks,CreatedOn,ModifiedOn,ModifiedBy from OpenXml(#DocHandle,'/ArrayOfAssetVerificationModel/AssetVerificationModel',2)
With(SerialId int, AuditId int, VerificationDate datetime, Status int, Remarks varchar(200), CreatedOn datetime, ModifiedOn datetime, ModifiedBy int) x
where SerialId NOT IN (select a.id from asset_verification a where a.audit_id = x.AuditId);
End
Problem:- How to make this trigger work for every record that is inserted through Open XML ?
You've made the classic mistake of thinking that triggers fire once-per-row. They dont, it's once-per-action, so the inserted pseudo table holds all the rows affected by the action. Your trigger needs to work in a set based manner, not row based. Try this;
CREATE TRIGGER [dbo].[tr_insert_after_asset_verification] ON [dbo].[asset_verification] FOR INSERT AS
BEGIN
SET NOCOUNT ON
INSERT INTO [dbo].[asset_verification_history]
( [verification_id]
,[id]
,[audit_id]
,[date]
,[status]
,[remarks]
,[creationDate]
,[modificationDate]
,[updatedBy]
,[audit_action]
)
SELECT i.verification_id
,i.id
,i.audit_id
,i.date
,i.status
,i.remarks
,i.creationDate
,i.modificationDate
,i.updatedBy
,'Insert Record'
FROM inserted i
WHERE i.verification_id IS NOT NULL
END
As an aside, and strictly speaking, your original trigger will log one row, not necessarily the first.
I have two stored procedures as follows:
create stored procedure p1
as
select * from table1 where datediff(day, table1.[date], getdate())
create stored procedure p2
as
declare #t1 table(
ref varchar(20)
)
insert into #t1 select * from table1 where ref = 'some ref'
declare #t2 table(
fname varchar(20),
lname varchar(20),
email varchar(1000)
)
declare #len int = (select count(ref) from #t1)
while #len > 0
begin
declare #value varchar(20) = (select top 1 ref from #t1)
insert into #t2 select * from table2 where ref = #ref
delete from #t1
where ref = #value
set #len = (select count(ref) from #t1)
end
select * from #t2
Java code
....
String query = "Execute [p2]";
try(CallableStatement cstmt = conn.prepareCall(query);
ResultSet rs = cstmt.executeQuery()){
... some code
}
The table variable #t1 hold select result from a table 'table1'
The variable #len hold the number of rows in #t1
Using #len > 0 as condition in while loop, I want to select records from another table 'table2' the table variable #t2 hold the select records from 'table2'
The delete statement removes value from #t1
#len set to new number of rows in #t1
the last statement return all the records store in #t2
The first procedure works fine, but the second procedure works only in SQL Server.
I get this an error message in my java application
statement did not return a resultset
I want this to return a result set with the select statement I have at the
end of the query.
Please is there a way around this?
Your [p2] stored procedure needs to include SET NOCOUNT ON right at the beginning to suppress the "n rows affected" counts so JDBC doesn't get confused as to what it should put into the ResultSet:
CREATE PROCEDURE p2
AS
SET NOCOUNT ON;
declare #t1 table(
ref varchar(20)
)
-- ... and so on
For more information on SET NOCOUNT see
SET NOCOUNT (Transact-SQL)
For more information on precisely what gets returned from a stored procedure call, see
How to get everything back from a stored procedure using JDBC
use method "execute" instead of "executeQuery".
I have some SQL within a stored procedure where I am updating a table based on another SELECT statement from a temp table (code below).
SET NOCOUNT ON
DECLARE #RowCount int
UPDATE TABLEX SET
TRA = ISNULL (ir.DcTra, DCBASIC.TRA),
TRD = ISNULL(CAST(NULLIF(REPLACE(ir.DcTRD, '-', ''), '') AS datetime), DCBASIC.TRD),
LSINC = ISNULL(ir.DcLsInc, DCBASIC.LSINC),
REVSWOVR = ISNULL(ir.DcRevswovr, DCBASIC.REVSWOVR) FROM #TempData ir WHERE TABLEX.MEMBNO = ir.IntMembNo
SET #RowCount = ##ROWCOUNT
The #RowCount variable is being set to 1.
The SELECT of the #TempData table returns no rows and no rows in the TABLEX table are updated (or even exist) with the MembNo (I have added SELECT statements within the sp to debug and they confirm this)
Why is #RowCount being set to 1?
Here is an explanation:
Statements that make a simple assignment always set the ##ROWCOUNT value to 1.
More information you can find here:
##ROWCOUNT
My example:
CREATE DATABASE FirstDB
GO
USE FirstDB;
GO
CREATE TABLE Person (
personId INT IDENTITY PRIMARY KEY,
firstName varchar(20) ,
lastName varchar(20) ,
age int
)
INSERT INTO dbo.Person (firstName, lastName, age)
VALUES ('Nick', 'Smith', 30),
('Jack', 'South', 25),
('Garry', 'Perth', 20)
CREATE TABLE PersonAge (
personAgeId INT IDENTITY PRIMARY KEY ,
personId INT ,
newAge varchar(10)
)
INSERT INTO dbo.PersonAge(personId, newAge)
VALUES (1, 60),
(2, 65),
(3, 70)
ALTER TABLE dbo.PersonAge
ADD CONSTRAINT FK_PersonAgePerson FOREIGN KEY (personId)
REFERENCES dbo.Person (personId)
And then example of query:
USE FirstDB;
GO
SET NOCOUNT ON;
DECLARE #row int;
UPDATE Person
SET age = 40
FROM dbo.Person as p join dbo.PersonAge as p1
ON p.personId = p1.personId
WHERE p.age = 60
SET #row = ##ROWCOUNT
SELECT #row
I create an UPDATE query where none of rows will be affected.
At the end #row consist 0 value.
Here is another example, using INSERT and DELETE--
DECLARE #deletedRows INT = 0;
SELECT #deletedRows = ##ROWCOUNT; --no previous DML statement
SELECT #deletedRows; --##ROWCOUNT = 1 for a simple assignment
GO
DROP TABLE IF EXISTS #Test;
GO
CREATE TABLE #Test (ID INT IDENTITY, CurrentDate DATETIME DEFAULT GETDATE());
GO
INSERT #Test DEFAULT VALUES; --INSERT a single row
DECLARE #deletedRows INT = ##ROWCOUNT; --##ROWCOUNT = 1
SELECT #deletedRows;
GO
DELETE FROM #Test WHERE 1=2; --no rows deleted
DECLARE #deletedRows INT = ##ROWCOUNT; --##ROWCOUNT = 0
SELECT #deletedRows;
GO
DELETE TOP (1) t FROM #Test t WHERE 1=1; --1 row deleted
DECLARE #deletedRows INT = ##ROWCOUNT; --##ROWCOUNT = 1
SELECT #deletedRows;
GO
DELETE TOP (1) t FROM #Test t WHERE 1=1; --no rows left to delete
DECLARE #deletedRows INT = ##ROWCOUNT; --##ROWCOUNT = 0
SELECT #deletedRows;
GO
I am writing a store procedure in T-SQL which inserts a row to the table, based on parameters
#UserName ,#CompanyName ,#RestName,#Desc
INSERT INTO Orders(UserId,CompanyId,RestId)
SELECT UserNames.Id,CompanyNames.Id,RestNames.Id FROM UserNames,CompanyNames,RestNames
WHERE
UserNames.Name = #UserName AND
CompanyNames.Name = #CompanyName AND
RestNames.Name = #RestName
Besides the insert to the 3 columns above,I also want to insert the #Desc value.
I tried :
INSERT INTO Orders(UserId,CompanyId,RestId,Desc)
VALUES(
(SELECT UserNames.Id,CompanyNames.Id,RestNames.Id FROM UserNames,CompanyNames,RestNames
WHERE
UserNames.Name = #UserName AND
CompanyNames.Name = #CompanyName AND
RestNames.Name = #RestName),#Desc)
Only one expression can be specified in the select list when the subquery is not introduced with EXISTSt-
It doesn`t work giving the following error:
#UserName ,#CompanyName ,#RestName,#Desc
INSERT INTO Orders(UserId,CompanyId,RestId, Desc_Column)
SELECT UserNames.Id,CompanyNames.Id,RestNames.Id , #Desc --<-- Just SELECT that variable
FROM UserNames,CompanyNames,RestNames -- in your select statement.
WHERE UserNames.Name = #UserName
AND CompanyNames.Name = #CompanyName
AND RestNames.Name = #RestName
Retrieve ID Values Inserted
DECLARE #t TABLE (ID INT); --<-- Declare a table variable
INSERT INTO Orders(UserId,CompanyId,RestId, Desc_Column)
OUTPUT Inserted.ID INTO #t --<-- use OUTPUT, get values from INSERTED Table
SELECT UserNames.Id,CompanyNames.Id,RestNames.Id , #Desc --and insert them into your table variable
FROM UserNames,CompanyNames,RestNames
WHERE UserNames.Name = #UserName
AND CompanyNames.Name = #CompanyName
AND RestNames.Name = #RestName
/*At last just simply select from that table variable to get the inserted IDs*/
SELECT * FROM #t