Exception Handling in T-SQL - Multiple messages vs. multiple states? - sql-server

I've got a stored procedure that can fail for a number of reasons:
A parameter has the value of zero.
Foreign key violation in Table A.
Foreign key violation in Table B.
Unique constraint violation in Table C.
In terms of 'best practice', should I be using error states or error messages to notify the end user of the problem?
I know states are used to indicate where an error occurred in the actual code, but to an end user it's useless without some kind of documentation.
Having an entry in sys.messages seems overkill for every possible failure case for every stored procedure/trigger in the database.
So should I do this:
THROW 50001, 'Parameter value cannot be 0', 1
THROW 50002, 'Matching value not found in table A', 1
THROW 50003, 'Matching value not found in table B', 1
THROW 50004, 'Combination of values already exists in table C.', 1
Or this:
THROW 50001, 'An error occurred in the procedure.', 1
THROW 50001, 'An error occurred in the procedure.', 2
THROW 50001, 'An error occurred in the procedure.', 3
THROW 50001, 'An error occurred in the procedure.', 4

Related

Raising exception in others on Snowflake

I'm migrating some stored procedures from Oracle to Snowflake and I need to raise an exception in others segment of exception on snowflake exception including a column from cursor. Here is an example from oracle:
create or replace PROCEDURE sp
AS
CURSOR SCGR
IS
SELECT SP.REQUEST_NUM,
…
from table;
BEGIN
FOR I IN SCGR
LOOP
BEGIN
INSERT
INTO table(…)
VALUES
(…);
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001,'An error was encountered in CUST_GROUP_REQUEST insert for - '||I.REQUEST_NUM||' - '||SQLCODE||' -ERROR- '||SQLERRM);
EXIT;
END;
END;
With this part I'm having troubles in Snowflake:
EXCEPTION
WHEN OTHERS THEN
RAISE_APPLICATION_ERROR(-20001,'An error was encountered in CUST_GROUP_REQUEST insert for - '||I.REQUEST_NUM||' - '||SQLCODE||' -ERROR- '||SQLERRM);
EXIT;
Can someone help me?
If you check the RAISE command, you will see that it accepts an exception name, not a string expression which you can build dynamically.
https://docs.snowflake.com/en/sql-reference/snowflake-scripting/raise.html
Do you really need to raise an exception, or want to return a result cotaining the exception details?
https://docs.snowflake.com/en/developer-guide/snowflake-scripting/exceptions.html?_ga=2.186509665.956198205.1648142906-1409600040.1607023304#handling-an-exception
return object_construct('Error type', 'Other error',
'SQLCODE', sqlcode,
'SQLERRM', sqlerrm,
'SQLSTATE', sqlstate);

Unknown error during execution of trigger

I've got an insert/update trigger set up which prevents an employee from existing in two tables at the same time. It works fine with catching illegal insertions/updates but i'm also getting another error report when testing the trigger with illegal insertions/updates.
Here's my code:
CREATE OR REPLACE TRIGGER check_foobar
BEFORE INSERT OR UPDATE OF VarX ON FOOBAR
FOR EACH ROW
DECLARE
counter NUMBER(38);
BEGIN
SELECT count(*)
INTO counter
FROM BARFOO
WHERE VarX = :NEW.VarX
GROUP BY VarX;
IF counter > 0 THEN
RAISE_APPLICATION_ERROR(-20001, 'This is an illegal insertion/update');
END IF;
EXCEPTION WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE('TEST');
END;
/
And the errors ORA-06512 and ORA-04088 i'm not sure of:
SQL> INSERT INTO DRIVER VALUES(2, 10345, 'AVAILABLE');
Error starting at line : 26 File #test.sql
In command -
INSERT INTO FOOBAR VALUES(2)
Error report -
ORA-20001: This is an illegal insertion/update
ORA-06512: at "HR.CHECK_FOOBAR", line 11
ORA-04088: error during execution of trigger 'HR.CHECK_FOOBAR'
When i add an exception handler for the select statement my trigger stops working properly and the illegal insertion isn't prevented. But the execution error is prevented.
UPDATE: I've added a group by and an exception to the trigger, so now the trigger still works with an exception handler but the errors ORA-06512 and ORA-04088 are still coming up with the illegal insertion/update.
Line 11 mentioned in the error is
GROUP BY VarX;
Any advice would be much appreciated.
There is no problem here, it is working as expected.
Error starting at line : 26 File #test.sql
In command -
INSERT INTO FOOBAR VALUES(2)
This is referring to the line in your script where you have the INSERT statement. It's telling you an exception was raised at this point.
Error report -
This is showing the error stack:
ORA-20001: This is an illegal insertion/update
This is the actual exception that was raised.
ORA-06512: at "HR.CHECK_FOOBAR", line 11
ORA-04088: error during execution of trigger 'HR.CHECK_FOOBAR'
These are additional messages just to tell you where the exception was originally raised, in this case, in your trigger on line 11, where the RAISE_APPLICATION_ERROR is, as you might expect. Note that line numbers for triggers refer to the executable portion of the trigger, so in your case the DECLARE is line 1.
The ORA-04088 error means that the trigger has an un-handled exception. You are raising a application error but then not handling it. You need to handle this exception as per below.
CREATE OR REPLACE TRIGGER check_foobar
BEFORE INSERT OR UPDATE OF VarX ON FOOBAR
FOR EACH ROW
DECLARE
counter NUMBER(38);
BEGIN
SELECT count(*)
INTO counter
FROM BARFOO
WHERE VarX = :NEW.VarX
GROUP BY VarX;
IF counter > 0 THEN
RAISE_APPLICATION_ERROR(-20001, 'This is an illegal insertion/update');
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('TEST');
WHEN OTHERS THEN
IF SQLCODE = -20001 THEN
-- do some logging
RAISE;
ELSE
-- do some logging and any other actions you feel are needed. Then depending on needs you can raise or not.
END IF;
END;
/

