0

I am working on a Transaction using Repeatable Read Isolation Level. I want to incorporate both Try-Catch and an Error-handler features in this Transaction. When I run the code, I get an error message that : Msg 102, Level 15, State 1, Line 18 Incorrect syntax near 'BEGIN'. Msg 102, Level 15, State 1, Line 23 Incorrect syntax near '@errnum'.

How do I complete this Transaction successfully? OR What is the correct way to write this Transaction?

This is my work as at now:

CREATE PROCEDURE ItemFlow (@Name  VARCHAR(50),
                                @aPrice  MONEY,
                                @bPrice MONEY)
AS
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
GO
  BEGIN TRAN
      IF EXISTS (SELECT 1
                 FROM   Cashier
                 WHERE  Item = '@Item')
        UPDATE Cashier
        SET    bPrice = '@bPrice',
               aprice = '@aprice'
        WHERE  Item = '@Item'
      ELSE
        INSERT INTO Cashier
                    (Item, aPrice, bPrice)
        VALUES      ('@Item', '@aPrice', '@bPrice')
                     END
BEGIN TRY
    EXECUTE ItemFlow
END TRY

BEGIN CATCH
    @errnum = ERROR_NUMBER(),
           @severity = ERROR_SEVERITY(),
           @errstate = ERROR_STATE(),
           @proc = ERROR_PROCEDURE(),
           @line = ERROR_LINE(),
           @message = ERROR_MESSAGE()

END CATCH
Exceli
  • 47
  • 1
  • 6
  • Just move the error handling into the client. Why do it in T-SQL the hard way? It's so easy in C#. – usr Jan 10 '15 at 18:28
  • @usr No, when transactions are being used then there should definitely be a TRY / CATCH block to properly handle the rollback. – Solomon Rutzky Jan 10 '15 at 18:38
  • @srutzky manage transactions on the client as well. a `using(conn.BeginTran())` is all it takes. T-SQL is horrible for tran management as well. – usr Jan 10 '15 at 18:52
  • 1
    @usr Still no ;-). I would only do app-level transactions when absolutely required. The transactions are in the DB and should stay there. Also, quite often procs are called by app code AND other procs and/or SQL Agent Jobs. Some procs aren't ever called by the app. – Solomon Rutzky Jan 10 '15 at 19:15
  • 1
    @Exceli Unfortunately most of the answers here are not going to help you. One was worthless (where you took most of your changes from) and has been deleted. Another is both worthless and dangerous and should be deleted (but that ain't gonna happen). Another is a little helpful, but only addresses maybe half of the problems. Please delete the code that you currently have and see my answer for the correct code. – Solomon Rutzky Jan 10 '15 at 20:42
  • @Exceli Also, it is clear from your original code here and on http://stackoverflow.com/questions/27874184/sql-server-create-and-call-stored-procedure that you have never done this before. Having random people correct your code is not going to truly help you, especially when getting into more advanced concepts. If you do not know how to use a variable then you need to start learning the basics of T-SQL, including the structure of stored procedures, how transactions work, etc. Check out this answer: http://stackoverflow.com/questions/2664839/recommended-sql-practice-and-books/2678873#2678873 – Solomon Rutzky Jan 10 '15 at 21:08
  • Thanks for the suggestion. All the books are obsolete relative to my cause. SQL2012 or newer is what I am dealing with. Perhaps these books have newer versions. I am new to this thus my questions. Or else I wont be on this site – Exceli Jan 11 '15 at 09:21
  • @Exceli I understand being new and needing help. That is why I am offering suggestions in addition to providing the correct answer (unlike the others who are providing neither). My point was that you are jumping too far ahead too quickly and need to get the basics down first. There is a newer version of [SQL Server 2012 T-SQL Fundamentals](http://www.amazon.com/gp/product/0735658145/) but the other 2 are the most recent, and hence probably still relevant. This just came out [Beginning SQL Server for Developers](http://www.amazon.com/Beginning-Server-Developers-Robin-Dewson/dp/1484202813/) . – Solomon Rutzky Jan 11 '15 at 15:39
  • Also, if you want someone to be notified of a comment that you are posting, you should include their name at the beginning, preceded with an `@`, but only if they did not post the question or answer that you are commenting on. I did not put your name in the front this time but you should still get the notification since the comment is on the question that you posted. But I was not notified of your comments because they were on the question, which I did not create, and you didn't put my name at the beginning. Does that make sense? – Solomon Rutzky Jan 11 '15 at 15:42

3 Answers3

1

One problem is the GO that terminates the create procedure statement. I always use begin/end with stored procedures:

CREATE PROCEDURE ItemFlow (@Name  VARCHAR(50),
                           @aPrice  MONEY,
                           @bPrice MONEY)
AS
BEGIN
    SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;
    BEGIN TRAN
      IF EXISTS (SELECT 1
                 FROM   Cashier
                 WHERE  Item = '@Item')
        UPDATE Cashier
        SET    bPrice = '@bPrice',
               aprice = '@aprice'
        WHERE  Item = '@Item'
      ELSE
        INSERT INTO Cashier
                    (Item, aPrice, bPrice)
        VALUES      ('@Item', '@aPrice', '@bPrice')
    COMMIT TRAN;
END;  -- ItemFlow

Then, you need arguments when you call the stored procedure, or default values defined for them:

BEGIN TRY
    EXECUTE ItemFlow @arg1, @arg2, @arg3
END TRY
BEGIN CATCH
    @errnum = ERROR_NUMBER(),
           @severity = ERROR_SEVERITY(),
           @errstate = ERROR_STATE(),
           @proc = ERROR_PROCEDURE(),
           @line = ERROR_LINE(),
           @message = ERROR_MESSAGE()

END CATCH;
Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786
  • THANKS for the insight Gordon Linoff. I'll make the changes and run... then post if it works or not. I appreciate it – Exceli Jan 10 '15 at 17:51
  • 1
    @Exceli this is a fairly bad answer and the code is useless. It would be a horrible mistake to start a transaction in the proc and not have the TRY / CATCH logic to capture an error so that a ROLLBACK can be done. It makes no sense to have the TRY/CATCH outside of the proc. Some errors will abort the proc and not proceed to the COMMIT, leaving an orphaned transaction. The variables in the CATCH block aren't even declared, let alone used. The input params can't be used as literals and be of any use. – Solomon Rutzky Jan 10 '15 at 18:26
1

Before implementing any of the suggested changes, please answer: 1) why you are calling the proc itself within the proc? and 2) why are you setting the ERROR_ functions into variables? Are you going to use them? If not, no need to declare variables.

