problem with raiseerror() - sql

what I want to do is to create a stored procedure that executes insert statement.There is a possibility the execution to fail because of a check constraint for the table Employee.In that case I want to handle a user-defined error.Obviously the following procedure is not working properly because it always raises my error,but not only when insertion fails.
EXEC sp_addmessage 50001, 16, N'Title must be one of the following - Captain,Engineer,Flight-attendant,Purser,First-officer';
CREATE PROCEDURE InsertIntoEmployee
#firstName nvarchar(30),
#familyName nvarchar(30),
#title nvarchar(50),
#address nvarchar(50),
#chiefID int ,
#salary money ,
#FK_IDCrew int,
#FK_DepartmentID int
AS
BEGIN
declare #err_num int;
declare #err_sev int;
declare #err_msg int;
begin try
insert into Employee(firstName, familyName, title, address, chiefID, salary, FK_IDCrew,
FK_DepartmentID)
values(#firstName, #familyName, #title, #address, #chiefID, #salary, #FK_IDCrew,
#FK_DepartmentID);
raiserror(50001,16,1);
END try
begin catch
set #err_num=ERROR_NUMBER();
set #err_sev=ERROR_SEVERITY();
set #err_msg=ERROR_STATE();
raiserror(#err_num,#err_sev,#err_msg);
end catch
end
GO

In this case:
Title should be a lookup to another table and a foreign key
In the CATCH block you can trap the FK constraint violation separately if you want...
...but you'd only allow rows from the new table in your client so I wouldn't personally
No need for a sys.messages entry
Your code will also always hit the RAISERROR too which doesn't add any value,

I hope that the dimensions mentioned in the parameter list is sycn with table columns length.
Before insertion, You should check take care of following points.
Check the existence of #FK_IDCrew value in it's table.
Check the existence of #FK_DepartmentID value in it's table.
It should be like below.
If Not Exists(Select IDCrewColumnName From Table Where columnName = #FK_IDCrew)
Begin
return here from the stored procedure.
End
In case any of them fails to meet the conditions, you should show some user friendly message to user that
(a) Crew ID, you are going to insert, either deleted or does not exists in the database.
(b) DepartmentID, you are going to insert, either deleted or does not exists in the database.
In this way the probability of error will also come to an end.

Related

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.

Insert / update Deadlock with SQL Server

I have a table A (id int, domain nvarchar, status nvarchar) and a trigger A_trigger after insert on table A. The trigger calls a stored procedure and depending on the result of the procedure, updates the status on the newly inserted row.
When I run it in two sessions, I end up with a deadlock issue:
Isolation level: read committed
INSERT statement:
INSERT INTO dbo.TEST_TRIGGER (DOMAIN)
VALUES ('toto')
Trigger:
CREATE TRIGGER dbo.dim_trigger
ON db.dbo.TEST_TRIGGER
AFTER INSERT
AS
DECLARE #status nvarchar(200),
#domain nvarchar(200),
#trackingId int
BEGIN
SET NOCOUNT ON;
-- This code assumes we insert one and only one row at a time.
SELECT #trackingId = id, #domain = domain FROM INSERTED;
DECLARE #toCallProcName NVARCHAR(200);
SET #toCallProcName = 'db.dbo.'+#domain+'_proc';
EXEC #toCallProcName #status out;
UPDATE db.dbo.TEST_TRIGGER
SET status = #status
WHERE id = #trackingId;
END
I tried to:
issue the update statement with WITH (UPDLOCK), but that's not working
Creating an index on (ID) works, but I'm concerned about this solution!!
EDIT1:
Table schema:
CREATE TABLE [dbo].[TEST_TRIGGER]
(
[DOMAIN] [NVARCHAR](200) NOT NULL,
[ID] [BIGINT] IDENTITY(1,1) NOT NULL,
[STATUS] [NVARCHAR](100) NULL
)
Stored procedure:
CREATE PROCEDURE [dbo].[toto_proc]
#res NVARCHAR(200) OUTPUT
AS
BEGIN
SET NOCOUNT ON;
WAITFOR DELAY '00:00:5'
PRINT 'This is me: '+CONVERT(VARCHAR(8), GETDATE(), 108) ;
SET #res = 'OK'
END
Any help?
Thanks
Reason for your issue is, Triggers keep holding the lock on the base table. Due to multiple session and same resource this is bound to happen. Having no index on the table will cause table scan every time for each different session and cause deadlock. Applying the index is the right move, because in this way your update statement will hit the granular level. So this should work. I suggest an additional hint of RowLock in your update statement.
Important : I am assuming, each insert will have only one value to be inserted. Other wise this Trigger has issues. (As mentioned by marc_s)
What you need to do is to create an index on your table
example:
CREATE INDEX IX_Test_Trigger ON dbo.TEST_TRIGGER(Id)
Read this for more info about Index and its impact on locking mechanism:
https://www.mssqltips.com/sqlservertip/2517/using-a-clustered-index-to-solve-a-sql-server-deadlock-issue/

SQL Insert Into Value with condition included

I'm trying to write a stored procedure to modify a session in my Sessions table. I need to be able to insert values into a specified row i.e. with a condition included although I'm not sure how.
Here is my code (I'm aware that I cannot do INSERT INTO > VALUES > WHERE but I'm trying to give you an idea of what I want to do).
CREATE PROCEDURE [dbo].[TT_Modify_Session]
#SessionName NVARCHAR(50),
#TrainingName NVARCHAR(100),
#Trainee NVARCHAR(20),
#TrainingDate DATE,
#SessionID INT
AS
SET NOCOUNT ON;
BEGIN TRY
BEGIN TRAN
INSERT INTO dbo.TT_Sessions (SessionName, Trainee, TrainingDate, TrainingName)
VALUES #SessionName, #Trainee, #TrainingDate, #TrainingName
WHERE #SessionID = [SessionID]
COMMIT
END TRY
BEGIN CATCH
ROLLBACK
PRINT ERROR_MESSAGE()
END CATCH
RETURN #sessionID
Any help is greatly appreciated!
You describe code to "modify" values that already exist in the table. That's an UPDATE...
(INSERT adds a new row to a table, and leaves all pre-existing rows as they were...)
UPDATE
dbo.TT_Sessions
SET
SessionName = #SessionName,
Trainee = #Trainee,
TrainingDate = #TrainingDate,
TrainingName = #TrainingName
WHERE
SessionID = #SessionID

Why this code would fail or not fail?

What is the best way to write it ? Since I have written it but I suspect it's not a good way.
Scenario: I have written a store procedure which simply INSERTS a record into TABLE and then picks last inserted Primary key and return it to front end application i.e.
#parameter1 int,
#Parameter2 smallint,
#Error varchar(MAX) output,
#OutPutParamter int Output
Begin
Begin Try
insert into table1
values (#parameter1, #parameter2)
Set #OutPutParamter= Scope_Identity()
End Try
Begin Catch
set #Error= Select Error_Message()
set #OutPutParameter= 0
End Catch
End
Now question is that how #OutPutParameter would be treated ? What hazards in returning a value could be ?
The query you write is correct.
Scope_Identity() will get the id which the record insert into the table
#OutPutParamter is a output type variable, after run the SP, you can find the id from #OutPutParamter.
You also can use a local variable to get the id and use select to return the value

Lock Stored Procedure in SQL

I have a stored procedure which inserts info into multiple tables and gets the IDs by SCOPE_IDENTITY(). I would like to prevent multiple users from executing it at the same time, so that my IDs don't get mixed up.
How do I lock it? I have read about sp_getapplock and sp_releaselock, but there is no clear explanation how to use it. Below I put my procedure.
create procedure AddPerson(
#Name nvarchar(255),
#LastName nvarchar(255),
#City nvarchar(255),
#Address nvarchar(255)
)
as
BEGIN TRY
BEGIN TRANSACTION
insert into Location(Address, City)
values(#Address, #City)
declare #LocationID int
set #LocationID = SCOPE_IDENTITY()
insert into PersonalInfo(Name, LastName)
values (#Name, #LastName)
declare #PersonInfoID int
set #PersonInfoID = SCOPE_IDENTITY()
insert into Teacher
values(#LocationID, #PersonInfoID)
COMMIT
END TRY
BEGIN CATCH
ROLLBACK
END CATCH
No locking of any kind needed - this behavior you're trying to accomplish is already in place implicitly.
SCOPE_IDENTITY() returns the new ID in the scope of each transaction - so if 10 users are running this simultaneously, each will get their own, separate ID back from SCOPE_IDENTITY