UPDATE/SET/OUTPUT/FROM with output into variable - tsql

Consider the following stored procedure
CREATE PROCEDURE AssignCodeToCustomer (#customerId int)
AS
BEGIN
DECLARE #code NVARCHAR(255)
BEGIN TRY
BEGIN TRANSACTION
SELECT #code = (
UPDATE
Codes
SET
CustomerId = #customerId
OUTPUT
INSERTED.Code
FROM (
SELECT TOP 1
Code
FROM
Codes
) AS c
WHERE
c.Code = Codes.Code
-- Other stuff
COMMIT TRANSACTION
END TRY
BEGIN CATCH
BEGIN
ROLLBACK TRANSACTION
EXEC spLogSQLError
END
END CATCH
END
GO
I get an error 'Incorrect syntax near the keyword UPDATE' on line 10 (which holds the keyword UPDATE). I could also first select a code and then assign it, but with concurrency in mind I want only one query. The query works if I don't try to set the output value into the variable. How can I fix this error or should I use another approach?

Related

Handling error with exception clause in postgreSQL

I wrote this piece of code in PL/PGSQL.
WHILE nbStatus1 < nbStatus0 AND StatusExec = 1 LOOP
BEGIN
SELECT Id, query INTO idQuery, Selectquery FROM temp_table_test WHERE Status = 0 LIMIT 1;
EXECUTE Selectquery;
UPDATE temp_table_test
SET Status = 1
WHERE Id = idQuery;
nbStatus1 := nbStatus1 + 1;
EXCEPTION
WHEN others THEN
UPDATE LOGS_TEST_DETAILS
SET ENDDATE = NOW(),
ERRORMESSAGE = SQLERRM,
ERRORCODE = SQLSTATE
WHERE TRAITEMENTID = lastTraitementId AND QUERYID = idQuery;
StatusExec := 0;
ROLLBACK;
END;
COMMIT;
END LOOP;
I use EXECUTE to CALL a list of procedure, one after the other in the while loop.
Each CALL is stored in the variable Selectquery.
It works fine but...
I'm trying to implement a kind of try catch in case i made a mistake in one of the procedure i call in the loop.
So i did a mistake (on purpose) inside one of the procedure i call. But the ROLLBACK, remove all the changes i did, even before the while loop. it kinda propagates to all my code
Is there a way to ROLLBACK only the changes that occured within the EXECUTE command and then exit the loop ?
There is something to deal with savepoint i guess, but i can't sort it out
Thanks for your help

Firebird run execute statement inside trigger

I wrote a procedure STRING_SESTAVLJEN_ENAKOST_TABEL('MERILA_STRANKE') that generates part of code that i want to execute (some long if statement)
IF ((new.LOKACIJA IS DISTINCT FROM old.LOKACIJA )
OR (new.MODIFIED IS DISTINCT FROM old.MODIFIED )
OR (new.KARAKTERISTIKE IS DISTINCT FROM old.KARAKTERISTIKE )
OR (new.LETNIK IS DISTINCT FROM old.LETNIK )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS ))
and I want to call it in trigger, then add some code and run it all together.
The code is:
SET TERM ^ ;
ALTER TRIGGER BI_MERILA_STRANKE ACTIVE
BEFORE INSERT OR UPDATE POSITION 0
AS
declare variable besedilo_primerjave varchar(5000);
BEGIN
begin
if (new.ID_MERILA_STRANKE is null OR new.ID_MERILA_STRANKE = 0) then new.ID_MERILA_STRANKE = gen_id(GEN_ID_MERILA_STRANKE,1);
end
begin
execute procedure STRING_SESTAVLJEN_ENAKOST_TABEL('MERILA_STRANKE')
returning_values :besedilo_primerjave;
execute statement besedilo_primerjave || ' THEN BEGIN INSERT INTO SYNC_INFO(TABLE_NAME,ID_COLUMN_NAME,ID_VALUE,DATETIME)
VALUES (
''MERILA_STRANKE'',
''ID_MERILA_STRANKE'',
NEW.ID_MERILA_STRANKE,
CURRENT_TIMESTAMP
);
END ELSE BEGIN
exception ENAK_RECORD;
END';
end
END^
SET TERM ; ^
Now when I run the update and trigger triggers I get this error:
SQL Message : -104 Invalid token
Engine Code : 335544569 Engine Message : Dynamic SQL Error SQL
error code = -104 Token unknown - line 1, column 1 IF
On the other hand if I write it like this:
SET TERM ^ ;
ALTER TRIGGER BI_MERILA_STRANKE ACTIVE
BEFORE INSERT OR UPDATE POSITION 0
AS
BEGIN
begin
if (new.ID_MERILA_STRANKE is null OR new.ID_MERILA_STRANKE = 0) then new.ID_MERILA_STRANKE = gen_id(GEN_ID_MERILA_STRANKE,1);
end
begin
IF ((new.LOKACIJA IS DISTINCT FROM old.LOKACIJA )
OR (new.MODIFIED IS DISTINCT FROM old.MODIFIED )
OR (new.KARAKTERISTIKE IS DISTINCT FROM old.KARAKTERISTIKE )
OR (new.LETNIK IS DISTINCT FROM old.LETNIK )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS )
OR (new.ID_PNS_CERT_POS IS DISTINCT FROM old.ID_PNS_CERT_POS ))
THEN BEGIN
INSERT INTO SYNC_INFO(TABLE_NAME,ID_COLUMN_NAME,ID_VALUE,DATETIME)
VALUES (
'MERILA_STRANKE',
'ID_MERILA_STRANKE',
NEW.ID_MERILA_STRANKE,
CURRENT_TIMESTAMP
);
END ELSE BEGIN
exception ENAK_RECORD;
END
end
END^
SET TERM ; ^
It works as it should. I do not understand why it doesn't run if is more or less the same code.
As I also mentioned in your previous question, execute statement cannot be used to execute snippets of PSQL (procedural SQL) like that, it can only execute normal DSQL (dynamic SQL). And as it doesn't understand PSQL, you get the "token unknown - if" error, because if is not valid in DSQL.
execute statement is equivalent to executing SQL yourself from a query tool or application (it uses the same API), you can't use if there either.
There is a loophole by using an execute block statement, but that still would not allow you to gain access to the NEW (or OLD) trigger context variables unless explicitly passed as parameters, which would negate most of the usefulness of dynamically generated code in this context.
The only real solution is to write the trigger and not do it dynamically, maybe using a code generator (I'm not sure if any exist, otherwise you need to write that yourself).

What does a try catch change in a while statement that uses ##ROWCOUNT?

I want to add a try catch to a while loop. The loop used while on ##ROWCOUNT > 0. In the while I have an update top (100) statement that works well without a try catch around it. When I add the try, the while ends in the first loop. What impact does the try have on ##ROWCOUNT that makes the while loop end even tough the update touched 100 records?
--do we have anything to process?
select top 1 * from SomeTable where processedFlag is null
WHILE(##ROWCOUNT > 0)
BEGIN
begin try
-- here I have an udpate top (100) statement that processes records with null flag in small batches
end try
begin catch
-- update ##ROWCOUNT so the while continues?
select top 1 * from SomeTable where processedFlag is null
end catch
END
I believe it because of
Statements such as USE, SET , DEALLOCATE CURSOR, CLOSE CURSOR,
BEGIN TRANSACTION or COMMIT TRANSACTION reset the ROWCOUNT value to 0.
May be END TRY is among them, but MSDN doesn't list all possible statements.
This will fix the problem:
DECLARE #i INT
SELECT #i = COUNT(*) FROM SomeTable WHERE processedFlag IS NULL
WHILE(#i > 0)
BEGIN
BEGIN TRY
UPDATE...
SET #i = ##ROWCOUNT
END TRY
BEGIN CATCH
SELECT #i = COUNT(*) FROM SomeTable WHERE processedFlag IS NULL
END CATCH
END

how to execute the next T-SQL when a before T-SQL throw an error?

I am working with SQlite and I have many T-SQL that I want to execute all of them in this way:
T-SQL1; T-SQL2, ... T-SQLN
My T-SQL are:
insert into myRelationTable(IDTable1, IDTabl2) VALUES (1,1);
insert into myRelationTable(IDTable1, IDTabl2) VALUES (1,2);
insert into myRelationTable(IDTable1, IDTabl2) VALUES (1,3);
...
With this T.SQLs I want to related records from the table1 with the table2. If any of the relations exist, there are no problem all the T-SQL is execute, but if for exameple there are a problem with the second, the first is execute but the third and the next T-SQL are not executed.
My quiestion it's if there are any way to continue execute the T-SQL and don't take care if some of the T-SQL throw an error, because what I want it's to have the relation, if some relation exists it's because other user created it, so at the end it's what I want, that the relation exists, so I would like to continue with the next T-SQL.
Is it possible?
However, if I try to delete a record that does not exists, the next T-SQL are executed, so SQLite does not take care about the error and continue with the following. Why when I try to add a new record it does not work in the same way?
Thanks.
I would strongly recommend checking if it is OK to perform the T-SQL rather than ignoring errors.
You can do this by:
DECLARE #count int
SET #count = (SELECT COUNT(1) FROM myRelationTable WHERE IDTable1 =1 AND IDTabl2 = 1)
IF #count = 0 OR #count IS NULL
BEGIN
INSERT INTO myRelationTable(IDTable1, IDTabl2) VALUES (1,1)
END
SET #count = (SELECT COUNT(1) FROM myRelationTable WHERE IDTable1 =1 AND IDTabl2 = 2)
IF #count = 0 OR #count IS NULL
BEGIN
INSERT INTO myRelationTable(IDTable1, IDTabl2) VALUES (1,2)
END
SET #count = (SELECT COUNT(1) FROM myRelationTable WHERE IDTable1 =1 AND IDTabl2 = 3)
IF #count = 0 OR #count IS NULL
BEGIN
INSERT INTO myRelationTable(IDTable1, IDTabl2) VALUES (1,3)
END
Which can very easily be wrapped within a stored procedure.
As to your question the answer is:
Sure, easily:
BEGIN TRY
insert into myRelationTable(IDTable1, IDTabl2) VALUES (1,1);
END TRY
BEGIN CATCH
--Do nothing
END CATCH
BEGIN TRY
insert into myRelationTable(IDTable1, IDTabl2) VALUES (1,2);
END TRY
BEGIN CATCH
--Do nothing
END CATCH
BEGIN TRY
insert into myRelationTable(IDTable1, IDTabl2) VALUES (1,3);
END TRY
BEGIN CATCH
--Do nothing
END CATCH
Really my problem is that I am using SQLite Expert to execute the T-SQL, but this program detect as error syntax the Begin try line.
Other way to solve the problem is to use the igonre keyword in this way:
insert or ignore into my table...
This makes that if exists an error, the ignore it and execute the next statement. But again, the "ignore" keyword is detected as an syntax error by SQLite Expert.
The "ignore" keyword belongs to the on conflict clouse. There are more information in this link.
Thanks.

Getting value from stored procedure in another stored procedure

Sorry, lots of code coming up..
I saw another question like this that used output parameters. I'm using the RETURN statement to return the value I want to use.
I have one stored procedure InsertMessage that looks like this:
ALTER PROCEDURE dbo.InsertNewMessage
(
#messageText text,
#dateTime DATETIME,
#byEmail bit,
#bySMS bit
)
AS
DECLARE #NewId int
BEGIN
BEGIN TRANSACTION
INSERT INTO MessageSet VALUES (#byEmail, #bySMS, #dateTime, #messageText)
SET #NewId = SCOPE_IDENTITY()
COMMIT
END
RETURN #NewId
which another stored procedure uses:
ALTER PROCEDURE dbo.InsertMessageFromUserToGroup
(
#userEmail nvarchar(256),
#groupId int,
#messageText text,
#bySMS bit,
#byEmail bit
)
AS
--Inserts a new message to a group
DECLARE #messageId int
DECLARE #dateTime DATETIME = GETDATE()
--First check if user is a part of the group
IF NOT EXISTS (SELECT userEmail FROM UserToGroupSet WHERE userEmail = #userEmail AND groupId = #groupId)
RETURN 'User not part of group'
ELSE --User is a part of the group, add message
BEGIN
BEGIN TRANSACTION
SET #messageId = [dbo].[InsertNewMessage](#messageText, #dateTime, #bySMS, #byEmail)
INSERT INTO MessageToUser VALUES(#userEmail, #messageId)
INSERT INTO MessageToGroup VALUES(#messageId, #groupId)
COMMIT
END
The row that causes the trouble and of which I'm unsure how to handle is this one:
SET #messageId = [dbo].[InsertNewMessage](#messageText, #dateTime, #bySMS, #byEmail)
The syntax seems ok because I can save it. When I run it I get the error message:
Running [dbo].[InsertMessageFromUserToGroup] ( #userEmail = test#test.com, #groupId = 5, #messageText = sdfsdf, #bySMS = false, #byEmail = true ).
Cannot find either column "dbo" or the user-defined function or aggregate "dbo.InsertNewMessage", or the name is ambiguous.
Transaction count after EXECUTE indicates a mismatching number of BEGIN and COMMIT statements. Previous count = 0, current count = 1.
No rows affected.
(0 row(s) returned)
#RETURN_VALUE =
Finished running [dbo].[InsertMessageFromUserToGroup].
It seems as if the other stored procedure can't be found. I've tried different ways of calling the procedure but everything else fails as well. Any suggestions?
Try changing
SET #messageId = [dbo].[InsertNewMessage](#messageText, #dateTime, #bySMS,
#byEmail)
to
EXEC #messageId = [dbo].[InsertNewMessage] #messageText, #dateTime, #bySMS,
#byEmail
Notice that SET has been changed to EXEC, and the parentheses have been removed from the parameters.
See the example in the MSDN documenation at the end of the article for more information.