Should ##rowcount persist from a trigger into the proc its calling? - sql-server-2008

In other words, if I have a trigger that calls a proc; should I be able to check ##rowcount in the first line of the proc? And should ##rowcount have a value based on the last SQL statment executed in the trigger?
I'm debugging a proc which I did not write. First line of the proc is
if ##rowcount=0
RETURN
The proc is called by a trigger. The last thing the trigger does before it calls the proc is to:
INSERT INTO #temp_table (some fields...)
SELECT some fields.. FROM inserted
EXEC SOMEPROC
What's happening is that ##rowcount in the proc is always 0 and the proc ends before it does anything. The fix is pretty easy in that I'd just move the ##rowcount check into the trigger and take it out of the proc.
But its curious to me why it was written this way in the first place. Almost like it used to work at some point and now it doesn't? I just wanted to check and see if maybe there was some change in behavior between SQL2005 and SQL2008 or some system setting that got flipped. Or did one of my predecessors just write bad code that never worked?

I don't like to use ##ROWCOUNT for checking the status of a trigger. Too many other statements in the procedure and in between can reset that to 0.
For example:
SELECT TOP (10) name FROM sys.objects;
SET ANSI_NULLS ON;
SELECT ##ROWCOUNT;
----
0
Rather, I would check the inserted/deleted pseudo-tables. So, for an insert or update trigger:
IF EXISTS (SELECT 1 FROM inserted)
BEGIN
...
For a delete trigger:
IF EXISTS (SELECT 1 FROM deleted)
BEGIN
...

Related

SQL Trigger fallback

My bosses nor any of the DBAs know how to make triggers and I don't neither(new-ish programmer), they just copied/pasted some old triggers from the DB for examples for me. Anyway, my triggers copy data from one table to another after an update/insert. The insert/update that goes to the original table is vital, so if anything fails, I just want the trigger to fail and the original insert/update to still run just fine.
I am using MySQL to test, but we use DB2, but they won't give me access to test triggers in their DB2 environemt, so this the closest solution I can use to test the trigger logic.
I noticed that BEGIN ATOMIC is in the example triggers, does that do what I want? And what would be equivalent in MySQL, so I can test?
I have subselects in my triggers are these safe? Should I declare variables to help avoid issues?
--#SET TERMINATOR #
create table test_trigger (i int) in userspace1#
create table test_trigger_copy (i int) in userspace1#
create or replace trigger test_trigger_air
after insert on test_trigger
referencing new as n
for each row
begin
declare continue handler for sqlexception begin end;
insert into test_trigger_copy(i) values (case when mod (n.i, 2)=0 then cast(RAISE_ERROR('70001', 'No even numbers!') as int) else n.i end);
end#
insert into test_trigger(i) values 1, 2, 3#
select * from test_trigger_copy#
select * from test_trigger#
For DB2 (for LUW at least) databases.
The INSERT statement inside the trigger generates an exception when you try to insert an even number into the base table. The CONTINUE handler consumes exceptions, so you get all inserted numbers in the base table, but only odd numbers in the copy table.
A close MySQL equivalent to DB2's BEGIN ATOMIC, would be a combination of START TRANSACTION, COMMIT, EXIT HANDLER, and ROLLBACK. Instead of:
BEGIN ATOMIC
<statement(s)>
END
one could do the following in MySQL:
BEGIN
DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN
ROLLBACK;
RESIGNAL;
END;
START TRANSACTION;
<statement(s)>
COMMIT;
END
As to subselects (you mean subqueries?) in the trigger, I do not see why they would not work, as long as you observe the restrictions.

Not allowed to return a result set from a trigger

