Insert trigger: set value from procedure ends with 3609 error - sql-server

I have to check if a specific value of an insert is null. If it is null, I want to give it a value from a stored procedure. This procedure returns a number like a sequence, but because I have SQL Server 2008 I had to create it myself:
CREATE PROCEDURE dbo.Get_BAV_PERSONALARCHIV_SEQUENCE ( #value BIGINT OUTPUT)
AS
BEGIN TRANSACTION;
INSERT dbo.BAV_Personalarchiv_Sequence WITH (TABLOCKX) DEFAULT VALUES;
ROLLBACK TRANSACTION;
SELECT #value = SCOPE_IDENTITY();
GO
I want to use the created value in my Insert Trigger if 'SYSROWID' is null (if it is null, it should be the only record with it) :
ALTER TRIGGER [dbo].[NT_BAV_PERSONALARCHIV_MITARBEITER_INSERT]
ON [dbo].[NT_BAV_PERSONALARCHIV_MITARBEITER]
AFTER INSERT
AS
BEGIN
SET NOCOUNT ON;
If (SELECT SYSROWID FROM INSERTED) IS NULL
Begin
DECLARE #value BIGINT;
EXECUTE dbo.Get_BAV_PERSONALARCHIV_SEQUENCE #value OUTPUT;
Update dbo.NT_BAV_PERSONALARCHIV_MITARBEITER
SET SYSROWID = #value
where SYSROWID IS NULL
End
END
But sadly it returns with an 3609 error and rolls back the transaction.
Testing only the Execute and Update works without a problem:
DECLARE #value BIGINT;
EXECUTE dbo.Get_BAV_PERSONALARCHIV_SEQUENCE #value OUTPUT;
Update dbo.NT_BAV_PERSONALARCHIV_MITARBEITER
SET SYSROWID = #value
where SYSROWID IS NULL
What am I missing? Thank you for your help!

Ok, I found out that the rollback in the procedure causes the trigger to end with the error. My workaround is to delete the values of the "sequence"-table before I create a new value:
ALTER PROCEDURE dbo.Get_BAV_PERSONALARCHIV_SEQUENCE ( #value BIGINT OUTPUT)
AS
delete from dbo.BAV_Personalarchiv_Sequence
INSERT dbo.BAV_Personalarchiv_Sequence WITH (TABLOCKX) DEFAULT VALUES;
SELECT #value = SCOPE_IDENTITY();
GO

Related

SQL server execute SP from sql table and update

The table below stores sql insert statements and I run those from a sp. I need to also add an insert to the last_run_dt column. I put the code together via existing stackoverflow questions. I need help implementing this in my code, any feedback will be helpful.
How can I update my code to update the last_run_dt column?
Table:
audit_sql_id audit_sql last_run_dt
1 select * from <<need to add last run_dt value>>
2 select * from <<need to add last run_dt value>>
Code:
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
alter proc [dbo].[sp_sqlAudit]
#packagename as varchar(255)
as
begin
set nocount on;
if #packagename='SQL_DM_AUDIT'
begin
declare #queries table (audit_sql_id int identity(1,1),sqlscript varchar(max))
declare #str_query varchar(max);
declare #startloop int
declare #endloop int
insert into #queries
select audit_sql
from dw.dbo.audit_sql with(nolock)
select #endloop = max(audit_sql_id), #startloop = min(audit_sql_id)
from #queries
while #startloop < = #endloop
begin
select #str_query = sqlscript
from #queries
where audit_sql_id = #startloop
exec (#str_query)
set #startloop = #startloop + 1
end
end
end
I would suggest a slight refactor something like the below. There's no need to bring the entire list of sql statements into TemDB, just iterate over it and get each statement in turn. I would also always add a #debug parameter to print the sql instead if executing.
create or alter procedure dbo.sqlAudit
#packagename as varchar(255)
as
set nocount on;
declare #str_query varchar(max), #Id int
declare #AuditID table (Id int)
if #packagename='SQL_DM_AUDIT'
begin
insert into #AuditID (Id) /* Get list of IDs */
select audit_sql_id
from dw.dbo.audit_sql
while exists(select * from #AuditID) /* Continue while there are IDs in the list */
begin
select top (1) #Id=Id from #AuditID /* Get an ID */
select #str_query=audit_sql /* Get the sql for the ID */
from dw.dbo.audit_sql
where audit_sql_id=#Id
delete from #AuditID where Id=#Id /* Remove this ID from the list */
begin try
exec (#str_query)
if ##Error=0
begin
update dw.dbo.audit_sql set last_run_dt=GetDate() /* Update date for ID if run successful */
where audit_sql_id=#Id
end
end try
begin catch
/*handle error*/
end catch
end
end
go

Delete statement not working in Stored Procedure

I am trying delete multiple records from store procedure SQL-Server 2012. I have select and then delete statement, apparently my delete statement is not been called
ALTER PROCEDURE [dbo].[DeleteFunctionsNavigation]
#FunctionName nvarchar(250),
#Function_identity INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SELECT Navigation_Functions.Function_ID
FROM Navigation_Functions
WHERE Navigation_Functions.FunctionName = #FunctionName
SET #Function_identity=SCOPE_IDENTITY()
DELETE FROM Navigation_FunctionHierarchy
WHERE Navigation_FunctionHierarchy.Function_IDs = #Function_identity
RETURN
END
The usage of SCOPE_IDENTITY() is incorrect in this context. Try this
ALTER PROCEDURE [dbo].[DeleteFunctionsNavigation]
#FunctionName nvarchar(250),
#Function_identity INT OUTPUT
AS
BEGIN
SET NOCOUNT ON;
SELECT #Function_identity = Navigation_Functions.Function_ID
FROM Navigation_Functions
WHERE Navigation_Functions.FunctionName = #FunctionName
DELETE FROM Navigation_FunctionHierarchy
WHERE Navigation_FunctionHierarchy.Function_IDs = #Function_identity
RETURN
END

SQL Server 2012 scope_identity advise

I created a stored procedure in SQL Server 2012 and I have used scope_identity to get identity column's value but in my case I do not know is this correct or not please help
CREATE PROCEDURE Add_Translation
#english nvarchar(70),
#kurdish nvarchar(70),
#english_category int,
#kurdish_category int,
#result int out
AS
BEGIN
SET NOCOUNT ON;
if #english is not null and #kurdish is not null and #english_category is not null and #kurdish_category is not null
begin
insert into english_table values(#english, #english_category);
declare #identityEnglish int;
set #identityEnglish = SCOPE_IDENTITY();
insert into kurdish_table values(#kurdish, #kurdish_category);
declare #identityKurdish int;
set #identityKurdish = SCOPE_IDENTITY();
insert into transactions values(#identityEnglish, #identityKurdish);
set #result = 1;
end
else
begin
set #result = 0;
end
END
My question is that will the variable #identityEnglish get last identity value of english_table and variable #identityKurdish get last identity value of kurdish_table
thank you..
If the inserts succeed then scope_identity() works correctly.
You can use "Output" clause on DML operations.
Click here

Calling a stored proc that returns a recordset from within a stored proc

Working in SQL Server 2005, I have a stored procedure that inserts a record and returns the new ID via SELECT ##IDENTITY; as the last command.
I then want to call this from another stored proc, and get the value of the new ID.
But I can't work out how to get the value returned from the first procedure.
Example:
CREATE PROCEDURE spMyInsert(#Field1 VARCHAR(10)) AS
BEGIN
INSERT INTO tMyTable (Column1) VALUES (#Field1); // ID column implicitly set
SELECT ##IDENTITY ID;
END
CREATE PROCEDURE spMyMain AS
BEGIN
DECLARE #NewID INT;
EXEC spMyInsert 'TEST';
// How do I set #NewID to the value returned from spMyInsert?
END
There is another question that nearly answers my question, but not quite. This explains how to insert the results into another table, but all I want to do is store it in a local variable.
Looking at other similar questions, the general answer is to change to either set an OUTPUT variable or create a function to do it, but I can't do this in my case as other .NET data access stuff uses the same stored proc, and I don't want to have to duplicate all the work of the stored procs as functions as well.
I couple of things that I've tried but all fail are:
SET #NewID = (EXEC spMyInsert 'TEST');
SET #NewID = (SELECT ID FROM (EXEC spMyInsert 'TEST'));
Anybody know how to do this?
Thanks,
Ben
By the way you should probably check that ##identity is what you need as opposed to scope_identity.
If it is what you need then it will still be accessible in the calling stored procedure too.
CREATE PROCEDURE spMyMain
AS
BEGIN
DECLARE #NewID INT;
EXEC spMyInsert 'TEST';
SET #NewID = ##IDENTITY
SELECT #NewID AS '#NewID'
END
The more general solution that would need to be applied if you use scope_identity and don't want to use either output parameters or the procedure return code is
CREATE PROCEDURE spMyMain AS
BEGIN
DECLARE #NewID INT;
DECLARE #IdHolder TABLE
(
id INT
)
INSERT INTO #IdHolder
EXEC spMyInsert 'TEST';
IF ##ROWCOUNT<>1
RAISERROR('Blah',16,1)
SELECT #NewID = id FROM #IdHolder
END
First, don't use ##IDENTITY, use SCOPE_IDENTITY() instead (search this site or Google for the reason why). Then just return the value in an output parameter:
CREATE PROCEDURE spMyInsert(#Field1 VARCHAR(10), #NewID int output) AS
BEGIN
INSERT INTO tMyTable (Column1) VALUES (#Field1);
SET #NewID = scope_identity();
END
go
CREATE PROCEDURE spMyMain AS
BEGIN
DECLARE #NewID INT;
EXEC spMyInsert #Field1 = 'TEST', #NewID = #NewID OUTPUT;
END
go
The issue here is that the spMyInsert returns a Select. When you execute spMyMain it will return the Select from spMyInsert and then the select from spMyMain
I would suggest that you amend spMyInsert to utilise OUTPUT parameters
CREATE PROCEDURE spMyInsert(#Field1 VARCHAR(10), #NewId int output) AS
BEGIN
INSERT INTO tMyTable (Column1) VALUES (#Field1); // ID column implicitly set
SELECT #NewId = ##SCOPE_IDENTITY;
END
and then
CREATE PROCEDURE spMyMain AS
BEGIN
DECLARE #NewID INT;
Set #NewId = 0
EXEC spMyInsert 'TEST', #NewId output;
select #NewId
// How do I set #NewID to the value returned from spMyInsert?
END
Note that I have also changed ##Identity to ##scope_identity It is better to use ##Scope_Identity as that will return the new ID that applies to the current connection.
Try this:
Execute #NewID = spMyInsert 'TEST'
Edit: After reading his question more thoroughly and realizing he was dealing with a select rather than a return: Could you wrap that procedure in a function call and then call the function?
select #NewId = from fnMyInsert('TEST')
An output parameter is the way to go, but if you really can't change the inner SP then, as you say, you can have the inner SP return its results to a table and then get the value out of there.
eg.
declare #NewID int,
#Customer table(CustomerId int);
insert into #Customer
exec spMyInsert 'TEST';
select #NewID = CustomerId from #Customer;

can we return a null from stored procedure

Can we return null value from stored procedure. i dont want to use collase or isnull. I want to capture NULL at the frontend. Is it possible ?
Edit:
I am using Sql Server 2005
eg. where i want to use
CREATE PROCEDURE [Authentication].[spOnlineTest_CheckLogin]
#UserName NVARCHAR(50)
AS
BEGIN TRY
BEGIN TRAN
COMMIT TRAN
RETURN NULL
END TRY
Error
The 'spOnlineTest_CheckLogin' procedure attempted to return a status of NULL, which is not allowed. A status of 0 will be returned instead.
Msg 0, Level 11, State 0, Line 0
A severe error occurred on the current command. The results, if any, should be discarded.
No, the return type of a stored procedure is INT and it cannot be null.
use an output parameter, example
CREATE PROCEDURE Test
#UserName NVARCHAR(50), #Status int output
AS
BEGIN TRY
BEGIN TRAN
COMMIT TRAN
set #Status = null
END TRY
begin catch
end catch
go
then call it like this
declare #s int
set #s =5
exec Test'bla',#s output
select #s --will be null
You can think of a proc like follows. Let me first set the context. We might have a table Table1(id int, name varchar(2), Address varchar(2)) and want to get the id and if not found, it will be null. So we might write a proc like the following:
CREATE PROCEDURE GetId
#Name VARCHAR(50), #Status int output
AS
BEGIN TRY
set #Status = null
select #Status = id from Table1 where name=#name
This will work for you.

Resources