How to update SQL field using function and stored procedure? - sql

What I want to do is to update column (NewID) in my table (SampleTable) with the following code, but it's not working.. can somebody help me please? Whats wrong with it?
I have the table 'SampleTable' wich has the fields 'NewID' and 'OldID'.
UPDATE SampleTable SET NewID = dbo.fn_DoStuff(OldID) <-- Not working
My function:
ALTER FUNCTION [dbo].[fn_DoStuff]
(
#int oldid
)
RETURNS int
AS
BEGIN
DECLARE #returnValue int
EXEC #returnValue = dbo.spc_DoStuff #oldid
RETURN #returnValue
END
My stored procedure:
SampleTable1 has the columns ID, SomeColName.
ALTER PROCEDURE [dbo].[spc_GeraAtriðisorðalistaÚrAtriðisorði]
(
#oldid int
)
AS
BEGIN
DECLARE #returnValue int
INSERT INTO SampleTable1 (SomeColName) VALUES (null)
SET #returnValue = ##IDENTITY
INSERT INTO SampleTable2 (SomeColName1, SomeColName2) VALUES (#returnValue, #oldid)
SELECT #returnValue AS RetVal
END

You have 2 problems, the first is you cannot call a stored procedure inside a function, nor can you perform your insert within a function.
The second problem is that even if you could call a stored procedure inside a function, you are not returning the value from the procedure correctly. You would need something like:
CREATE TABLE dbo.T (ID INT IDENTITY, Filler CHAR(10));
GO
CREATE PROCEDURE dbo.Test
AS
DECLARE #i INT;
INSERT dbo.T (Filler) VALUES (NULL);
RETURN SCOPE_IDENTITY();
GO
Note the use of the RETURN statement, if you don't use this the default return value is 0
Then you can use:
DECLARE #i INT;
EXECUTE #i = dbo.Test;
SELECT ReturnVal = #i;
*Note, I have replaced ##IDENTITY with SCOPE_IDENTITY(), ##IDENTITY is rarely the correct function to use*
Example on SQL Fiddle

With your solution GarethD I could still not call the function like I wanted to
UPDATE SampleTable SET NewID = dbo.fn_DoStuff(OldID).
Your code helped me though to start thinking another way. Now I'm using a cursor and a while loop and it works perfectly. See my solution below:
DECLARE #OldID AS INT
DECLARE Upd_CURSOR FOR
SELECT OldID
FROM dbo.SampleTable
WHERE OldID is not null
FOR UPDATE OF NewID
OPEN Upd_CURSOR;
FETCH NEXT FROM Upd_CURSOR INTO #OldID
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #returnValue int;
INSERT INTO SampleTable1 (SomeColName) VALUES (null);
SET #returnValue = SCOPE_IDENTITY();
INSERT INTO SampleTable2 (SomeColName1, SomeColName2) VALUES (#returnValue, #OldID)
UPDATE dbo.SampleTable SET NewID = #returnValue WHERE CURRENT OF Upd_CURSOR;
FETCH NEXT FROM Upd_CURSOR INTO #OldID;
END;
CLOSE Upd_CURSOR;
DEALLOCATE Upd_CURSOR;
GO

Related

sp_executesql sets a column to null

I'm attempting to execute some SQL inside of sp_executesql.
Here is the generated SQL:
exec sp_executesql
N'declare #RC int
EXECUTE #RC = [dbo].[sp_StoredProcedureName]
#parameterName
select #RC',
N'#parameterName nvarchar(4000)',
#parameterName=N'TEST'
Here is the stored procedure that is called from the generated SQL:
ALTER PROCEDURE [dbo].[sp_StoredProcedureName] (
#parameterName varchar(4000)
)
with execute as owner
as
DECLARE #returnValue int
BEGIN TRANSACTION
INSERT INTO [dbo].[TableName]
(parameterName)
VALUES
(#parameterName)
set #returnValue = IDENT_CURRENT('TableName')
COMMIT
SELECT #returnValue
GO
For some reason, parameterName is never set.
When attempting to select from TableName after the SP has been executed, ParameterName is NULL.
I am using MS SQL. The SQL was generated by ADO.NET.
Your stored procedure is not returning anything, hence the return value is NULL.
In general, you should only be using the return value from a stored procedure as a status, not to return actual data.
Real return values should be returned via output parameters.
Further, I strongly recommend an OUTPUT clause for this purpose:
ALTER PROCEDURE [dbo].[sp_StoredProcedureName] (
#parameterName varchar(4000),
#returnValue int OUTPUT
)
with execute as owner
as
BEGIN
DECLARE #ids TABLE (id int);
INSERT INTO [dbo].[TableName] (parameterName)
OUTPUT id INTO #ids
VALUES (#parameterName);
SELECT TOP (1) #returnValue = id -- only 1 is expected anyway
FROM #ids;
END;
You would then call this as:
declare #RC int;
declare #parameterName nvarchar(4000);
set #parameterName = N'TEST';
exec [dbo].[sp_StoredProcedureName] #parameterName, #rc int OUTPUT;
Dynamic SQL is not necessary.

SQL Server trigger with loop for multiple row insertion

I've created trigger for my database which handles some insertion but when I add multiple values in 1 SQL query it doesn't work:
ALTER TRIGGER [dbo].[ConferenceDayTrigger]
ON [dbo].[Conferences]
AFTER INSERT
AS
BEGIN
DECLARE #ID INT
DECLARE #dayC INT
DECLARE #counter INT
SET #counter = 1
SET #ID = (SELECT IDConference FROM Inserted)
SET #dayC = (SELECT DATEDIFF(DAY, start,finish) FROM Inserted)
WHILE #counter <= #dayC + 1
BEGIN
EXEC AddConferenceDay #Id, #counter
SET #counter = #counter +1
END
END
For single insertion it works ok. But what should I change/add to make it execute for each row of inserted values?
If you cannot change the stored procedure, then this might be one of the (very few) cases when a cursor comes to the rescue. Double loops, in fact:
ALTER TRIGGER [dbo].[ConferenceDayTrigger]
ON [dbo].[Conferences]
AFTER INSERT
AS
BEGIN
DECLARE #ID INT;
DECLARE #dayC INT;
DECLARE #counter INT
SET #counter = 1;
DECLARE yucky_Cursor CURSOR FOR
SELECT IDConference, DATEDIFF(DAY, start,finish) FROM Inserted;
OPEN yucky_Cursor; /*Open cursor for reading*/
FETCH NEXT FROM yucky_Cursor INTO #ID, #dayC;
WHILE ##FETCH_STATUS = 0
BEGIN
WHILE #counter <= #dayC + 1
BEGIN
EXEC AddConferenceDay #Id, #counter;
SET #counter = #counter + 1;
END;
FETCH NEXT FROM yucky_Cursor INTO #ID, #dayC;
END;
CLOSE yucky_Cursor;
DEALLOCATE yucky_Cursor;
END;
I suspect there is a way to refactor and get rid of the cursor and use set-based operations.
When you insert more than one record, you need to cursor/while to call the AddConferenceDay procedure for each record.
But I will suggest you to alter your procedure to accept table type as input parameter. So that more than one ID and dayC as input to AddConferenceDay procedure. It is more efficient than your current approach.
something like this
create type udt_Conferences as table (ID int,dayC int)
Alter the procedure to use udt_Conferences as input parameter
Alter procedure AddConferenceDay (#input udt_Conferences readonly)
as
begin
/* use #input table type instead of #Id and #counter variables */
end
To call the procedure update the trigger with created udt
ALTER TRIGGER [dbo].[ConferenceDayTrigger]
ON [dbo].[Conferences]
AFTER INSERT
AS
BEGIN
Declare #input udt_Conferences
insert into #input (ID,dayC)
select IDConference,DATEDIFF(DAY, start,finish) from Inserted
END
add these lines to your trigger
AFTER INSERT
AS
BEGIN
AFTER INSERT
AS
BEGIN
Declare #Count int;
Set #Count=##ROWCOUNT;
IF #Count=0
Return;
SET NOCOUNT ON;
-- Insert statements for trigger here

getting the last inserted id from previous insert statement

I am having a stored procedure with two insert statement, where I want to insert the ID of the first insert statement into the second one.
CREATE PROC [dbo].[Log_Action]
#action_description VARCHAR(MAX),
#creator_id INT,
#entity VARCHAR(50),
#entity_identifier UNIQUEIDENTIFIER
AS
BEGIN
DECLARE #return_value BIT;
BEGIN TRY
INSERT INTO Action_Lookup (action_description)
VALUES (#action_description);
INSERT INTO Audit ([user_id], action_id, CREATED, [guid], entity, entity_identifier)
VALUES (#creator_id, SCOPE_IDENTITY(), GETDATE(), NEWID(), #entity, #entity_identifier);
SET #return_value = 1;
RETURN #return_value;
END TRY
BEGIN CATCH
SET #return_value = 0;
RETURN #return_value;
END CATCH
END
the problem that SCOPE_IDENTITY() returns null, I also tried ##IDENTITY and IDENT_CURRENT but non works.
Try output clause:
CREATE PROC [dbo].[Log_Action]
#action_description VARCHAR(MAX),
#creator_id INT,
#entity varchar(50),
#entity_identifier uniqueidentifier
AS
BEGIN
DECLARE #return_value bit;
BEGIN TRY
INSERT INTO Action_Lookup (action_description)
OUTPUT
#creator_id,
inserted.[id], -- in [] there should be actual name of identity column
GETDATE(),
NEWID(),
#entity,
#entity_identifier
INTO Audit ([user_id], action_id, created, [guid], entity, entity_identifier)
VALUES (#action_description);
set #return_value = 1;
return #return_value;
END TRY
BEGIN CATCH
set #return_value = 0;
return #return_value;
END CATCH
END

How to return the output of stored procedure into a variable in sql server

I want to execute a stored procedure in SQL Server and assign the output to a variable (it returns a single value) ?
That depends on the nature of the information you want to return.
If it is a single integer value, you can use the return statement
create proc myproc
as
begin
return 1
end
go
declare #i int
exec #i = myproc
If you have a non integer value, or a number of scalar values, you can use output parameters
create proc myproc
#a int output,
#b varchar(50) output
as
begin
select #a = 1, #b='hello'
end
go
declare #i int, #j varchar(50)
exec myproc #i output, #j output
If you want to return a dataset, you can use insert exec
create proc myproc
as
begin
select name from sysobjects
end
go
declare #t table (name varchar(100))
insert #t (name)
exec myproc
You can even return a cursor but that's just horrid so I shan't give an example :)
You can use the return statement inside a stored procedure to return an integer status code (and only of integer type). By convention a return value of zero is used for success.
If no return is explicitly set, then the stored procedure returns zero.
CREATE PROCEDURE GetImmediateManager
#employeeID INT,
#managerID INT OUTPUT
AS
BEGIN
SELECT #managerID = ManagerID
FROM HumanResources.Employee
WHERE EmployeeID = #employeeID
if ##rowcount = 0 -- manager not found?
return 1;
END
And you call it this way:
DECLARE #return_status int;
DECLARE #managerID int;
EXEC #return_status = GetImmediateManager 2, #managerID output;
if #return_status = 1
print N'Immediate manager not found!';
else
print N'ManagerID is ' + #managerID;
go
You should use the return value for status codes only. To return data, you should use output parameters.
If you want to return a dataset, then use an output parameter of type cursor.
more on RETURN statement
Use this code, Working properly
CREATE PROCEDURE [dbo].[sp_delete_item]
#ItemId int = 0
#status bit OUT
AS
Begin
DECLARE #cnt int;
DECLARE #status int =0;
SET NOCOUNT OFF
SELECT #cnt =COUNT(Id) from ItemTransaction where ItemId = #ItemId
if(#cnt = 1)
Begin
return #status;
End
else
Begin
SET #status =1;
return #status;
End
END
Execute SP
DECLARE #statuss bit;
EXECUTE [dbo].[sp_delete_item] 6, #statuss output;
PRINT #statuss;
With the Return statement from the proc, I needed to assign the temp variable and pass it to another stored procedure. The value was getting assigned fine but when passing it as a parameter, it lost the value. I had to create a temp table and set the variable from the table (SQL 2008)
From this:
declare #anID int
exec #anID = dbo.StoredProc_Fetch #ID, #anotherID, #finalID
exec dbo.ADifferentStoredProc #anID (no value here)
To this:
declare #t table(id int)
declare #anID int
insert into #t exec dbo.StoredProc_Fetch #ID, #anotherID, #finalID
set #anID= (select Top 1 * from #t)

Stored Procedure OutPut Parameters

i need to write a stored procedure which will return a string.logic is
when user try to insert a new record i need to check whether that record already exist.if exist need to return msg "Record exist" else return "Inserted"
following is what i have done for the moment and i'm stuck here.can some one help me to complete the procedure
CREATE PROCEDURE [dbo].[spInsetPurpose]
#Purpose VARCHAR(500),
#Type VARCHAR(6),
#Result VARCHAR(10)= NULL OUTPUT
AS
BEGIN
Declare #Position VARCHAR(20)
DECLARE #TempTable TABLE (Purpose VARCHAR(500))
INSERT INTO #TempTable
SELECT Purpose FROM tblPurpose WHERE Purpose=#Purpose
INSERT INTO tblPurpose(Purpose,[Type]) VALUES(#Purpose,#Type)
END
To check if the row already exists you can do
If Exists (Select Top 1 1 from tblPurpose where Purpose = #Purpose and [Type] = #Type)
Begin
Insert Into tblPurpose
(Purpose, [Type])
Select
#Purpose, #Type
SET #Result = 'Inserted'
End
Else
Begin
SET #Result = 'Record exists'
End