I need to increment the next highest character field upon an insert - e.g. if '007' exists, then next is '008'
I have a stored procedure:
BEGIN
SELECT LPAD(CAST(MAX(Line_Order) AS SIGNED INTEGER) + 1,3,'0')
FROM bill_project_lineitems
WHERE Item_Id = vItem;
END
Then I have this trigger:
CALL MaxLineOrder(new.item_id,#new.line_order )
Which I call before.
When I try to INSERT, MySQL complains: 1415 - 'Not allowed to return a result set from a trigger'
So then how do I solve this problem?
Ok, I read again and MAYBE I undestood. (but why not pasting the full code?) The error is clear, You CANT return a resultset from a Trigger.
I suppose that you want to issue an INSERT and get a resultset. But you can't. So you should:
Move your INSERT into the Stored Procedure
DROP the Trigger
CALL the Procedure.
Stored Procedures can return a resultset (or even many resultsets), so this will work. I hope that this is what you want.

MySql - trigger doesn't run as expected

New to MySql triggers, just learning.
CREATE TRIGGER MyTrigger
AFTER UPDATE ON MyTable
FOR EACH ROW
BEGIN
IF (new.field1 < 0 or new.field1 > 5) THEN
UPDATE new SET new.field1 = old.field1;
END IF;
END;
The goal is to keep the value of field1 the same, if the update puts it outside the range.
However, instead it sets it to 0. What am I doing wrong? How should this code look?
Here is an example that should hopefully get you started:
DELIMITER ~
CREATE TRIGGER `so_13547992_trigger`
BEFORE UPDATE ON `so_13547992`
FOR EACH ROW BEGIN
IF ( NEW.`field1` < 0 OR NEW.`field1` > 5 ) THEN
SET NEW.`field1` = OLD.`field1`;
END IF;
END;
~
Why would it work better? Well first of all your example trigger is recursive, you can't update the same table in a trigger that was triggered by an update.
Second, the new in your UPDATE statement is not a table name, you need to specify one explicitly.
It doesn't appear to be a legit trigger at all, doesn't your server complain when you try to create it? Can you perhaps show actually SHOW CREATE TRIGGER `your_trigger`; to make sure that it's really created and looks like you pasted it above?
Even if your example would would work, you're trying to do an unconstrained update on all rows of your table, not on the ones you're trying to update, you should have a WHERE clause; again, given that issue one and two are taken care of.

If conditional in SQL Script for Mysql

In a sql script that does sequential execution, is there a way one can introduce an IF THEN ELSE conditional to control the flow of query execution?
I happened to run into this http://www.bennadel.com/blog/1340-MySQL-Does-Not-Support-IF-ELSE-Statements-In-General-SQL-Work-Flow.htm
which says that the IF THEN ELSE will not work in a sql script.
Is there another way around?
Basically, I want to run a particular "select colName from table" command and check if colName corresponds to a particular value. If it does, proceed with the rest of the script. Else, halt execution.
Please advise.
I just wrap my SQL script in a procedure, where conditional code is allowed. If you'd rather not leave the statements lying around, you can drop the procedure when you're done. Here's an example:
delimiter //
create procedure insert_games()
begin
set #platform_id := (select id from platform where name = 'Nintendo DS');
-- Only insert rows if the platform was found
if #platform_id is not null then
insert into game(name, platform_id) values('New Super Mario Bros', #platform_id);
insert into game(name, platform_id) values('Mario Kart DS', #platform_id);
end if;
end;
//
delimiter ;
-- Execute the procedure
call insert_games();
-- Drop the procedure
drop procedure insert_games;
If you haven't used procedures, the "delimiter" keyword might need some explanation. The first line switches the delimiter to "//" so that we can include semi-colons in our procedure definition without MySQL attempting to interpret them yet. Once the procedure has been created, we switch the delimiter back to ";" so we can execute statements as usual.
After doing some research I think I may have found a way to work around this. I was looking for a way to verify if a script had already executed against a target database. This will be primarily for version control of my databases. I have a table created to keep track of the scripts that have been executed and wanted some flow inside my scripts to check that table first before execution. While I have not completely solved the problem yet I have created a simple script that basically does what I need, I just need to wrap the DDL into the selects based on the value of the variables.
step 1 - Setup a bit variable to hold the result
step 2 - do your select and set the variable if the result is found
step 3 - Do what you need to do on false result
step 4 - Do what you need to do on true result
Here is the example script
set #schemachangeid = 0;
select #schemachangeid := 1 from SchemaChangeLog where scriptname = '1_create_tables.sql';
select 'scriptalreadyran' from dual where #schemachangeid = 1;
select 'scriptnotran' from dual where #schemachangeid = 0;
I also recognize this is an old thread but maybe this will help someone out there trying to do this kind of thing outside of a stored procedure like me.

Does MySQL permit callbacks in C such that when a change happens, I can be notified?

Does MySQL permit callbacks in C such that when a change happens in the database, like an insert, that is performed by a different program or by the user at the command line, I can be notified?
I am guessing that it doesn't, because mysqlclient is a library, not a running thread. But I may as well ask.
Create a trigger like so.
DELIMITER $$
CREATE TRIGGER ad_mytable_each AFTER DELETE ON MyTable FOR EACH ROW
BEGIN
#write code that trigger After delete (hence the "ad_" prefix)
#For table MyTable (The _MyTable_ middle)
#On each row that gets inserted (_each suffix)
#
#You can see the old delete values by accesing the "old" virtual table.
INSERT INTO log VALUES (old.id, 'MyTable', old.field1, old.field2, now());
END$$
DELIMITER ;
There are triggers for INSERT, DELETE, UPDATE
And they can fire BEFORE or AFTER the action.
The trigger BEFORE the action can cancel the action by forcing an error, like so.
CREATE TRIGGER bd_mytable_each BEFORE DELETE ON MyTable FOR EACH ROW
BEGIN
#write code that trigger Before delete (hence the "db_" prefix)
declare DoError Boolean;
SET DoError = 0;
IF old.id = 1 THEN SET DoError = 1; END IF;
IF (DoError = 1) THEN SELECT * FROM Table_that_does_not_exist_to_force_error;
#seriously this example is in the manual.
END$$
DELIMITER ;
This will prevent deletion of record 1.
A before UPDATE Trigger can even change the values updated.
CREATE TRIGGER bu_mytable_each BEFORE UPDATE ON MyTable FOR EACH ROW
BEGIN
IF new.text = 'Doon sucks' THEN SET new.text = 'Doon rules';
END$$
DELIMITER ;
Hope you'll be Trigger happy.
MySQL's triggers allow you to hook into insert/update/delete queries and do something additional. You could log them in a separate table, for example.
Well you could attach a trigger to user defined function, and have it call an external program, that would then notify your code..
http://dev.mysql.com/doc/refman/5.0/en/faqs-triggers.html#qandaitem-B-5-1-10
You can use triggers combined with UDFs (user defined functions) so that the corresponding action on the database executes a trigger that calls a C/C++ function.
Just consider that this mechanism runs your code inside the mysql server process, not in the client side.