SQL Server 2012 error handling in stored procedure - sql-server

ALTER PROCEDURE Ins2(#id INT,#name NVARCHAR(50))
AS
BEGIN
BEGIN try
INSERT INTO abc (id, name)
VALUES (#id, #name)
END try
BEGIN catch
INSERT INTO exception
VALUES (Error_number(),
Error_severity(),
Error_state(),
Error_procedure(),
Error_line(),
Error_message(),
Getdate())
SELECT *
FROM exception
END catch
END
ERROR:
Msg 8152, Level 16, State 13, Procedure ins2, Line 19
String or binary data would be truncated.
The statement has been terminated.
In above stored procedure id is the primary key in the abc table.. while running this I want to insert the error details in the error table (exception).. but I am getting the above error..

Error is just due to fields length. Data being inserted contain length more than field length.
Check length of fields in both "abc" and "exception" table.
Hope this will help you.

I suspect an error occurs in your catch block.
Could you confirm that length of all char columns is big enough to hold data you're trying to insert into exception table?
For example the column that should store error message might be smaller in size than error message is.
You might set it to VARCHAR(MAX).

The problem is at the lenght of the data you are interting into the table . #nume param is 50 chars long ( as it's nvarchar(50) ) and the name from the abc table is maybe? not set to nvarchar(50) or bigger or is in a different format.
If you can't modify the structure of the table I suggest you trim the Parameter.
Go to your abc table select it and click ALT+F1, look at the column name and check if it's varchar or nvarchar and it lenght.
ALTER PROCEDURE Ins2(#id INT,#name NVARCHAR(50))
AS
Declare #sName nvarchar( <insert the lenght of the column name from table abc> )
set #sName = (SUBSTRING(LTRIM(RTRIM(#name),0,49)
BEGIN
BEGIN try
INSERT INTO abc (id, name)
VALUES (#id, #sName )
END try
BEGIN catch
INSERT INTO exception
VALUES (Error_number(),
Error_severity(),
Error_state(),
Error_procedure(),
Error_line(),
Error_message(),
Getdate())
SELECT *
FROM exception
END catch
END
`Declare #sName nvarchar( <insert the lenght of the column name from table abc> )` <--- this declare an variable with the lenght equal to the one from the table abc
`set #sName = (SUBSTRING(LTRIM(RTRIM(#name),0,49)` <-- select the first 50 chars from the parameter
LTRIM(RTRIM(#name) <-- always use this if you don't want any black spaces on either sides of the string
Also there is the posibility to output an error if the paramter lenght is to much for the field name in table abc.

Related

Compare two KeyValue types data in SQL Server 2012

I have the below data available in a table
DECLARE #AddressTbl As Table (ID int identity,Address varchar(100))
INSERT INTO #AddressTbl
VALUES ('State:AndhraPradesh,Dist:Prakasam')
Next time when I enter the same value I should be notified that this value exists in the table.
For this I will use an sp with warning message that the data is available. But I want how to implement the logic to compare the data.
Create Procedure usp_InsertAddress
(
#Address varchar(100)
)
AS
DECLARE #ID INT
SELECT #ID=(SELECT ID FROM #AddressTbl WHERE Address = #Address)
IF #ID IS NULL
BEGIN
INSERT INTO #AddressTbl
VALUES ('State:AndhraPradesh,Dist:Prakasam')
END;
I may enter the address like 'Dist:Prakasam,State:AndhraPradesh'
and there may be some blank spaces also. So need to parse the address and check the key and values.
I will use permanent table instead of table variable.
Appreciate your help.
You can use if exists for check. Or you can add unique CONSTRAINT in your table. The UNIQUE constraint ensures that all values in a column are different. This will return error if you are trying to insert duplicate value.
Create Procedure usp_InsertAddress
(
#Address varchar(100)
)
AS
if exists(SELECT ID FROM #AddressTbl WHERE Address = #Address)
begin
select 'Value is Already Exist in Table'---For Warning
end
else
BEGIN
INSERT INTO #AddressTbl
VALUES ('State:AndhraPradesh,Dist:Prakasam')
select 'Value Inserted Sucessful'---For Success
END;

insert a field value into a table from c# Form application

I want to Insert a DateTime variable into a table by " Insert into " , I mean by using from coding not wizard.
fro example I have a table which has a some field and one of them is Time_1 and I've write a procedure which its duty is filling Information to my table and in my procedure I've used from :
Insert into my_table
values
('#name','#last_Name',#ID,#Time)
but SQL show error when I enter 12:45:00 for #Time
so please tell me how to enter times into my filed .
Thanks in advance
create procedure Insert_List
#code int, #name nvarchar(20),#lastname nvarchar(40),#time1 datetime,#time2 datetime,
#time3 datetime,#price1 int,#price2 int, #B1 int , #V1 int , #M1 int
as
begin
insert into Employee
values
(#code,#name,#lastname,#time1,#time2,#time3,#price1,#price2,#B1,#V1,#M1)
end
//and I try to execute my procedure by these information
execute Insert_List 1,'Keyvan','Fardi',10:00:00,18:15:00,19:10:00,10,10,10,10
select * from employee
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near ':'.
Insert into my_table values ('#name','#last_Name',#ID,#Time)
Since you are using a parameterized SQL statement, do not enclose the parameters in quotes. Instead:
INSERT INTO my_table VALUES (#name, #last_Name, #ID, #Time);
If you are still getting an error, post the error message and all proc code.

Display the number of rows inserted into a table from an existing table

Please how can display the number of rows inserted into an archive table using Microsoft SQL server 2012 with Stored Procedure. I have my code display below. I ran the code successful, but calling the stored procedure, I got error message says
Msg 8114, Level 16, State 5
Procedure p_moveTransHisttoArchive3a, Line 0 Error converting data type varchar to datetime.
Here is my code:
IF OBJECT_ID(N'Production.p_moveTransHisttoArchive3a', N'P') IS NOT NULL
DROP PROCEDURE Production.p_moveTransHisttoArchive3a;
GO
CREATE PROCEDURE Production.p_moveTransHisttoArchive3a
#FiveDaysAgo DATETIME
AS
SET NOCOUNT ON;
BEGIN
DECLARE #counter INT;
SET #counter = ##ROWCOUNT
INSERT INTO Production.TransactionHistoryArchive
SELECT*
FROM Production.TransactionHistory
WHERE TransactionDate <= #FiveDaysAgo
RETURN #counter --No of rows moved
END;
GO
Here is something close to what you want I think. Why is your inbound parameter named FiveDaysAgo? If you want to always get rows that are older than five days ago that belongs INSIDE the proc, not as a parameter.
CREATE PROCEDURE Production.p_moveTransHisttoArchive3a
#FiveDaysAgo DATETIME
, #RowCount int OUTPUT
AS
set nocount on;
INSERT INTO Production.TransactionHistoryArchive
(
Col1
, Col2
)
SELECT Col1
, Col2
FROM Production.TransactionHistory
WHERE TransactionDate <= #FiveDaysAgo
select #RowCount = ##ROWCOUNT

Show Actual Query Plan causes error?

Curious if anyone has experienced this problem before and what the root cause is. The problem is that an error occurs when executed in SQL 2012 with Include Actual Query Plan turned on. It works in 2008 R2 either way (with or without plan) and works in 2012 without the Plan turned on. I discovered this when testing out functionality against partitioned views.
--Setup
USE master
go
DROP DATABASE Test
GO
CREATE DATABASE Test
GO
USE Test
GO
CREATE TABLE DD (pkID int IDENTITY(1,1), FullDate date);
INSERT INTO DD (FullDate) VALUES ('2013-01-01')
INSERT INTO DD (FullDate) VALUES ('2013-01-02')
INSERT INTO DD (FullDate) VALUES ('2013-01-03')
INSERT INTO DD (FullDate) VALUES ('2013-01-04')
INSERT INTO DD (FullDate) VALUES ('2013-01-05')
GO
CREATE TABLE DC (pkID int IDENTITY(1,1), Filter varchar(32), FilterGroup varchar(32));
INSERT INTO DC (Filter, FilterGroup) VALUES ('one', 'groupone')
INSERT INTO DC (Filter, FilterGroup) VALUES ('two', 'grouptwo')
INSERT INTO DC (Filter, FilterGroup) VALUES ('three', 'groupone')
GO
CREATE TABLE FDA1 (pkID int IDENTITY(1,1), fkpID int, fkCID int, fkDateID int)
INSERT INTO FDA1(fkpID, fkCID, fkDateID) VALUES (1,1,1)
INSERT INTO FDA1(fkpID, fkCID, fkDateID) VALUES (1,2,1)
INSERT INTO FDA1(fkpID, fkCID, fkDateID) VALUES (1,1,3)
INSERT INTO FDA1(fkpID, fkCID, fkDateID) VALUES (1,3,5)
GO
CREATE TABLE FDA2 (pkID int IDENTITY(1,1), fkpID int, fkCID int, fkDateID int)
INSERT INTO FDA2(fkpID, fkCID, fkDateID) VALUES (2,1,2)
INSERT INTO FDA2(fkpID, fkCID, fkDateID) VALUES (2,2,2)
INSERT INTO FDA2(fkpID, fkCID, fkDateID) VALUES (2,1,4)
INSERT INTO FDA2(fkpID, fkCID, fkDateID) VALUES (2,3,5)
GO
CREATE VIEW FDA
AS
SELECT pkID, fkpID, fkCID, fkDateID FROM FDA1
UNION ALL
SELECT pkID, fkpID, fkCID, fkDateID FROM FDA2
GO
CREATE FUNCTION GetFilter
(
#pID int
, #filterGroup varchar(32)
)
RETURNS #Filter TABLE
(
CID int
)
AS
BEGIN
INSERT INTO
#Filter
SELECT
dc.pkID
FROM
DC dc
WHERE
dc.FilterGroup = #filterGroup
RETURN
END
GO
CREATE PROC test (#ID int)
AS
BEGIN
BEGIN TRY
DECLARE #FilterGroup varchar(32) = 'groupone'
SELECT
CAST(MIN(dd.FullDate) As datetime) as ProjectReviewStartDate
FROM
dbo.FDA fda
INNER JOIN dbo.DD dd On fda.fkDateID = dd.pkID
INNER JOIN dbo.GetFilter(#ID, #FilterGroup) ctl on fda.fkCID = ctl.CID
WHERE
fda.pkID = #ID
OPTION (RECOMPILE);
RETURN 0;
END TRY
BEGIN CATCH
--Declare variables for error information.
Declare
#ErrorMessage nvarchar(max),
#ErrorSeverity bigint,
#ErrorState int;
--Populate error information.
Select
#ErrorMessage = ERROR_MESSAGE(),
#ErrorSeverity = ERROR_SEVERITY(),
#ErrorState = ERROR_STATE();
--Re-throw error to calling method.
RAISERROR(#ErrorMessage, #ErrorSeverity, #ErrorState);
--Failure
RETURN -1;
END CATCH;
END
GO
Now that setup is done, let's run the actual code, first "Include Actual Execution Plan"
USE Test
GO
SET STATISTICS IO ON;
SET STATISTICS TIME ON;
GO
DECLARE #id int = 1
DECLARE #tbl table (dt datetime)
DECLARE #findate datetime
INSERT #tbl EXEC test #id
SELECT #findate = dt FROM #tbl
SELECT #findate
GO
You should receive the result of: 2013-01-01 00:00:00.000.
Now turn on Include Actual Execution Plan and you will receive the result of NULL and see an error (In SQL 2012):
Msg 50000, Level 16, State 10, Procedure test, Line 33
String or binary data would be truncated.
With Include Actual Execution Plan on and removing the Try/Catch block from the proc removes the error and returns the correct result. However again this works fine in 2008R2.
Any ideas?
Thanks
I don't have an answer for you yet, just more ammo that you have likely encountered a bug.
First, to prove that this doesn't involve RAISERROR specifically, changing that line to:
THROW;
Returns this error message instead:
Msg 8152, Level 16, State 10, Procedure test, Line 7
String or binary data would be truncated.
So this shows that the error is happening in the SELECT call and has something to do with the combination of TRY/CATCH and show actual plan.
Next, let's see what the metadata describes as the output for the stored procedure:
SELECT name, system_type_name
FROM sys.dm_exec_describe_first_result_set_for_object
(OBJECT_ID('test'), NULL);
Results:
name system_type_name
---------------------- ----------------
ProjectReviewStartDate datetime
Now let's see what it says about the metadata (well, actually, turns out an error message) if we try to test the metadata for the batch you're running:
SELECT [error_message]
FROM sys.dm_exec_describe_first_result_set(N'DECLARE #id int = 1
DECLARE #tbl table (dt datetime)
DECLARE #findate datetime
INSERT #tbl
EXEC test #id', -1, NULL);
The result:
error_message
----------------------------------------------------------
Incorrect syntax near '-'.
The batch could not be analyzed because of compile errors.
There is no - in the batch. And this is with execution plan turned off. And you get the same result even if you comment out the insert in the batch above. Even for example:
SELECT error_message
FROM sys.dm_exec_describe_first_result_set(N'EXEC test 1', -1, NULL);
The error message changes ever so slightly (look closely):
error_message
----------------------------------------------------------
Incorrect syntax near '1'.
The batch could not be analyzed because of compile errors.
We've removed any involvement of showplan or potential truncation and we still can demonstrate that SQL Server has some kind of problem compiling and/or generating metadata for this simple batch.
Seems to be a pretty simple repro, and you have workarounds, but I would definitely file this on Connect.
edit
I see that you have filed a bug; anyone else who can repro this situation should vote and confirm repro:
http://connect.microsoft.com/SQLServer/feedback/details/785151/show-actual-query-plan-causes-error

result set based on success or failure

I'm having a stored procedure which returns two result sets based on the success or failure.
SP success result set: name, id ,error,desc
SP failure result sret: error,desc
I'm using the following query to get the result of the stored procedure. It returns 0 for success and -1 for failure.
declare #ret int
DECLARE #tmp TABLE (
name char(70),
id int,
error char(2),
desc varchar(30)
)
insert into #tmp
EXEC #ret = sptest '100','King'
select #ret
select * from #tmp
If the SP is success the four field gets inserted into the temp table since the column matches.
But in case of failure the sp result set has only error and desc which does not matchs with no of columns in the temp table...
.I can't change the Sp, so I need to do some thing (not sure) in temp table to handle both failure and success.
You can't return 2 different recordsets and load the same temp table.
Neither can try and fill 2 different tables.
There are 2 options.
Modify your stored proc
All 4 columns are returned in all conditions
1st pair (name, ID) columns are NULL on error
2nd pair (error, desc) are NULL on success
If you are using SQL Server 2005 then use the TRY/CATCH to separate your success and fail code paths. The code below relies on using the new error handling to pass back the error result set via exception/RAISERROR.
Example:
CREATE PROC sptest
AS
DECLARE #errmsg varchar(2000)
BEGIN TRY
do stuff
SELECT col1, col2, col3, col4 FROM table etc
--do more stuff
END TRY
BEGIN CATCH
SELECT #errmsg = ERROR_MESSAGE()
RAISERROR ('Oops! %s', 16, 1, #errmsg)
END CATCH
GO
DECLARE #tmp TABLE ( name CHAR(70), id INT, error char(2), desc varchar(30)
BEGIN TRY
insert into #tmp
EXEC sptest '100','King'
select * from #tmp
END TRY
BEGIN CATCH
PRINT ERROR_MESSAGE()
END CATCH
My fault!!
Was too quick in the answer.
You need only to relv on the return value, so building up the logic against it is much better.
If you still want to use the temp table, then calling the sptest twice could be a way to deal with it (not optimal though), one time to get the return value and based on it then have 2 different temp tables you are filling up (one would be with the 4 fields, the other only with 2 fields).
declare #ret int
DECLARE #tmp TABLE (name CHAR(70), id INT, error char(2), desc varchar(30))
DECLARE #tmperror TABLE (error char(2), desc varchar(30))
EXEC #ret = sptest '100','King'
IF #ret != 0
BEGIN
INSERT INTO #tmperror
EXEC sptest '100','King';
SELECT * FROM #tmperror;
END
ELSE
BEGIN
INSERT INTO #tmp
EXEC sptest '100','King';
SELECT * FROM #tmp;
END
Keep in mind that this solution is not optimal.
Try modifying your table definition so that the first two columns are nullable:
DECLARE #tmp TABLE (
name char(70) null,
id int null,
error char(2),
desc varchar(30)
)
Hope this helps,
Bill
You cannot do this with just one call. You will have to call it once, either getting the return status and then branching depending on the status to the INSERT..EXEC command that will work for the number of columns that will be returned or Call it once, assuming success, with TRY..CATCH, and then in the Catch call it again assuming that it will fail (which is how it got to the CATCH).
Even better, would be to either re-write the stored procedure so that it returns a consistent column set or to write you own stored procedure, table-valued function or query, by extracting the code from this stored procedure and adapting it to your use. This is the proper answer in SQL.

Resources