Stored procedures - writing errors out to another table - sql

I have two tables and a simple stored procedure. I want to use the stored procedure to insert into table1 with one of the parms being the primary key.
Here is where I am having trouble:
I want to have it so that if the parm/primary key the user has entered into the stored procedure and run is already on the table then it writes out the fields to a second table as well as the error description.
How do you capture / output this error information?
Stored procedure (sans the error logging):
CREATE PROCEDURE procedure1
#Primary INT,
#Info NVARCHAR
AS
BEGIN
SET NOCOUNT ON
INSERT INTO Table1
(
Primary ,
Info
)
VALUES
(
#Primary ,
#Info
)
END
Thanks

CREATE PROCEDURE dbo.InsertSomething
(
#PrimaryKey INT
, #Info NVARCHAR(MAX)
)
AS
BEGIN
IF EXISTS (SELECT 1 FROM dbo.Table1 WHERE ID = #PrimaryKey)
BEGIN
INSERT INTO dbo.Table2 (ID, Info)
VALUES (#PrimaryKey, #Info);
RAISERROR (15600,-1,-1, 'Your custom message');
END
ELSE
BEGIN
INSERT INTO dbo.Table1 (ID, Info)
VALUES (#PrimaryKey, #Info);
END
END
All you have to do is a simple check if record already exists in first table, and if it does just insert into it and throw error.

Related

INSERT return value of stored procedure

I'm trying to INSERT a RETURN value for a stored procedure and I just can't get it to work.
I have one table called "person.person_ids" with fields "id int, uid uniqueidentifier"
and a bunch of other tables called for example "person.employee", "person.client" etc. These tables all get their "id"s as foreign keys from the "person.person_ids" table.
The procedure does this:
CREATE PROCEDURE PERSON.NewPerson
AS
BEGIN
DECLARE #ret INT;
INSERT INTO PERSON.ID_PERSON
VALUES (NEWID());
SET #ret = SCOPE_IDENTITY()
RETURN #ret;
END
GO
and my INSERT looks like this:
INSERT INTO PERSON.EMPLOYEE VALUES
(EXECUTE PERSON.NewPerson, 1, '15434235', '10768348153', '1962-3-2', '1999-10-2', 'PETER', '', 'SMITH', 'HAMMER')
GO
Basically I'm trying to create a stored procedure of function that I can call to automatically generate a new record in "person.person_ids" whenever I insert a new record into "person.employee".
I tried using OUTPUT from INSERT but I could not get it to work and INSERT isn't allowed inside of functions.
Here is your code with the issues fixed and some recommended improvements. The comments explain what and why.
SP:
CREATE PROCEDURE PERSON.NewPerson
(
-- Use an output parameter to get values out of an SP
#NewId INT OUT
)
AS
BEGIN
-- Recommended to always list the columns you are inserting to
-- Personally my preference is to select them (because that scales to multiple inserts), I never use the 'values' clause.
INSERT INTO PERSON.ID_PERSON (uid)
SELECT NEWID();
SET #NewId = SCOPE_IDENTITY();
-- The return statement is for a status for the SP, usually 0 for success, some other int for an error
RETURN 0;
END
GO
Calling SP:
DECLARE #MyNewId INT;
-- Run the SP before your insert to get your new value
EXEC PERSON.NewPerson #MyNewId OUT;
-- Then insert - ideally with a list of columns
INSERT INTO PERSON.EMPLOYEE
SELECT #MyNewId, 1, '15434235', '10768348153', '1962-3-2', '1999-10-2', 'PETER', '', 'SMITH', 'HAMMER'

Microsoft SQL Server - default value provided by stored procedure

Is it possible to have a non-null column where the value is generated at insert by calling a stored procedure the parameters of which are values passed to insert into the row?
For example, I have table User:
| username | name | surname | id |
Insert looks like this:
INSERT INTO USER (username, name, surname)
VALUES ('myusername', 'myname', 'mysurname');
The id column is populated with an (integer) value retrieved by calling stored procedure mystoredproc with parameters myusername, myname, mysurname.
A further question is, would this stored procedure be called on each row, or can it be called in a grouped fashion. For example, I'd like my stored procedure to take the name and append a random integer to it so that that if I insert 100 users with the name 'David', they will get the same id and the stored procedure will be called only once. A bit of a bad example on the second point.
Good day,
Is it possible to have a non-null column where the value is generated at insert by calling a stored procedure
Option 1: please check if this work for you
Specify Default Value for the Column and use "NOT NULL"
create trigger on the table AFTER INSERT
Inside the trigger, you can use the virtual table "inserted" in order to get the inserted values.
Using these values (using the inserted table) you can update the column using the logic you need for all the rows at once
** there is no need to use external SP probably, but you can execute SP from trigger if needed
** All executed by a trigger is in the same transaction as the original query.
would this stored procedure be called on each row
NO! The trigger will be executed once for all rows you insert in the same statement. The inserted table includes all the rows which were inserted. In your update section (step 4) you can update all the rows which were inserted in once and no need to execute something for each row
** If you do use external SP which is executed from the trigger then you can pass it all the inserted table as one using Table-Valued Parameter
------------------- update ---------------
Here is a full example of using this logic:
drop table if exists T;
CREATE TABLE T (id int identity(2,2), c int NOT NULL default 1)
GO
CREATE TRIGGER tr ON T AFTER INSERT
AS BEGIN
SET NOCOUNT ON;
UPDATE T SET T.c = T2.C + 1
FROM inserted T2
INNER JOIN T T1 ON T1.id = T2.id
END
INSERT T(c) values (1) -- I insert the value 1 but the trigger will change it to 1+1=2
select * from T
GO
-- test multiple rows:
INSERT T(c) values (10),(20),(30),(40)
select * from T
GO
DECLARE #rc INT = 0,
#UserID INT = ABS(CHECKSUM(NEWID())) % 1000000 + 1;
WHILE #rc = 0
BEGIN
IF NOT EXISTS (SELECT 1 FROM dbo.Users WHERE UserId= #UserId)
BEGIN
INSERT dbo.Users(UserId) WHERE Username = #UserName SELECT #UserId;
SET #rc = 1;
END
ELSE
BEGIN
SELECT #UserId = ABS(CHECKSUM(NEWID())) % 1000000 + 1,
#rc = 0;
END
END

Inserting output parameters from stored procedure directly

I have a stored procedure that returns a pair of output parameters - the ID and the computed value. Is it possible to use a trigger with an insert statement, that inserts those two values directly? Something like this
CREATE TRIGGER Trig_FirstTable ON SecondTable AFTER UPDATE
AS
BEGIN
INSERT INTO FirstTable (OtherID, OtherValue)
VALUES (#otherID, #otherValue)
FROM StoredProcedure inserted.ID, inserted.Value, #otherID OUTPUT, #otherValue OUTPUT
END
According to the MSDN documentation you can use INSERT into with EXEC.
They give the following example:
--INSERT...EXECUTE procedure example
INSERT author_sales EXECUTE get_author_sales
But I think your stored procedure needs a SELECT statement to return the data instead of only filling the output parameters.
You can insert from SP like that:
drop procedure uspTest
GO
create procedure uspTest
AS
select 1 as id, 'x' as val
union all
select 2,'y'
GO
drop table #temp
GO
create table #temp (
id int,
val char(1)
)
GO
insert into #temp (id,val)
EXECUTE uspTest
GO
select
*
from #temp
But you cannot select a subset of columns, so this method will obviously fail if you add more outputs to your SP in the future:
insert into #temp (id)
EXECUTE uspTest
Another way is to store SP results in variables, and then use them for insert.

SQL trigger trouble with TRY--CATCH block for INSERT command

Ok, so I want to create a trigger that fires when someone tries to insert into the table "products" and checks for a valid foreign key. For right now (This is NOT the end design in the least, but I'm using it for testing), I want the trigger to check that the inserted line references a valid ID in the Manufacturer table, and if no such row exists in the Manufacturer table, insert one with the proper ID and some general information for the remaining fields. My current code is as follows:
create trigger checkman
on dbo.products
instead of insert
as
declare
#manid char(5),
#manName varchar(50),
#transactionName varchar(20) = 'transaction1'
Begin
select #manid=Man_ID from Inserted
begin try
/*begin tran #transactionName*/
Insert into Manufacturers (Man_ID, Man_Name, Man_Description) VALUES #manid, 'Unknown Name', 'This is an unknown manufacturer');
insert into dbo.products select * from inserted;
end try
begin catch
/*rollback tran #transactionName;*/
insert into dbo.products select * from inserted;
end catch
End
The problem is that whenever I run my insert with a Manufacturer ID that already exists, I get this error:
(0 row(s) affected)
Msg 3930, Level 16, State 1, Procedure checkman, Line 20
The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.
The statement has been terminated.
I get the same error when I put the "Insert into Manufacturers" line in the catch block, only this time it appears when I try to insert with an ID that doesn't yet exist.
Don't handle this in a try/catch block, it isn't necessary. Also you need to allow for the fact that inserted can contain multiple rows. You can do this using a NOT EXISTS query:
CREATE TRIGGER CheckMan
ON dbo.products
INSTEAD OF INSERT
AS
INSERT Manufacturers (Man_ID, Man_Name, Man_Description)
SELECT DISTINCT Man_ID, 'Unknown Name', 'This is an unknown manufacturer'
FROM inserted i
WHERE NOT EXISTS
( SELECT 1
FROM Manufacturers m
WHERE m.Man_ID = i.Man_ID
);
INSERT dbo.Products
SELECT *
FROM inserted;
HOWEVER, I don't advocate this approach, I think it would be much better to just rely on the referencial integrity provided by the foreign key itself, and if the Man_ID does not exist let the insert fail, and if necessary ensure all Man_IDs exist before even attempting to insert.
I changed your trigger to following. In doing so, I made a few implicit assumptions. Assuming here that Products is a 3 column table (col1 & col2 being the other columns apart from Man_ID).
Hope this helps
alter
trigger checkman
on dbo.products
instead of insert
as
declare
#manid char(5),
#col1 varchar(50),
#transactionName varchar(20) = 'transaction1',
#col2 Varchar(500)
Begin
select #manid=Man_ID, #col1 = col1, #col2 = col2 from Inserted
IF (NOT EXISTS (SELECT Man_ID
FROM Manufacturers Where Man_ID = #manid
))
INSERT INTO ManuFacturers (Man_ID, Man_name, Man_Description) Values (#manid, 'Unknown Name', 'This is an unknown manufacturer')
INSERT INTO Products (Man_ID, col1, col2) values (#manid, #col1, #col2)
--begin try
-- /*begin tran #transactionName*/
-- Insert into Manufacturers (Man_ID, Man_Name, Man_Description) VALUES (#manid, 'Unknown Name', 'This is an unknown manufacturer')
-- insert into dbo.products select * from inserted;
--end try
--begin catch
-- /*rollback tran #transactionName;*/
-- insert into dbo.products select * from inserted;
--end catch
End

Updating a Table after some values are inserted into it in SQL Server 2008

I am trying to write a stored procedure in SQL Server 2008 which updates a table after some values are inserted into the table.
My stored procedure takes the values from a DMV and stores them in a table. In the same procedure after insert query, I have written an update query for the same table.
Insert results are populated fine, but the results of updates are getting lost.
But when I try to do only inserts in the stored procedure and I execute the update query manually everything is fine.
Why it is happening like this?
there should not be a problem in this.
below code working as expected.
create procedure dbo.test
as
begin
create table #temp (
name varchar(100) ,
id int
)
insert #temp
select name ,
id
from master..sysobjects
update #temp
set name='ALL Same'
from #temp
select * from #temp
drop table #temp
end
go
Best approach is to use Trigger, sample of AFTER UPDATE trigger is below:
ALTER TRIGGER [dbo].[tr_MyTriggerName]
ON [dbo].[MyTableName] AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
--if MyColumnName is updated the do..
IF UPDATE (MyColumnName)
BEGIN
UPDATE MyTableName
SET AnotherColumnInMyTable = someValue
FROM MyTableName
INNER JOIN Inserted
ON MyTableName.PrimaryKeyColumn = Inserted.PrimaryKeyColumn
END
END