I already created the error in sys.message. The problem is when I add it to my stored procedure, it doesn't pass me the message back. The stored procedure checks to see if an id exists in a certain areacode if the id does not exists the raiserror should be fired.
AS
BEGIN
DECLARE #Result as int
IF EXISTS(SELECT ID, areacode
FROM Table1
WHERE ID = #ID
AND areacode = #areacode)
RAISERROR (50030, 1, 1)
BEGIN
INSERT INTO Table2 ( //columns go here )
VALUES ( //values for columns )
END
DECLARE #Result as int
IF EXISTS(SELECT 1
FROM Table1
WHERE ID=#ID
AND areacode=#areacode
)
BEGIN
INSERT INTO Table2 (/* columns go here */)
VALUES (/* values for columns */)
END ELSE BEGIN
RAISERROR (50030,1,1)
END
Your severity is to low, try to set it a bit higher:
raiserror (50030,16,1)
Btw. why do you try to raise the error right before your insert?
https://msdn.microsoft.com/en-us/library/ms178592.aspx
Related
I need to load in data first and then insert data into the same table.
I use a stored procedure to load in the data (I'm using SQL Server):
#employeeid int,
#thequestion varchar (220) output
)
as
begin
begin transaction
select #thequestion = thequestion
from question q
join contentment c on q.questionid= c.questionid
where c.employeeid = #employeeid
if ##ERROR <> 0
begin
rollback
raiserror ('You don't have question to answer', 16, 1)
return
end
commit
end
Then the user can add data to the table contentment. He only can add a score and a comment
Contentment table:
employeeid,
questionid,
date,
score,
comment
I using this stored procedure:
(#score int,
#comment varchar(50),
#date date
)
as
begin
begin transaction
insert into contentment (date, score, comment)
values (#date, #score, #comment)
if ##ERROR <> 0
begin
rollback
raiserror ('-some error-', 16, 1)
return
end
commit
end
The problem is that in the second stored procedure questionid and employeeid should not be inserted, it is already inserted, to link a employeeid to a questionid. But when I want to add a score and comment to this, I get an error that questionid and employeeid need to be inserted (otherwise it has value NULL). Also the problem is that my second stored procedure doesn't know which question belongs to the questionid/employeeid. I hope someone understand this, I know it is a bit weird.
Sorry I was busy travelling.
I tried creating 2nd procedure as follows.
Here I have created employeeid and questionid as 2 variables which we will passs while executing this stored procedure:
create procedure Proc2
(
#employeeid int
,#questionid int
,#score int
,#comment varchar(50)
,#date date
)
as
begin
begin transaction
insert into contentment (date, score, comment)
select #date = date, #score = score, #comment = comment
where employeeid = #employeeid and questionid = #questionid
if ##ERROR <> 0
begin
rollback
raiserror ('-some error-', 16, 1)
return
end
end transaction
commit
end
I have a code below that should insert records into the table but unfortunately this code foes not work in case multiple records are inserted or updated or deleted. How should I rewrite the code for procedure to loop through all the inserted / deleted records? And I do need to use that stored procedure with Input parameters (not just simple insert into ... select ... from ...)
IF EXISTS (SELECT * FROM MyDB.sys.triggers WHERE object_id = OBJECT_ID(N'[dbo].[MyTable_DEL_UPD_INS]'))
DROP TRIGGER [dbo].[MyTable_DEL_UPD_INS]
GO
CREATE TRIGGER [dbo].[MyTable_DEL_UPD_INS]
ON [MyDB].[dbo].[MyTable]
AFTER DELETE, UPDATE, INSERT
NOT FOR REPLICATION
AS
BEGIN
DECLARE #PKId INT,
#Code VARCHAR(5),
#AuditType VARCHAR(10)
SET #Code = 'TEST'
IF EXISTS (SELECT * FROM deleted d)
AND NOT EXISTS (SELECT * FROM inserted i)
BEGIN
SELECT TOP 1
#PKId = d.[MyTable_PK],
#AuditType = 'DELETE'
FROM
deleted d WITH (NOLOCK)
IF #PKId IS NOT NULL
AND #Code IS NOT NULL
EXEC MyDB.[dbo].[SP_Audit] #PKId, #Code, #AuditType
END
IF EXISTS (SELECT * FROM deleted d)
AND EXISTS (SELECT * FROM inserted i)
BEGIN
SELECT TOP 1
#PKId = d.[MyTable_PK],
#AuditType = 'UPDATE'
FROM
deleted d WITH (NOLOCK)
IF #PKId IS NOT NULL
AND #Code IS NOT NULL
EXEC MyDB.[dbo].[SP_Audit] #PKId, #Code, #AuditType
END
IF NOT EXISTS (SELECT * FROM deleted d)
AND EXISTS (SELECT * FROM inserted i)
BEGIN
SELECT TOP 1
#PKId = d.[MyTable_PK],
#AuditType = 'INSERT'
FROM
deleted d WITH (NOLOCK)
IF #PKId IS NOT NULL
AND #Code IS NOT NULL
EXEC MyDB.[dbo].[SP_Audit] #PKId, #Code, #AuditType
END
END
GO
ALTER TABLE [MyDB].[dbo].[MyTable] ENABLE TRIGGER [MyTable_DEL_UPD_INS]
You should avoid using loops in triggers.
Triggers should be as quick to run as possible, since SQL Server will not return control to whatever statement that fired the trigger until the trigger is completed.
So instead of a loop, you should modify your SP_Audit procedure to work with multiple records instead of a single one.
usually, this is easily be done using a table valued parameter.
If you could post the SP_Audit as well, we could give you a complete solution.
Since you didn't post it, you can use these guidelines as a start:
First, you create a user defined table type:
CREATE TYPE dbo.Ids AS TABLE
(
Id int NOT NULL PRIMARY KEY
)
GO
Then, you create the procedure to use it:
CREATE PROCEDURE [dbo].[STP_Audit_MultipleRecords]
(
#IDs dbo.Ids readonly,
#Code CHAR(4),
#AuditType CHAR(6)
)
AS
-- Implementation here
GO
Last, your write your trigger like this:
CREATE TRIGGER [dbo].[MyTable_DEL_UPD_INS]
ON [MyDB].[dbo].[MyTable]
AFTER DELETE, UPDATE, INSERT
NOT FOR REPLICATION
AS
BEGIN
DECLARE #HasDeleted bit = 0,
#HasInserted bit = 0,
#AuditType CHAR(6),
#Code CHAR(4)
SET #Code = 'TEST'
DECLARE #IDs as dbo.Ids
IF EXISTS (SELECT * FROM deleted d)
SET #HasDeleted = 1
IF EXISTS (SELECT * FROM inserted i)
SET #HasInserted = 1
IF #HasDeleted = 1
BEGIN
IF #HasInserted = 1
BEGIN
SET #AuditType = 'UPDATE'
END
ELSE
BEGIN
SET #AuditType = 'DELETE'
END
END
ELSE
IF #HasInserted = 1
BEGIN
SET #AuditType = 'INSERT'
END
INSERT INTO #IDs (Id)
SELECT [MyTable_PK]
FROM inserted
UNION
SELECT [MyTable_PK]
FROM deleted
EXEC [dbo].[STP_Audit_MultipleRecords] #IDs, #Code, #AuditType
END
GO
Notes:
The #HasDeleted and #HasInserted variables are to allow you to only execute the EXISTS query once for every procedure.
Getting the primary key values from the deleted and inserted table is done using a single union query. Since union eliminates duplicate values, you can write this query just once. If you want to, you can write a different query for each audit type, but then you will have to repeat the same query 3 times (with different tables)
I've changed the data types of your #code and #AuditType variables to char, since they have a fixed length.
I know that the SCOPE_IDENTITY() will get the last inserted row from insert statement. However, for the following case, I am not too sure is SCOPE_IDENTITY() is safe. As SELECT MAX(ID) FROM TableA will have go through scan the table to get the max id and it will have performance issue, even slightly, I believe.
Here is the case:
DECLARE #DaysInMonth INT
DECLARE #FirstID INT
DECLARE #SecondID INT
DECLARE #ThirdID INT
DECLARE #FourthID INT
SET #DaysInMonth = DAY(EOMONTH('2016-09-01'))
BEGIN TRY
BEGIN TRANSACTION
WHILE #DaysInMonth > 0
BEGIN
-- First Insert -- Begin
INSERT INTO tableA ('first insert - ' + #DaysInMonth)
-- First Insert -- End
SET #FirstID = SCOPE_IDENTITY()
-- Second Insert -- Begin
INSERT INTO tableB ('second insert - ' + #DaysInMonth)
-- Second Insert -- End
SET #SecondID = SCOPE_IDENTITY()
-- Third Insert -- Begin
INSERT INTO tableC ('third insert - ' + #DaysInMonth)
-- Third Insert -- End
SET #ThirdID = SCOPE_IDENTITY()
-- Fourth Insert -- Begin
INSERT INTO tableD ('fourth insert - ' + #DaysInMonth)
-- Fourth Insert -- End
SET #FourthID = SCOPE_IDENTITY()
SET #DaysInMonth = #DaysInMonth - 1
END
COMMIT TRANSACTION
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
ROLLBACK TRANSACTION
THROW
END CATCH
As from the case above, I have to insert the records every loop for fourth times for how many days in the month that I have declared.
From what I know, there are 4 to get the last inserted ID:
SCOPE_IDENTITY
##IDENTITY
SELECT MAX(ID) FROM tableA
IDENT_CURRENT
From the following post:
Post
Is mentioned that SCOPE_IDENTITY() is what generally that you want to use.
What I mean with 'Safe' is, do the ID will be unique during the loop?
Thank you.
You can use OUTPUT column in the last insert statement, Ofcourse this is another option where you will get what exactly input statement executed.. Below is just an example
CREATE TABLE #tablea (
id int IDENTITY (1, 1),
val char(10)
)
DECLARE #outputtbl TABLE (
id int,
val char(10)
)
INSERT INTO #tablea (val)
OUTPUT INSERTED.* INTO #outputtbl
VALUES ('test')
SELECT id
FROM #outputtbl
I have written the trigger below that prevents from NULL being entered in the pch_x field . It works fine if i insert 1 row but doesnt work if I enter more than one at once . Could someone please help me out a little ? Here is my code
create trigger test
ON [dbo].TEMP
for INSERT
AS
BEGIN
declare #xcheck varchar(50)
set #xcheck= (select i.pch_x FROM temp L INNER JOIN INSERTED I
ON L.id = I.id)
F (#xcheck is NULL )
begin
RAISERROR('NULL in pch_x', 16, 1)
ROLLBACK
end
END
I'm not sure why you're doing this in a trigger, but the set based way to do this test would be to use EXISTS:
create trigger test
ON [dbo].TEMP
for INSERT
AS
BEGIN
IF EXISTS(select * FROM temp L INNER JOIN
INSERTED I
ON L.id = I.id
where i.pch_x IS NULL)
begin
RAISERROR('NULL in pch_x', 16, 1)
ROLLBACK
end
END
I'm also not sure why you're joining back to the table - I'd have thought the check could run without reference to temp:
create trigger test
ON [dbo].TEMP
for INSERT
AS
BEGIN
IF EXISTS(select * FROM INSERTED
where pch_x IS NULL)
begin
RAISERROR('NULL in pch_x', 16, 1)
ROLLBACK
end
END
For you unusual requirement that, in a rowset containing some rows with nulls, you want success for those rows without nulls and failure for those rows with nulls, most sensible would be an INSTEAD OF trigger:
create trigger test
ON [dbo].TEMP
INSTEAD OF INSERT
AS
BEGIN
declare #rc int
INSERT INTO dbo.temp (/* column list */)
SELECT /* column list */ from inserted where pch_x IS NOT NULL
set #rc = ##ROWCOUNT
IF #rc <> (select COUNT(*) from inserted)
begin
RAISERROR('NULL in pch_x', 16, 1)
--ROLLBACK
end
END
I have two stored procedures as follows:
create stored procedure p1
as
select * from table1 where datediff(day, table1.[date], getdate())
create stored procedure p2
as
declare #t1 table(
ref varchar(20)
)
insert into #t1 select * from table1 where ref = 'some ref'
declare #t2 table(
fname varchar(20),
lname varchar(20),
email varchar(1000)
)
declare #len int = (select count(ref) from #t1)
while #len > 0
begin
declare #value varchar(20) = (select top 1 ref from #t1)
insert into #t2 select * from table2 where ref = #ref
delete from #t1
where ref = #value
set #len = (select count(ref) from #t1)
end
select * from #t2
Java code
....
String query = "Execute [p2]";
try(CallableStatement cstmt = conn.prepareCall(query);
ResultSet rs = cstmt.executeQuery()){
... some code
}
The table variable #t1 hold select result from a table 'table1'
The variable #len hold the number of rows in #t1
Using #len > 0 as condition in while loop, I want to select records from another table 'table2' the table variable #t2 hold the select records from 'table2'
The delete statement removes value from #t1
#len set to new number of rows in #t1
the last statement return all the records store in #t2
The first procedure works fine, but the second procedure works only in SQL Server.
I get this an error message in my java application
statement did not return a resultset
I want this to return a result set with the select statement I have at the
end of the query.
Please is there a way around this?
Your [p2] stored procedure needs to include SET NOCOUNT ON right at the beginning to suppress the "n rows affected" counts so JDBC doesn't get confused as to what it should put into the ResultSet:
CREATE PROCEDURE p2
AS
SET NOCOUNT ON;
declare #t1 table(
ref varchar(20)
)
-- ... and so on
For more information on SET NOCOUNT see
SET NOCOUNT (Transact-SQL)
For more information on precisely what gets returned from a stored procedure call, see
How to get everything back from a stored procedure using JDBC
use method "execute" instead of "executeQuery".