Error Handling in Sybase - sql

Is there a way to handle errors in SYBASE, such as the TRY-CATCH block you can use in MS SQL Server, Oracle, etc?
I've searched the web and the only option I found was the global variable ##error, but it didn' work as I expected, for example, the following code:
begin tran
update table1
set name = 'new name'
where name = 'old name'
update table2
set id = 1
where id = 30
-- suppose id has a unique constraint and there's already a row with id = 1
IF ##error = 0
begin
print 'commited'
commit
end
else
begin
print 'rolled back'
rollback
end
The will indeed rollback somehow, because the name I've changed on table1 keeps the old value as I've tested here, but it doesn't print the messages, or execute any instructions I put after the instructions that causes the error
Can anyone help me in this? Do you know how does Sybase error handling actually works?

1st solution.
You can't catch an exception this way on Sybase. Before you update you have to check data:
if not exists
(
select 1 from table2
where id = 1
)
begin
update table2
set id = 1
where id = 30
end
else
begin
print 'rolled back'
rollback
end
2nd solution.
You can also put an update command to procedure, then you can catch an exception.
Create procedure:
create procedure myproc
as
begin
update table2
set id = 1
where id = 30
end
and run it as below:
begin tran
update table1
set name = 'new name'
where name = 'old name'
exec myproc
IF ##error = 0
begin
print 'commited'
commit
end
else
begin
print 'rolled back'
rollback
end

Related

A lot of queries get suspended. What should I do?

