Add after Update trigger to all tables in database - sql-server-2008

I am currently adding a trigger to all tables in a database using this script:
exec sp_MSForEachTable 'CREATE TRIGGER [?_Update] ON ? FOR UPDATE AS
BEGIN SET NOCOUNT ON update ? set ModifiedOn = GETDATE() END'
I need to change the trigger so that it only updates the changed row so it need to look like this:
exec sp_MSForEachTable 'CREATE TRIGGER [?_Update] ON ? FOR UPDATE AS
BEGIN SET NOCOUNT ON update ? set ModifiedOn = GETDATE()
from ? updatedTable inner join inserted i
on i.[the tables primary key] = updatedTable.[the tables primary key]
END'
however I do not know how to get the [the tables primary key] value. A Trigger created by the first script looks like:
update [dim].[company] set ModifiedOn = GETDATE()
and assuming I could get the second script to work it would create this trigger:
update [dim].[company] set ModifiedOn = GETDATE()
from [Dim].[Company] updatedTable
inner join inserted i on i.CompanyKey = updatedTable.CompanyKey
Does anyone know how to do this, or if its not possible an alternative method of adding the required trigger to all tables in the database?
The ultimate goal is to know when a record was changed, preferably human readable or that can be converted to something human readable. I do not know when or where the updates will come from so using sprocs for update is out.

If all of your primary keys are, specifically, identity columns, then you can cheat and use the IDENTITYCOL "column" name:
create table T (ID int IDENTITY(1,1) not null primary key,Col1 varchar(10) not null,Changed datetime null)
go
create trigger T_T
on T
after insert,update
as
update t set Changed = GETDATE()
from inserted i
inner join
T t on i.IDENTITYCOL = t.IDENTITYCOL
IDENTITYCOL is effectively an automatic alias for whatever column is an identity column in that table.
Actually, $identity might be preferred:
update t set Changed = GETDATE()
from inserted i
inner join
T t on i.$identity = t.$identity
It would appear the IDENTITYCOL is deprecated. Not that I can actually find an actual documentation page for either of these.

Related

Unrecognised statement type (near IF)

