Can't alter sql object because it's being referenced by default constraint - sql

I have this script to store a function:
CREATE OR ALTER FUNCTION order.GetSupplierIdBySystemName(#SysName AS VARCHAR(100))
RETURNS INT
AS
BEGIN
DECLARE #SId AS INT
SELECT #SId = Id FROM [hyper].[Supplier] WHERE SystemName = #SysName
RETURN #SId
END
I then use it in a default constraint:
ALTER TABLE [h360_order].[ExternalStore]
ADD SupplierId INT NOT NULL CONSTRAINT DF_ExternalStore_SupplierId
DEFAULT(order.GetSupplierIdBySystemName('Name'));
Now the CREATE OR ALTER is for idempotence, but when running the scripts a second time I get the error:
SQL Error [3729][S0003]: Cannot ALTER 'order.GetSupplierIdBySystemName' because it is being referenced by object 'DF_ExternalStore_SupplierId'.
IF EXISTS... seems to be a possibility, but I'm not being able to use it inside the script that creates the function.
How can I fix it cleanly while still keeping idempotence?

Drop the constraint, alter the function, recreate the constraint.
ALTER TABLE [h360_order].[ExternalStore] DROP CONSTRAINT DF_ExternalStore_SupplierId ;
ALTER FUNCTION order.GetSupplierIdBySystemName(#SysName AS VARCHAR(100))
RETURNS INT
AS
BEGIN
DECLARE #SId AS INT
SELECT #SId = Id FROM [hyper].[Supplier] WHERE SystemName = #SysName
RETURN #SId
END;
ALTER TABLE [h360_order].[ExternalStore]
ADD CONSTRAINT DF_ExternalStore_SupplierId
DEFAULT(order.GetSupplierIdBySystemName('Name')) FOR [SupplierId];

I ended up using sp_executesql stored procedure within an IF statement:
IF OBJECT_ID('order.GetSupplierIdBySystemName') IS NULL
BEGIN
DECLARE #sqlstatement NVARCHAR(1000);
SET #sqlstatement =
'CREATE FUNCTION h360_order.GetSupplierIdBySystemName(#SysName AS VARCHAR(100))
RETURNS INT
AS
BEGIN
DECLARE #SId AS INT
SELECT #SId = Id FROM [hyper].[Supplier] WHERE SystemName = #SysName
RETURN #SId
END'
EXEC sp_executesql #sqlstatement;
END
So basically what this does is check if the stored function exists, and if not it creates one using the sql query stored in #sqlstatement through the stored procedure sp_executesql, very simple stuff, actually. Finding the right tool for the job is the complicated part.
Hope this helps someone with the same problem.

Related

I have a trigger to execute a SP when one column is updated. If I add an insert operation it runs the SP for any changes to the table

I am looking to get the SP run only when the Instructions column has something added or altered.
This code works for updating of the Instructions column
AFTER Update
AS BEGIN
SET NOCOUNT on
if update ([Instructions])
BEGIN
DECLARE #ID INT
SELECT #ID = (SELECT [ID] FROM inserted)
EXEC [dbo].[Gem_AddNoteToDelivery] #ID
END
END
GO
ALTER TABLE [dbo].[DeliveryNote] ENABLE TRIGGER [Gemini_DeliveryNote_AddNote]
GO
My Stored Procedure is this
ALTER PROCEDURE [dbo].[Gem_AddNoteToDelivery]
#ID INT output
AS
BEGIN
SET NOCOUNT ON;
DECLARE #DInst VARCHAR (4000)
DECLARE #DELNN VARCHAR (32)
Select #Dinst = [Instructions],
#DELNN = [DelNoteNumber]
FROM [dbo].[DeliveryNote] WHERE [id]=#ID
INSERT INTO [dbo].[Notes]
--([UserID],[OperatorID],[UserName],[DateDB],[ModuleType],[RecordID],[RecordNo],[Flags],[Details],[Priority],[NotesType])
VALUES
(115,0,'Automation',getdate(),4,#ID,#DELNN,
0x0000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000,#DInst,1,Null)
END
GO
If I alter the Trigger to read
AFTER Update,Insert
then the SP runs for updates to all columns not just the Instructions one.
The database is not mine so I am unable to alter tables.
Any help most appreciated.

Set variable with Scope Identity after executing different stored proc

I am trying to set a declared variable in a stored procedure after making use of another stored procedure to INSERT a 'Case' first.
The pseudo code looks like this:
DECLARE #CaseId variable
INSERT Case into Case table
SET #CaseId using SCOPE_IDENTITY
IF Case.CaseID = #CaseId
--rest of script
The below script works as expected for me:
INSERT INTO Case (CaseRef, [Source], DateCreated, CaseType)
VALUES (#caseRef, #source, #dateCreated, #caseType)
SET #caseID = SCOPE_IDENTITY();
I've tried with the below script but it doesn't seem to set the variable. Is this possible? Or must I set it the way I'm doing in the above script?
EXEC sp_InsertCase #caseRef, #source, #dateCreated, #caseType
SET #caseID = SCOPE_IDENTITY();
scope_identity() does what it says on the tin - it gives you the last identity value generated in the current scope. A stored procedure defines a scope. So when the stored procedure that causes the identity value to be generated exits, you're no longer in the scope where the value was generated, so scope_identity() can't tell you anything.
What you can do is capture the scope_identity() value into a variable inside the stored procedure, and return it as an output parameter:
create table t(i int identity(1,1), j int);
go
create proc insert_and_get_scope #scopeid int = null output as
begin
insert t(j) values (1);
set #scopeid = scope_identity();
end
go
declare #scopeid int;
exec insert_and_get_scope #scopeid output;
select #scopeid;
You can see from the example below:
DROP TABLE IF EXISTS [dbo].[StackOverflow];
CREATE TABLE [dbo].[StackOverflow]
(
[RowID] INT IDENTITY(1,1) NOT NULL
);
GO
CREATE OR ALTER PROCEDURE [dbo].[sp_StackOverflow]
AS
BEGIN;
INSERT INTO [dbo].[StackOverflow]
DEFAULT VALUES;
END;
GO
EXEC [dbo].[sp_StackOverflow];
SELECT SCOPE_IDENTITY();
it is not working, because it is not in the current scope:
Returns the last identity value inserted into an identity column in
the same scope. A scope is a module: a stored procedure, trigger,
function, or batch. Therefore, if two statements are in the same
stored procedure, function, or batch, they are in the same scope.
but you can try this:
DROP TABLE IF EXISTS [dbo].[StackOverflow];
CREATE TABLE [dbo].[StackOverflow]
(
[RowID] INT IDENTITY(1,1) NOT NULL
);
GO
CREATE OR ALTER PROCEDURE [dbo].[sp_StackOverflow]
(
#RowID BIGINT OUTPUT
)
AS
BEGIN;
INSERT INTO [dbo].[StackOverflow]
DEFAULT VALUES;
SET #RowID = SCOPE_IDENTITY()
END;
GO
DECLARE #RowID BIGINT;
EXEC [dbo].[sp_StackOverflow] #RowID = #RowID OUTPUT;
SELECT #RowID;

I want to write the code I created with the 'Stored procedure' as a function

CREATE PROC add_person
(
#id tinyint,
#name nvarchar(max),
#surname nvarchar(max),
#salary int,
#job nvarchar(max)
)
AS
BEGIN
INSERT INTO information
VALUES(#id,#name,#surname,#salary,#job)
END
I want to write this code as a function. But the concept of "return" confuses me. That's why I couldn't.
I tried to write the code above as a function. This code came out.
CREATE FUNCTION add_person
(
#id tinyint,
#name nvarchar(max),
#surname nvarchar(max),
#salary int,
#job nvarchar(max)
)
RETURNS TABLE
AS
BEGIN
RETURN INSERT INTO information -- not work
VALUES(#id,#name,#surname,#salary,#job)
END
If you want to return the newly created table, you can use the stored procedure to do that. If you're using SQL Server, the code would be:
BEGIN
INSERT INTO information -- not work
VALUES(#id,#name,#surname,#salary,#job);
SELECT * FROM information WHERE id = ##identity; -- this is the primary key just created.
END
Functions are much more limited in their functionality than are stored procedures.
Although insert is allowed, it is only allowed in local variables. As the documentation says:
INSERT, UPDATE, and DELETE statements modifying local table variables.
On the other hand, a stored procedure can return a value. Normally, this is a status code, where 0 means everything succeeded, and any other value means that the process failed.

Execute a stored procedure in another stored procedure in SQL server

How can i execute a stored procedure in another stored procedure in SQL server?
How will I pass the parameters of the second procedure.?
If you only want to perform some specific operations by your second SP and do not require values back from the SP then simply do:
Exec secondSPName #anyparams
Else, if you need values returned by your second SP inside your first one, then create a temporary table variable with equal numbers of columns and with same definition of column return by second SP. Then you can get these values in first SP as:
Insert into #tep_table
Exec secondSPName #anyparams
Update:
To pass parameter to second sp, do this:
Declare #id ID_Column_datatype
Set #id=(Select id from table_1 Where yourconditions)
Exec secondSPName #id
Update 2:
Suppose your second sp returns Id and Name where type of id is int and name is of varchar(64) type.
now, if you want to select these values in first sp then create a temporary table variable and insert values into it:
Declare #tep_table table
(
Id int,
Name varchar(64)
)
Insert into #tep_table
Exec secondSP
Select * From #tep_table
This will return you the values returned by second SP.
Hope, this clear all your doubts.
Suppose you have one stored procedure like this
First stored procedure:
Create PROCEDURE LoginId
#UserName nvarchar(200),
#Password nvarchar(200)
AS
BEGIN
DECLARE #loginID int
SELECT #loginID = LoginId
FROM UserLogin
WHERE UserName = #UserName AND Password = #Password
return #loginID
END
Now you want to call this procedure from another stored procedure like as below
Second stored procedure
Create PROCEDURE Emprecord
#UserName nvarchar(200),
#Password nvarchar(200),
#Email nvarchar(200),
#IsAdmin bit,
#EmpName nvarchar(200),
#EmpLastName nvarchar(200),
#EmpAddress nvarchar(200),
#EmpContactNo nvarchar(150),
#EmpCompanyName nvarchar(200)
AS
BEGIN
INSERT INTO UserLogin VALUES(#UserName,#Password,#Email,#IsAdmin)
DECLARE #EmpLoginid int
**exec #EmpLoginid= LoginId #UserName,#Password**
INSERT INTO tblEmployee VALUES(#EmpName,#EmpLastName,#EmpAddress,#EmpContactNo,#EmpCompanyName,#EmpLoginid)
END
As you seen above, we can call one stored procedure from another
Yes, you can do that like this:
BEGIN
DECLARE #Results TABLE (Tid INT PRIMARY KEY);
INSERT #Results
EXEC Procedure2 [parameters];
SET #total 1;
END
SELECT #total
Your sp_test: Return fullname
USE [MY_DB]
GO
IF (OBJECT_ID('[dbo].[sp_test]', 'P') IS NOT NULL)
DROP PROCEDURE [dbo].sp_test;
GO
CREATE PROCEDURE [dbo].sp_test
#name VARCHAR(20),
#last_name VARCHAR(30),
#full_name VARCHAR(50) OUTPUT
AS
SET #full_name = #name + #last_name;
GO
In your sp_main
...
DECLARE #my_name VARCHAR(20);
DECLARE #my_last_name VARCHAR(30);
DECLARE #my_full_name VARCHAR(50);
...
EXEC sp_test #my_name, #my_last_name, #my_full_name OUTPUT;
...
You can call User-defined Functions in a stored procedure alternately
this may solve your problem to call stored procedure
Yes ,
Its easy to way we call the function inside the store procedure.
for e.g. create user define Age function and use in select query.
select dbo.GetRegAge(R.DateOfBirth, r.RegistrationDate) as Age,R.DateOfBirth,r.RegistrationDate from T_Registration R
Procedure example:
Create PROCEDURE SP_Name
#UserName nvarchar(200),
#Password nvarchar(200)
AS
BEGIN
DECLARE #loginID int
--Statements for this Store Proc
--
--
--
--execute second store procedure
--below line calling sencond Store Procedure Exec is used for execute Store Procedure.
**Exec SP_Name_2 #params** (if any)
END

Store the result of a stored procedure without using an output parameter

I have 2 stored procedures: up_proc1 and up_proc2.
This is (a simplified version of) up_proc2:
CREATE PROCEDURE dbo.up_proc2
#id_campaign uniqueidentifier, #id_subcampaign uniqueidentifier,
#id_lead uniqueidentifier, #offer NVARCHAR(1000) = NULL
AS
SET NOCOUNT ON
DECLARE #id UNIQUEIDENTIFIER
SELECT #id = id FROM prospects WHERE id_lead = #id_lead
AND id_campaign = #id_campaign AND id_subcampaign = #id_subcampaign
IF #id IS NULL
BEGIN
SET #id = newid ()
INSERT INTO prospects (id, id_campaign, id_subcampaign, id_lead, offer)
values (#id, #id_campaign, #id_subcampaign, #id_lead, #offer)
END
ELSE
BEGIN
UPDATE prospects set offer = #offer WHERE id=#id
END
SELECT #id AS ID
GO
From up_proc1 I call up_proc2. What I would like to achieve is to store the #id of up_proc2 in a variable declared in up_proc1. Is this possible without using an output parameter?
This is how up_proc1 looks like:
CREATE PROCEDURE dbo.up_proc1
AS
SET NOCOUNT ON
DECLARE #fromProc2 UNIQUEIDENTIFIER
-- NOT WORKING
-- select #fromProc2 = exec up_insertProspects [snip]
-- ALSO NOT WORKING
-- exec #fromProc2 = up_insertProspects [snip]
What you could do is store the output into a table variable:
DECLARE #tmpTable TABLE (ID UNIQUEIDENTIFIER)
INSERT INTO #tmpTable
EXEC dbo.up_proc2 ..........
and then go from there and use that table variable later on.
You can certainly consume this as an output parameter in proc2 without affecting how your C# code retrieves the eventual resultset.
ALTER PROCEDURE dbo.up_proc2
#id_campaign uniqueidentifier,
#id_subcampaign uniqueidentifier,
#id_lead uniqueidentifier,
#offer NVARCHAR(1000) = NULL,
#fromProc2 UNIQUEIDENTIFER = NULL OUTPUT
AS
BEGIN
SET NOCOUNT ON;
...
C# can ignore the new parameter since it is nullable (but since a single output parameter is more efficient than a data reader, you may consider updating your C# code to take advantage of the output parameter later).
Now in proc1:
ALTER PROCEDURE dbo.up_proc1
AS
BEGIN
SET NOCOUNT ON;
DECLARE #fromProc2 UNIQUEIDENTIFIER;
EXEC dbo.up_proc2
--... other parameters ...,
#fromProc2 = #fromProc2 OUTPUT;
-- now you can use #fromProc2
END
GO