I'm handling an application used by around 2000 people. Everyday users insert around 300 rows/user at the same time (around 7 am to 11 am). I handle it using stored procedures that contain only insert statements, but I use begin tran to prevent the primary key getting duplicate.
Currently suspended transactions frequently happen, so my stored procedure takes around 1-2minutes to be done and this causes our users to wait for a long time to insert every data.
I already checked:
Disk speed normal around read : 600mb/s write 744mb/s.
Procesor usage between 20 - 40 % with 10 cores.
Memory usage only 6gb, I used 12gb of memory.
Check from sys.dm_exec_requests,sp_who2, and sys.dm_os_waiting_tasks.
Result from number 3 is I found that my stored procedure suspends each other (same stored procedures different executor)
This is my stored procedures (Sorry, for the naming, because it is confidential for my company):
ALTER PROC [dbo].[SP_DESTIONATION_TABLE_INSERT]
{params}
WITH RECOMPILE
AS
BEGIN
BEGIN TRAN InsertT
DECLARE #ERRORNMBR INT
SET #ERRORNMBR = 0
IF #T = ''
BEGIN
-------------------------------------------------
DECLARE #TCID VARCHAR(15)
SELECT #TCID = ID
FROM DESTIONATION_TABLE
WHERE NIK = #NIK AND
CUSTOMERID = #CUSTOMERID AND
CUSTOMERTYPE = #CUSTOMERTYPE AND --edit NvA 20180111
DATEDIFF(day,DATE,#DATE) = 0
--IF THERE IS ALREADY A CALL IN SERVER
IF #TCID IS NOT NULL
BEGIN
IF #INTERFACE <> 'WEB' BEGIN
--GET EXISTING CALL ID
SET #ID = #TCID
BEGIN TRAN UBAH
UPDATE DESTIONATION_TABLE
SET
columns=value
WHERE ID = #ID
AND employeeid = #employeeid
AND CUSTOMERID = #CUSTOMERID
SET #ERRORNMBR = #ERRORNMBR + ##ERROR
IF #ERRORNMBR = 0
BEGIN
COMMIT TRAN UBAH
SELECT
columns
FROM DESTIONATION_TABLE WHERE ID = #ID
END
ELSE
BEGIN
ROLLBACK TRAN UBAH
END
END
COMMIT TRAN InsertT
RETURN
END
--------------------------------------------------
-- CHECK #DEVICECONTROLID
IF #DEVICECONTROLID IS NOT NULL
AND #INTERFACE <> 'WEB'
AND EXISTS(SELECT 1 FROM DESTIONATION_TABLE WHERE DEVICECONTROLID = #DEVICECONTROLID)
BEGIN
IF NOT EXISTS(SELECT 1 FROM DESTIONATION_TABLE_TEMP WHERE DEVICECONTROLID = #DEVICECONTROLID)
BEGIN
INSERT INTO DESTIONATION_TABLE_TEMP
(COLUMNS)
VALUES
(VALUES)
END
SELECT * FROM DESTIONATION_TABLE WHERE _DEVICECONTROLID = #_DEVICECONTROLID
END
ELSE
BEGIN
some logic to make primary key formula{string+date+employeeid+increment}
END
END
ELSE
BEGIN
BEGIN TRAN UBAH
IF #PARAMS = 'WEB'
BEGIN
UPDATE DESTIONATION_TABLE
SET
COLUMNS = PARAMS
WHERE ID = #ID
END
ELSE IF PARAMS = 'MOBILE'
BEGIN
UPDATE DESTIONATION_TABLE
SET
COLUMNS = PARAMS
WHERE ID = ID
END
SET #ERRORNMBR = #ERRORNMBR + ##ERROR
IF #ERRORNMBR = 0
BEGIN
COMMIT TRAN UBAH
SELECT
COLUMNS
FROM DESTIONATION_TABLE WHERE ID = ID
END
ELSE
BEGIN
ROLLBACK TRAN UBAH
END
END
COMMIT TRAN InsertT
END
I need a suggestion, what should I check next to get what's wrong with my server is.
Is begin tran is the issue here?
I'm not an expert but a googling of "BEGIN TRAN" seems to show that it "locks a table until the transaction is committed with a "COMMIT TRAN" ... meaning that it is a blocking process. So if you're table is locked when all these inserts are attempting to execute, some of them would obviously not be successful.
https://www.mssqltips.com/sqlservertutorial/3305/what-does-begin-tran-rollback-tran-and-commit-tran-mean/
I would suggest building a work queue on its own thread that listens for INSERTS/UPDATES. Then the queue can empty into the DB at it's leisure.

TSQL error checking code not working

I'm trying to add an alert to my log for a running insert of a view into a table when there is an error executing the query in the view. When I run the view alone, I get an invalid input into SUBSTRING (the exact wording of the error I can't remember). When I run it as part of my view -> table stored procedure, the error is ignored, then I have to go digging for the offending line and make an exception in the view's code to omit that line from the results (I know, it sounds kludge-y, but I'm doing data reduction on huge web-log files from a specialized webapp), but I digress.
I've tried two different methods for trying to catch the error and neither are triggered in such a way to insert the row indicating an error in my execution result table (refresh_results). I think I may be missing some fundamental - perhaps the errors are being encapsulated in come way. If I can't detect the error, the only way to notice an error is if someone notices the number of entries into the table is low for a given period of time.
SELECT #TransactionName = 'tname';
BEGIN TRANSACTION #TransactionName;
BEGIN TRY
print 'tname ***In Try***';
if exists (select name from sysobjects where name='tablename')
begin
drop table tablename;
end
select * into tablename
from opendatasource('SQLNCLI', 'Data Source=DATABASE;UID=####;password=####').dbo.viewname;
COMMIT TRANSACTION #TransactionName;
END TRY
BEGIN CATCH
print 'tablename ***ERROR - check for SUBSTRING***';
begin transaction
set #result_table = 'tablename ***ERROR - check for SUBSTRING***'
select #result_time = getdate(),
#result_rows = count(logtime)
from tablename
insert INTO [dbo].[refresh_results] (result_time, result_table, result_rows)
values (#result_time, #result_table, #result_rows);
commit transaction
ROLLBACK TRANSACTION #TransactionName;
END CATCH
or
if exists (select name from sysobjects where name='tablename')
begin
drop table tablename;
end
select * into tablename
from opendatasource('SQLNCLI', 'Data Source=DATABASE;UID=####;password=####').dbo.viewname;
print '##error'
print ##error
if ##error <> 0
Begin
print 'tablename ***ERROR - check for SUBSTRING***';
set #result_table = 'tablename ***ERROR - check for SUBSTRING***'
select #result_time = getdate(),
#result_rows = count(logtime)
from tablename
insert INTO [dbo].[refresh_results] (result_time, result_table, result_rows)
values (#result_time, #result_table, #result_rows);
End
Your nested transactions aren't doing what you think. You are rolling back the error you thought you stored. Roll back the initial transaction and then, if you feel the need, start a new transaction for logging the error.
See here.
You have two seperate problems
In your first example you are running transactions that do the following:
BEGIN TRAN
...error...
BEGIN TRAN
...log error...
COMMIT TRAN
ROLLBACK TRAN
The inner transaction is rolled back with the outer transaction. Maybe try:
BEGIN TRAN
...error...
ROLLBACK TRAN
BEGIN TRAN
...log error...
ROLLBACK TRAN
The second example you are using ##ERROR. As I understand it as soon as you run something ##ERROR is replaced. That something I think includes the print statement.
If you change it to something like:
DECLARE #Error INT
select * into tablename
from opendatasource('SQLNCLI', 'Data Source=DATA3;UID=;password=').dbo.viewname;
SET #Error = ##ERROR
print '##error'
print #Error
if #Error <> 0
...log the error
The advantage of the TRY CATCH is that if you have an error it will catch it. The ##ERROR method works 100% but it only works on the last line run. so if you have an error with DROP TABLE tablename ##ERROR won't get it (unless you add another check)
Ok, so I had to use a helper procedure to add a log entry. I think what was going on is that the rollback was also rolling back the log entry.
This is what I had to do:
DECLARE #myError tinyint;
BEGIN TRY
BEGIN TRANSACTION;
if exists (select name from sys.sysobjects where name='table_name')
begin
drop table table_name
end
select * into table_name
from opendatasource('SQLNCLI', 'Data Source=###;UID=###;password=###').view_Table
COMMIT TRANSACTION;
END TRY
BEGIN CATCH
set #myError = 1
ROLLBACK TRANSACTION;
END CATCH
if #myError <> 0
begin
exec dbo.table error
end
ELSE
EXEC exec dbo.table normal row

With(XLock,RowLock) does not lock row exclusively

I have a table that has a column named "Is_Locked".
I open 2 SSMS and in every one create a new Query with this script:
BEGIN TRAN Nima1
BEGIN TRY
DECLARE #a INT
SELECT #a=COUNT(*)
FROM dbo.Siahe WITH(XLOCK,ROWLOCK)
WHERE TedadDaryaii=8
AND Is_Locked=1
IF #a = 0
BEGIN
UPDATE Siahe
SET Is_Locked = 1
WHERE ShMarja = 9999
END
COMMIT TRAN Nima1
END TRY
BEGIN CATCH
ROLLBACK TRAN Nima1
END CATCH
but if all Is_Lock field Is false then both query execute and Select Statement does not lock the rows exclusively.
Why?
If #a = 0 then there were 0 matching rows from your first query. All 0 of those rows are exclusively locked. I'm a bit confused by your different where conditions in your select and update statements. If the same where conditions were used in both, I'd suggest something like:
UPDATE Siahe
SET Is_Locked = 1
WHERE
Is_Locked = 0 and
/* Other Conditions */
IF ##ROWCOUNT = 1
BEGIN
PRINT 'We got the lock'
END
ELSE
BEGIN
PRINT 'Someone else has the lock'
END

SQL Server optimistic locking - Returning changed timestamp value

I have an update stored procedure that implements optimistic locking. The stored procedure looks like this:
ALTER PROCEDURE [dbo].[usp_Test]
#Id AS char(2),
#recordTimestamp as timestamp
...
BEGIN
UPDATE XY
..
WHERE ((Id = #Id) AND (recordTimeStamp = #recordTimestamp))
if ##rowcount = 0
begin
RAISERROR ('this row was changed by another user', 18, 1)
end
SELECT timeStamp from XY where Id = #Idend
Is there a simpler way to return the new timestamp? I would really like to avoid the SELECT statement.
Assuming at least SQL Server 2005 you can use OUTPUT
UPDATE XY
SET Col = 'foo'
OUTPUT inserted.recordTimeStamp
WHERE ((Id = #Id) AND (recordTimeStamp = #recordTimestamp))
Or a version that uses a table variable to more closely mirror the behaviour of the original query.
DECLARE #Timestamp TABLE(stamp binary(8))
UPDATE XY
SET col='foo'
OUTPUT inserted.recordTimeStamp INTO #Timestamp
WHERE (Id = #Id) AND (recordTimeStamp = #recordTimestamp)
if ##rowcount = 0
begin
RAISERROR ('this row was changed by another user', 18, 1)
end
SELECT stamp
FROM #Timestamp
Obviously I was blind. ##DBTS(http://msdn.microsoft.com/en-us/library/ms187366(SQL.90).aspx) command is the right way to go.
...
if ##rowcount = 0
begin
RAISERROR ('this row was changed by another user', 18, 1)
end
SELECT ##DBTS

Timeout in SQL Procedure

I am using the below sql to import some data from a file from the intranet. However every once a while, there will be a timeout error and the proc would fail, which is why I am using a transaction. If the transaction fails, I want the ImportedTable to get cleared. However this does not seem to happen. Is there anything I am missing here?
ALTER PROCEDURE [dbo].[pr_ImportData]
#StoreCode varchar(10),
#UserId varchar(100)
AS
BEGIN TRANSACTION
-- 1) Clear the data
exec pr_INTRANET_ClearData #StoreCode, #UserId
IF ##ERROR <> 0
BEGIN
ROLLBACK TRANSACTION
GOTO EXIT1
END
-- 2) Add the new data to the history Table
INSERT INTO data_History (...)
SELECT ... from ImportedTable WHERE StoreCode = #StoreCode and UserId = #UserId
IF ##ERROR <> 0
BEGIN
ROLLBACK TRANSACTION
GOTO EXIT1
END
-- 3) Add the data to the live table
INSERT INTO data_Live (...)
SELECT ... from ImportedTable WHERE StoreCode = #StoreCode and UserId = #UserId
IF ##ERROR <> 0
BEGIN
ROLLBACK TRANSACTION
GOTO EXIT1
END
IF ##ERROR <> 0
BEGIN
ROLLBACK TRANSACTION
GOTO EXIT1
END
EXIT1:
-- 4) Delete the rows from the temp table
DELETE FROM ImportedTable WHERE StoreCode = #StoreCode and UserId = #UserId
COMMIT TRANSACTION
Update 1: I am running this against SQL 2000 and SQL2005.
Update 2: To clarify: The ImportedTable never gets cleared at Exit1.
SET XACT_ABORT ON will make any error to rollback the transaction, removing the need to explicitly rollback in case of error. You should also consider using BEGIN TRY/BEGIN CATCH, as is significantly easier to program than checking for ##ERROR after every statement.