SQL Procedure behavior while having multiple if statements - sql-server

I have stored procedure like this,
Create Procedure [dbo].[Get_Data](
#Id as Varchar(20),
#Type as Varchar(10)
)
As
Begin
IF(#Type = 'skill')
Begin
.....
select * ....
END
IF(#Type = 'agent')
Begin
.....
select * ....
END
IF(#Type = 'skillProfile')
Begin
Print 'abc'
select * ....
.....
END
END
Note: there is no syntax or any other error inside any of the if loops as every select query inside each loops are tested successfully.
So now, When i try to execute procedure using command as below,
EXEC [Get_Data] '1391520','skillProfile'
the statement print is not printed also dint get any rows in return, instead i get message Commands completed successfully.
I tried changing last(i.e. here third) if loop statement i.e. from IF(#Type = 'skillProfile') to IF(#Type = 'profile') and tried executing like EXEC [Get_Data] '1391520','profile' which worked all fine!
Got confused with this and tried changing first if loop and last(third) if loop like this, IF(#Type = 'skill') to IF(#Type = 'xyz') and IF(#Type = 'skillProfile') to IF(#Type = 'xyzProfile') and tried executing like EXEC [Get_Data] '1391520','xyzProfile' which also worked all fine.
Now i am totally confused why it doesn't work when i use skill and skillProfile.

You defined your parameter as Varchar(10), which means it cannot hold more than 10 characters. If you try to assign it a longer value, it gets truncated with no warning. So you are trying to execute your procedure with #Type='skillProfi'. The solution is to increase the size of your parameter, say varchar(100).

Use Try catch block you will let know where error is
Create Procedure [dbo].[Get_Data](
#Id as Varchar(20),
#Type as Varchar(10)
)
As
BEGIN TRY
SET NOCOUNT ON
IF(#Type = 'skill')
Begin
.....
select * ....
END
IF(#Type = 'agent')
Begin
.....
select * ....
END
IF(#Type = 'skillProfile')
Begin
Print 'abc'
select * ....
.....
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;
END CATCH

Related

How do I get an error message returned to my stored procedure?

I am calling StoredProcA from StoredProcB like this:
EXEC StoredProcA(#a,#b,#c)
StoredProcA has code similar to this:
do some stuff...
if #a < #b
insert into TableX(ItemNo, ItemDescr, ItemCost)
values(#q, #r, #c)
if ##ERROR <> 0 begin
rollback transaction
raiserror('Insert into TableX failed', 16, 1)
return
end
do more stuff...
So when I call StoredProcA from StoredProcB how do I get that error message returned to StoredProcB and assign a variable to it?
Also, I cannot change StoredProcA in any way.
I assume it would be something like this:
declare #err_msg varchar(50)
EXEC #err_msg = StoredProcA(#a,#b,#c)
update MyTable Set Result = #err_msg Where Cost = #a
but I just can't figure out the exact syntax...
You'll need to use T-SQL structured error handling to capture the error message text and reference with ERROR_MESSAGE() in the catch block:
BEGIN TRY
EXEC StoredProcA #a, #b, #c;
END TRY
BEGIN CATCH
UPDATE dbo.MyTable SET Result = ERROR_MESSAGE() WHERE Cost = #a;
END CATCH;

T SQL if column exists then (if it has value X then (

I need to perform the following logical clause:
If column is present and it has certain value then do something.
If not, then do something else.
IF EXISTS(
SELECT *
FROM sys.columns
WHERE Name = N'legacyoptions'
AND Object_ID = Object_ID(N'config '))
BEGIN
if ( select legacyoptions from config)=1
begin
Do stuff when legacy=1
end
else begin
Do stuff when legacy !=1
END
else
begin
do stuff when legacy is not present
end
However, this does not work in case legacyoptions is not present
Here is the way using TRY CATCH block and a dynamic SQL so this block of code will be compiled without table config and/or legacyoptionsfield in the database.
BEGIN TRY
DECLARE #legacyoptions int;
EXECUTE sp_executesql N'select TOP 1 #legacyoptions=legacyoptions from config',
N'#legacyoptions int OUTPUT',
#legacyoptions OUTPUT;
if #legacyoptions=1
begin
-- Do stuff when legacy=1
end
ELSE
BEGIN
-- Do stuff when legacy !=1
END
END TRY
BEGIN CATCH
-- do stuff when legacy is not present
END CATCH
try this :(i guess you are leaving an end of if)
IF EXISTS(
SELECT *
FROM sys.columns
WHERE Name = N'legacyoptions'
AND Object_ID = Object_ID(N'config '))
BEGIN
if ( select legacyoptions from config)=1
begin
Do stuff when legacy=1
end
else begin
Do stuff when legacy !=1
END
end
else
begin
do stuff when legacy is not present
end
This works, but seems stupid to me.
IF EXISTS( SELECT * FROM sys.columns WHERE Name = N'legacyoptions' AND Object_ID = Object_ID(N'config '))
BEGIN
exec('
if (select legacyoptions from config)=1
begin
print ''Config==1''
end
else
begin
print ''Config!=1''
end
')
end
else
begin
print 'no legacy'
end

Error Handle in sql server

DECLARE #id bigint=0,
#id int=0,
#name varchar(50) = '36',
#marks int = 'SDFGS'
#Op varchar(50) = 'UPSERT'
IF(#Op='UPSERT')
BEGIN
INSERT INTO tbl_student
(name, marks)
VALUES
(#name, #marks)
SELECT SCOPE_IDENTITY()
END
ELSE
BEGIN
UPDATE tbl_student SET
name = #name,
marks = #marks
WHERE id = #id
SELECT 'Success'
END
It throw error 'Conversion failed when converting the varchar value 'SDFGS' to data type int.'
I want to handle this error.
If error then it will be return 'Error' string.
You can handle this error using TRY... CATCH Block
Begin
declare #msg varchar(100)
Begin try
DECLARE #id bigint=0,#name varchar(50) = '36',#marks int = 'SDFGS',#Op varchar(50) = 'UPSERT'
IF(#Op='UPSERT')
BEGIN
INSERT INTO tbl_student
(name, marks)
VALUES
(#name, #marks)
SELECT SCOPE_IDENTITY()
END
ELSE
BEGIN
UPDATE tbl_student SET
name = #name,
marks = #marks
WHERE id = #id
SELECT 'Success'
Set #msg='Success'
END
End try
Begin catch
SELECT 'Error'
Set #msg='Error'
End catch
End
You can use TRY ... CATCH
https://msdn.microsoft.com/en-us/library/ms175976.aspx - there is a sample code here.
The error says it all, you are trying to put a string value in an int datatype and hence the error. If you want to catch this error then try to use TRY...CATCH. Something like
BEGIN TRY
-- Your code.
END TRY
BEGIN CATCH
-- Catch the exception/error here.
END CATCH;

Return Resultset from SQL Server to VB.NET application

I need to return a resultset consisting of database errors from a SQL Server stored procedure's CATCH clause but I'm stuck with it. Do I need to use cursors to return resultset and if so, then what is the type declaration for the OUTPUT parameter in my .NET application? I tried Object and Variant but did not work.
I also tried the simple way just using a SELECT statement to return and it works with one stored procedure but not with another as thus in my CATCH clause:
while (#I <= #count)
begin
BEGIN TRY
-- delete all previous rows inserted in #customerRow for previous counts #I
delete from #customerRow
-- this is inserting the current row that we want to save in database
insert into #customerRow
SELECT
[id],[firstname], [lastname], [street], [city],
[phone],[mobile],[fax], [email], [companyName],
[licence],[brn], [vat], [companyStreet], [companyCity], [status]
FROM
(SELECT
ROW_NUMBER() OVER (ORDER BY id ASC) AS rownumber,
[id], [firstname], [lastname], [street], [city],
[phone], [mobile], [fax], [email], [companyName],
[licence], [brn], [vat], [companyStreet], [companyCity], [status]
FROM
#registerDetails) AS foo
WHERE
rownumber = #I
-- this stored procedure handles the saving of the current customer row just defined above
-- if there is any error from that sproc, it will jump to CATCH block
--save the error message in the temp table and continue
--with the next customer row in the while loop.
exec dbo.sp_SaveCustomer #customerRow
END TRY
BEGIN CATCH
IF ##TranCount = 0
-- Transaction started in procedure.
-- Roll back complete transaction.
ROLLBACK TRANSACTION;
if XACT_STATE()= -1 rollback transaction
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
SELECT #ErrorMessage = ERROR_MESSAGE() + ' ' + (select firstname from #registerDetails where id=#I)
SELECT #ErrorSeverity = ERROR_SEVERITY();
SELECT #ErrorState = ERROR_STATE()
INSERT INTO #registrationResults (error,id)
SELECT #ErrorMessage, #I
END CATCH
set #I = #I +1
end
COMMIT TRANSACTION registerTran
select * from #registrationResults
The above works with one stored procedure when I call it in my vb.net code as :
ta.Fill(registrationErrors, clientDetailsDT)
where registrationErrors and clientDetailsDT are strongly typed data tables.
This one does not :
begin catch
IF ##TranCount > 0 or XACT_STATE()= -1 ROLLBACK TRANSACTION;
DECLARE #ErrorMessage NVARCHAR(4000);
DECLARE #ErrorSeverity INT;
DECLARE #ErrorState INT;
DECLARE #ErrorLine INT;
SELECT #ErrorMessage = ERROR_MESSAGE();
SELECT #ErrorSeverity = ERROR_SEVERITY();
SELECT #ErrorState = ERROR_STATE();
SELECT #ErrorLine = ERROR_Line();
****ERROR -- THIS SELECT WAS MESSING ALL UP as it was this select that was being returned to the .NET and not the select of the desired #temp table after, hence returning 0 resultset as this select was EMPTY. !!
select status_indicator from InsertInvoiceTriggerData where session_GUID = guid**
delete from InsertInvoiceTriggerData where session_GUID = #guid**
INSERT INTO #registrationResults (error,id)
SELECT #ErrorMessage, NULL
select * from #registrationResults
end catch
Any suggestions how to return resultsets?
I haven't seen your database code, but in my experience the very first error caught by catch means that the entire transaction has to be rolled back. Apart from other things, it also implies that I never have more than 1 error to return in any given situation.
As such, I use 2 scalar output parameters in my stored procedures, that is:
#Error int = null output,
#Message nvarchar(2048) = null output
And I can retrieve them just like any other output variables.
UPD: Even after you have added some code, I still fail to understand what is your problem, exactly. However, I see several problems with your code, so I'll point them out and chances are, one of them will solve the problem. I am commenting only the first snippet, since the last one is too incomplete.
You should have been started the outermost transaction somewhere before the loop. If not, the code will fail.
If I guessed correctly, you implemented all savepoint logic inside the dbo.sp_SaveCustomer stored proc. If not, the whole discussion is pointless, since there are no save tran or rollback #savepoint statements in the code you have shown.
The first catch statement - IF ##TranCount = 0 ROLLBACK TRANSACTION is all wrong. If the condition is successful, it will result in error trying to rollback nonexistent transaction. Should not be here if you rely on savepoints.
The next after it should result in unconditional break:
if XACT_STATE()= -1 begin
rollback transaction;
break;
end;
The rest of your catch code can be replaced with this:
INSERT INTO #registrationResults (error, id)
SELECT error_message() + ' ' + firstname, id
from #registerDetails where id=#I;
Also, never use temp tables for this purpose, because rollback will affect them as well. Always use table variables for this, they are non-transactional (just like any other variable).
The commit should be conditional, because you may end up at this point with no transaction to commit:
if ##trancount > 0 commit tran;
There is no point in specifying savepoint name in the commit statement, it only leads to confusion (though isn't considered an error). Also, there should not any savepoint in this module (unless you have defined it before the loop).
I suspect that's just the tip of the iceberg, since I have no idea what actually happens inside the dbo.SaveCustomer stored procedure.
UPD2: Here is a sample of my VB.NET code which I use to receive recordsets from stored procedures:
Private Function SearchObjectsBase( _
SearchMode As penum_SEARCH_MODE, SearchCriteria As String
) As DataSet
Dim Cmd As DbCommand, Pr As DbParameter, dda As DbDataAdapter
' Initialise returning dataset object
SearchObjectsBase = New DataSet()
Cmd = MyBase.CreateCommand(String.Format("dbo.{0}", SearchMode))
With Cmd
' Parameter definitions
Pr = .CreateParameter()
With Pr
.ParameterName = "#SearchCriteria"
.DbType = DbType.Xml
.Value = SearchCriteria
End With
.Parameters.Add(Pr)
' Create data adapter to use its Fill() method
dda = DbProviderFactories.GetFactory(.Connection).CreateDataAdapter()
' Assign the prepared DbCommand as a select method for the adapter
dda.SelectCommand = Cmd
' A single resultset is expected here
dda.Fill(SearchObjectsBase)
End With
' Set error vars and get rid of it
Call MyBase.SetErrorOutput(Cmd)
' Check for errors and, if any, discard the dataset
If MyBase.ErrorNumber <> 0 Then SearchObjectsBase.Clear()
End Function
I use .NET 4.5, which has a very nice method to automatically select the most appropriate data adapter based on the actual connection.
And here is a call of this function:
Dim XDoc As New XElement("Criteria"), DS As DataSet = Nothing, DT As DataTable
...
DS = .SearchPatients(XDoc.ToString(SaveOptions.None))
' Assign datasource to a grid
Me.dgr_Search.DataSource = DS.Tables.Item(0)
Here, SearchPatients() is a wrapper on top of the SearchObjectsBase().

Cannot insert data with stord procedure. (0 row(s) affected) There is some error

CREATE PROCEDURE sp_BOOKEDVEHICLES
#flag bit output,-- return 0 for fail,1 for success
#REGISTRATIONID varchar(30),
#VEHICLEID varchar(9),
#VEHICLENAME varchar(20)
AS
BEGIN
BEGIN TRANSACTION
BEGIN TRY
Insert into BST_TVL_BOOKED_VEHICLES_TB(REGISTRATIONID, VEHICLEID, VEHICLENAME) Values(#REGISTRATIONID,#VEHICLEID,#VEHICLENAME)
set #flag=1;
IF ##TRANCOUNT > 0
BEGIN commit TRANSACTION;
END
END TRY
BEGIN CATCH
IF ##TRANCOUNT > 0
BEGIN rollback TRANSACTION;
END
set #flag=0;
END CATCH
END
GO
Declare #flag bit
EXEC sp_BOOKEDVEHICLES #flag output,
#REGISTRATIONID = '2364',
#VEHICLEID = '27',
#VEHICLENAME = 'Ford'
if #flag=1
print 'Successfully inserted'
else
print 'There is some error'
Obviously, there is an error in the code, but i can't find it... Cannot insert data with stord procedure.
It shows me this error : (0 row(s) affected) There is some error.
Insert into BST_TVL_BOOKED_VEHICLES_TB(REGISTRATIONID, VEHICLEID, VEHICLENAME) Values(#REGISTRATIONID,#VEHICLEID,#VEHICLENAME)
set #flag=1;
IF ##TRANCOUNT > 0
BEGIN commit TRANSACTION;
END
This must throw an SQL Exception, because #flag <> 1. Try print ERROR_MESSAGE() in order to get description of the actual error ?
Okay, you are mixing up some concepts.
1 - Transactions are used when you want multiple statements to either commit or roll back.
For instance, debit from customer account and credit vendor. Either both commit or rollback.
2 - Since you do not have that condition, just wrap the insert with a TRY/CATCH.
See my blog on ways to error handle.
http://craftydba.com/?tag=error-handling
3 - Procedures can return a value. Why not zero if good and one if bad.
I rewrote your code as below.
--
-- Re-write of code
--
CREATE PROCEDURE sp_BOOKEDVEHICLES
#REGISTRATIONID varchar(30),
#VEHICLEID varchar(9),
#VEHICLENAME varchar(20)
AS
BEGIN
-- TRY TO ADD A RECORD
BEGIN TRY
INSERT INTO BST_TVL_BOOKED_VEHICLES_TB
(REGISTRATIONID, VEHICLEID, VEHICLENAME)
VALUES
(#REGISTRATIONID, #VEHICLEID, #VEHICLENAME);
RETURN 0;
END TRY
-- CATCH ERROR
BEGIN CATCH
SELECT
ERROR_NUMBER() AS ErrorNumber
,ERROR_SEVERITY() AS ErrorSeverity
,ERROR_STATE() AS ErrorState
,ERROR_PROCEDURE() AS ErrorProcedure
,ERROR_LINE() AS ErrorLine
,ERROR_MESSAGE() AS ErrorMessage;
RETURN 1;
END CATCH
END
GO
-- Sample call
DECLARE #RETCODE INT
EXEC #RETCODE = sp_BOOKEDVEHICLES
#REGISTRATIONID = '2364',
#VEHICLEID = '27',
#VEHICLENAME = 'Ford';
-- Just show the result.
IF #RETCODE = 1
PRINT 'BAD INSERT CALL'
ELSE
PRINT 'GOOD INSERT CALL';
Please test the code since I do not have your table defined ...

Resources