Error ID 50,000 being returned within error process

I notice when this error is triggered within the stored procedure it returns 50,000. Is there a way to modify this to say 50,999 so the front-end app can specifically pick the error up and not confuse it with anything else.
RAISERROR('Client already has an Active Visit!',16,1)
As per the documentation of RAISERROR (Transact-SQL):
The message is returned as a server error message to the calling
application or to an associated CATCH block of a TRY…CATCH construct.
New applications should use THROW instead.
Emphasis mine. (THROW (Transact-SQL))
I don't know what your SQL statement looks like, but, instead you can therefore do something like:
BEGIN TRY
--Your INSERT statement
SELECT 0/0; --Causes an error
END TRY
BEGIN CATCH
THROW 50099, 'Client already has an Active Visit!',1;
END CATCH
With RAISEERROR, if you use message as the first parameter then you can't specify an error ID and it is implicitly 50000. However, you can create a custom message with parameters and pass your code there. ie:
RAISERROR('Client already has an Active Visit! - Specific Err.Number:[%d]',16,1, 50999)
Also Try\Catch is the suggested method for new applications.
You need to specify the first parameter of the raiseerror function, like so:
--configure the error message
sp_addmessage #msgnum = 50999,
#severity = 16,
#msgtext = N'Client %s already has an Active Visit!';
GO
-- throw error
RAISERROR (50999, -- Message id.
16, -- Severity,
1, -- State,
N'123456789'); -- First argument supplies the string.
GO
Output will be
Msg 50999, Level 16, State 1, Line 8
Client 123456789 already has an Active Visit!
If you don't specify the error number, the raiserror will be assumed to be 50000. Documentation here...

How to set SQLException Number

I'm having an issue on settin up SqlException.Number
On my Stored Proc i'm raising an error
--#number = 50001
RAISERROR(#number, 16, 1) -
I should expect that the Error_Number() should be #number but I always get 18054
Is there something wrong with my RAISERROR?
Check the sys.messages table for error code 74601. if this is a user defined error, it shouold be added in the table.
for any error that is greater than 50000 should give you this output if not found.
Msg 18054, Level 16, State 1, Line 1
Error XXXXX, severity 16, state 1 was raised, but no message with that error number was found in sys.messages. If error is larger than 50000, make sure the user-defined message is added using sp_addmessage.
There is one small caveat: You can't supply a message on your own in this case. But this can be circumvented by adding an additional %s in the sp_addmessage call or by changing all mapped messages to your own pattern and supplying the right parameters in the raiseerror call.
Check there for more information:
SQL Server: Rethrow exception with the original exception number
RAISERROR can either reference a user-defined message stored in the
sys.messages catalog view or build a message dynamically.
Check for your error message exists or not using this:
select * from sys.messages
If it does not exists then Use sp_addmessage to add user-defined error messages and sp_dropmessage to delete user-defined error messages.
for more information follow RaiseError documentation.

Null value return problem

I wanted to check my monthly payroll in which there are employee salary along with other detail.
I have created a PL/SQL block but when I place my condition for checking of existing employee id with another table some return null value and hence my table does not go further.
set serveroutput on
declare
emp_id NUMBER :=&emp;
temp NUMBER;
begin
select nvl(employee_id,10) into temp FROM bhavya_temp where bhavya_temp.employee_id=emp_id;
dbms_output.put_line(temp);
if temp is NULL
then
dbms_output.put_line('employee ID does not exist');
else
dbms_output.put_line('bye');
end if;
end;
When I enter employee id 1 or 2 which exist in table the result is
anonymous block completed
1
bye
When I enter 3 or more which is not there
Error report:
ORA-01403: no data found
ORA-06512: at line 6
01403. 00000 - "no data found"
*Cause:
*Action:
Thanks for help in advance.
I've never written any PL/SQL, but a very similar error is reported here.
It looks like you can handle the problem by placing the SELECT ... INTO portion inside of a BEGIN ... EXCEPTION .. END block.
Hope this helps.
It would be helpful to have the create table statements with some example data, so that the problem can be reproduced.
To the question : If the row with the ID does not exist vou get the ORA-1403 . There is no data, there is no NULL to convert. Oracle does not dream a row up for you.
Wrap your select stament in an exception block and catch the 'no data found' exception, and act accordingly

Resources