Also:

  • Obviously the GO can't be a part of this. It is just a batch separator for SSMS.

  • You have an input param called @Name that is not used, and a variable called @Item that is not declared. I suspect they are the same thing so I have used @Item as the input param instead of @Name.

  • You use the input params as string literals in the three queries, which makes no sense. You need to remove the single-quotes from around them so that they can act as variables.

  • And, please do NOT separate the TRY / CATCH logic from the transaction!

Assuming that there was no real intention in calling an infinite loop (the proc calling itself with no condition to ever stop), your code should look as follows:

CREATE PROCEDURE ItemFlow
(
  @Item  VARCHAR(50),
  @aPrice  MONEY,
  @bPrice MONEY
)
AS
SET NOCOUNT ON;

SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

BEGIN TRY

  BEGIN TRAN

  IF EXISTS (SELECT 1
             FROM   Cashier
             WHERE  Item = @Item)
  BEGIN
        UPDATE Cashier
        SET    bPrice = @bPrice,
               aprice = @aPrice
        WHERE  Item = @Item;
  END;
  ELSE
  BEGIN
        INSERT INTO Cashier
                    (Item, aPrice, bPrice)
        VALUES      (@Item, @aPrice, @bPrice);
  END;

  COMMIT TRAN;

END TRY
BEGIN CATCH

  IF (@@TRANCOUNT > 0)
  BEGIN
    ROLLBACK TRAN;
  END;

  THROW;

END CATCH;

THROW was introduced in SQL Server 2012. If you are using anything from 2005 - 2008 R2, replace the THROW with:

DECLARE @ErrMessage NVARCHAR(4000);
SET @ErrMessage = ERROR_MESSAGE();
RAISERROR(@ErrMessage, 16, 1);
RETURN;
Solomon Rutzky
  • 46,688
  • 9
  • 128
  • 171
0

Your code has a few problems:

Error near BEGIN

GO is a batch separator. It's not valid T-SQL and only understood by SSMS. You are effectively submitting two queries:

CREATE PROCEDURE ItemFlow (@Name  VARCHAR(50),
                                @aPrice  MONEY,
                                @bPrice MONEY)
AS
SET TRANSACTION ISOLATION LEVEL REPEATABLE READ;

and:

BEGIN TRAN
...

As you can see, the stored procedure body is empty. Remove GO to get rid of this.

Error near @errnum

Your variables are not declared. Also, you must use SELECT or SET to assign a value to these variables:

DECLARE @errnum int,
        @serverity int,
        (etc.)
        
SELECT @errnum = ERROR_NUMBER(),
       @severity = ERROR_SEVERITY(),
       @errstate = ERROR_STATE(),
       @proc = ERROR_PROCEDURE(),
       @line = ERROR_LINE(),
       @message = ERROR_MESSAGE()

One last thing:

You have a BEGIN TRAN but not a COMMIT TRAN. Your transaction is still open by the end of the sproc's execution.

Community
  • 1
  • 1
Code Different
  • 90,614
  • 16
  • 144
  • 163
  • Thank YOU Zoff Dino. I did not realize that I have to declare variables in the "Error-Handler" – Exceli Jan 10 '15 at 18:03