I'm trying to write an SQL query that will update a 'password' record in my database when the user provides a matching email and login. I've written the query below to try and achieve this:
SET #password = 'password123';
SET #email = 'email';
SET #newPassword = 'pass54321';
IF `customer.Password` = #password WHERE `customer.Email` == #email
BEGIN
set `customer`.`Password` = #newPassword
END
I get an error saying 'Unrecognised statement type (near IF)'. If anyone knows how to solve this, any help would be appreciated! I'm very new to using SQL so this might be completely wrong!!!
I'm not a mysql user but looking at the docs (https://dev.mysql.com/doc/), there are a couple of routes to take. I'm showing you the upsert way (which is pretty cool, I've never seen it before).
First, you should be using primary keys to help with lookup performance and to help qualify the selection criteria.
# sample table with primary key
CREATE TABLE customer (customer_id int not null primary key, name VARCHAR(20), email VARCHAR(20), password VARCHAR(20));
# seed with data
INSERT INTO customer VALUES(1,'John','john#email.com','password1');
INSERT INTO customer VALUES(2,'Jane','jane#email.com','passwordx');
# set test params
SET #password = 'password1';
SET #email = 'john#email.com';
SET #newPassword = 'pass54321';
/*
depending on how the rest of the query is structured or if this
doesn't fit the requirements...you have the option to use IF EXISTS
This is using the 'ON DUPLICATE KEY UPDATE' to check if the record exists then performs an update if it does or an insert if it doesn't (aka upsert)
*/
INSERT INTO customer VALUES (1, 'John', 'john#email.com', 'password1')
ON DUPLICATE KEY UPDATE password = #newPassword;
# test for password update
SELECT * FROM customer WHERE customer_id = 1
If your table doesn't have primary keys, run! It may make more sense to use the If EXISTS statement.
Tag someone in your organization to verify that what you're doing is within the coding standards. Use the MySql docs site - it seems like it's well maintained.

Added a Column to a table and fill it with existing column data from that table

I am updating some code removing a relationship from some terms we have with credit limits. To do this I get to update the database!
The new schema will add a column for CreditLimitCode which will be the same as the old column TermsCodeId without that columns keys. I need to leave TermsCodeId intact for the time being so a rename is not possible.
I attempted the answer for this SO question but that give me syntax errors because CreditLimitCode does not exist and I do not like the default being added.
IF COL_LENGTH('CreditApp.dbo.CreditLimit', 'CreditLimitCode') IS NULL
BEGIN
ALTER TABLE CreditApp.dbo.CreditLimit ADD CreditLimitCode VARCHAR(6) NOT NULL DEFAULT (0)
UPDATE CreditApp.dbo.CreditLimit SET CreditLimitCode = CreditTermsId WHERE CreditLimitCode = 0
PRINT 'Added CreditLimitCode to CreditLimt'
END
I am not sure a trigger for after insert here would be correct because I am altering the table not inserting rows.
exec('UPDATE CreditApp.dbo.CreditLimit
SET CreditLimitCode = CreditTermsId
WHERE CreditLimitCode = 0')
because at compilation time CreditLimitCode does not exist

How to create a Trigger within sql

I have been trying to create a Trigger, however my attempts have been unsuccessful. I seem to be getting an error (#1064), which I have no solution for. Can somebody explain or demonstrate any faults in the syntax.
Let me specify:
I have delivery_id as primary key in delivery table,
I also have delivery_id as a foreign key in entry_log table.
By comparing both id's(if true), will return a text referring to the output of the bit (either 0 or 1)
DELIMITER //
DROP TRIGGER IF EXISTS entry_trigger//
CREATE TRIGGER entry_trigger BEFORE INSERT ON entry_log
FOR EACH ROW
BEGIN
DECLARE #xentry VARCHAR(45)
DECLARE #inta bit
SET #inta = SELECT allowed
FROM delivery
WHERE delivery.delivery_id = entry_log.delivery_id;
CASE
when #inta = 0 then #xentry = 'Acces Denied'
when #inta = 1 then #xentry = 'Acces Allowed'
END CASE
INSERT INTO entry_log(entry_time,access_allowed) VALUES(now(),#xentry);
END
//
This is assuming that you use MySQL. In the body of the trigger you use
WHERE delivery.delivery_id = entry_log.delivery_id;
I think you want to compare to the entry_log entry that the trigger is running on, right? In that case you must use this syntax:
WHERE delivery.delivery_id = NEW.delivery_id;
see here for more examples.
UPDATE
I see that also you try to do an INSERT INTO entry_log within the TRIGGER. This will of course not work, because you would create an infinite recursive loop. Within the
body of the trigger you can do unrelated table access, but not into the table you are inserting. You can change the values to be inserted by the trigger by setting NEW.xyz = whatever
UPDATE 2
I doubt, that your CASE statement is correct. At least it must end with END CASE. You can use IF here, since you don't have many cases to address. If you must use CASE this post might help you: MYSQL Trigger set datetime value using case statement
UPDATE 3
I am not sure, but I think you need brackets around the variable setting statement. try this trigger definition:
DELIMITER //
DROP TRIGGER IF EXISTS entry_trigger//
CREATE TRIGGER entry_trigger BEFORE INSERT ON entry_log
FOR EACH ROW
BEGIN
SET #inta = (SELECT allowed
FROM delivery
WHERE delivery.delivery_id = NEW.delivery_id);
SET NEW.access_allowed = #inta;
SET NEW.entry_time = NOW();
END
//
Note, that this is written out of my head, so beware of syntax errors in my script.

Creating an immutable field in mysql

I'd like to make a TIMESTAMP field DEFAULT CURRENT_TIMESTAMP, for 'creation time' purpose. But if someone or somehow something changes that TIMESTAMP, my data won't be consistent.
Is there a way I can ensure it won't change unless I delete the row and reinsert it, other than application level?
With the suggested answer provided, i could work around with something like this
CREATE TRIGGER consistency1 BEFORE UPDATE ON table1
FOR EACH ROW
BEGIN
IF NEW.creationtime != OLD.creationtime THEN
SET NEW.creationtime = OLD.creationtime;
END IF;
END;
Since my comment has been appreciated, here's the extended version.
I personally don't think that it's possible.
Anyway, there are a couple of things you can try:
Make sure that only your application can write on the database
Write a trigger like this (pseudocode!)
create trigger prevent_change_timestamp on tbl_name
before update
#fetch old row value
#verify if the timestamp field has been changed
#raise an error (any SQL error will do)
Or like this
create trigger revert_change_timestamp on tbl_name
after update
#fetch pre-change row value
#update the row with the "old" value in place of the new one
I'd personally go with the 3rd option, if possible. Anyway, the 2nd one is good too. I'd not rely on the 1st option unless necessary (eg: no access to trigger functionality)
More info here: reference
It's funny in a way that database apps don't offer this functionality as standard: not only for a "created" timestamp field, but for things like autoincrement id fields, and any miscellaneous values which you may want to set on creating a record and then never allow to be changed... wonder what the rationale is?
What you can do here is, you can write a TRIGGER on the table when a row is being updated. In that trigger, you can compare the old and new values, and if they are different then you can just overwrite the new value with the old one.
I tried this in MySQL 5.1 and got an error
DELIMITER //
CREATE TRIGGER member_update_0
-> AFTER UPDATE ON members
-> FOR EACH ROW
-> BEGIN
-> IF NEW.id != OLD.id THEN
-> SET NEW.id = OLD.id;
-> END IF;
-> END;//
ERROR 1362 (HY000): Updating of NEW row is not allowed in after trigger
The same trigger with AFTER replaced by BEFORE is accepted;
to me, this is a counter-intuitive way to do it, but it works
delimiter ;
UPDATE members SET id=11353 WHERE id=1353;
Query OK, 0 rows affected (0.00 sec)
Rows matched: 1 Changed: 0 Warnings: 0
It is actually possible to do this very neatly if you are using InnoDB.
Create another table with just one column. That column should have a foreign key (hence the innodb requirement in this solution) that points to the immutable column of the original table in question.
Put a restriction like "ON UPDATE RESTRICT".
In summary:
CREATE TABLE original (
....
immutable_column ...
INDEX index1(immutable_column)
....
) ENGINE=INNODB;
CREATE TABLE restricter (
.....
col1,
INDEX index2(col1),
FOREIGN KEY (col1) REFERENCES original (immutable_colum) ON UPDATE RESTRICT ON DELETE CASCADE
) ENGINE=INNODB;
Taking the idea a step further (for those of us still stuck with a legacy version of MySQL) you can have BOTH a protected & defaulted create_stamp AND an auto-updating update_stamp as follows:
If you have a table such as
CREATE TABLE `csv_status` (
`id` int(11) NOT NULL primary key AUTO_INCREMENT,
`create_stamp` datetime not null,
`update_stamp` timestamp default current_timestamp on update current_timestamp,
`status` enum('happy','sad') not null default 'happy'
);
Then you can define these triggers on it
drop trigger if exists set_create_stamp ;
create definer = CURRENT_USER trigger set_create_stamp BEFORE INSERT on
csv_status for each row
set NEW.create_stamp = now();
drop trigger if exists protect_create_stamp ;
delimiter //
create definer = CURRENT_USER trigger protect_create_stamp BEFORE UPDATE on
csv_status for each row
begin
if NEW.create_stamp != OLD.create_stamp then
set NEW.create_stamp = OLD.create_stamp;
end if;
end;//
delimiter ;

MYSQL auto increase column entity by 1 on update?

I have a table: ID,name,count,varchar(255)
Now, what i'd like is to increase the "count" each time that row in the table is updated.
Of course, the easy way is to read first, get the value, increase by 1 in php, then update with the new value. BUT!
is there any quicker way to do it? is there a system in mysql that can do the ++ automatically? like autoincrement, but for a single entity on itself?
I see two options:
1.
Just add this logic to every update query
UPDATE `table` SET
`data` = 'new_data',
`update_counter` = `update_counter` + 1
WHERE `id` = 123
2.
Create a trigger that will do the work automatically:
CREATE TRIGGER trigger_name
AFTER UPDATE
ON `table`
FOR EACH ROW
BEGIN
UPDATE `table`
SET `update_counter` = `update_counter` + 1
WHERE `id` = NEW.id
END
Create a trigger:
http://dev.mysql.com/doc/refman/5.1/en/create-trigger.html
Triggers are pieces of code that are "triggered" by the database on certain events. In your case, the event would be an update. Many RDBMS support triggers, so does MySQL. The advantage of using a trigger is that every piece of your PHP logic that updates this entity, will implicitly invoke the trigger logic, you don't have to remember that anymore, when you want to update your entity from a different piece of PHP logic.
you can look up at the trigger
or can do with the extra mysql query
update table set count=count+1 ;
UPDATE table SET name='new value', count=count+1 WHERE id=...
An SQL update can use fields in the record being updated as a source of data for the update itself.