CREATE TABLE t (
id INTEGER PRIMARY KEY,
val INTEGER,
dt INTEGER NOT NULL DEFAULT (strftime('%s','now'))
)
CREATE TRIGGER tr AFTER UPDATE OF val
ON t
BEGIN
UPDATE t SET dt=strftime('%s','now') WHERE id=NEW.id;
END;
INSERT INTO t (1, 11)
Now when I do
UPDATE t SET val=2 WHERE id=1
It is working ok, but when I want to specify dt:
UPDATE t SET val=2, dt=140000 WHERE id=1
Trigger overwrite my new dt. How to get both of that two examples working ?
Why not BEFORE UPDATE OF val? The trigger will update dt to current (takes care of case 1), then the UPDATE will update dt to the desired (takes care of case 2).
Related
Im currently trying to implement a row trigger that fires when the new Employee number inserted into the table is not continuous.
"Continuous" in a relationship to the Employee number means the first record inserted will have the Employee number 1, the second record will have the employee number 2, and each next position must have a number greater by one that a number of the previous position.
I have successfully created the trigger, however when I inserted a new record that have an Employee number that is not continuous, my trigger is not fired.
Im unsure where I went wrong and hope I can get some explanation and corrections on my code.
CREATE OR REPLACE TRIGGER CONTENUM
AFTER INSERT ON TRKEMPLOYEE
FOR EACH ROW
DECLARE
continuous_value EXCEPTION;
PRAGMA exception_init(continuous_value, -20111);
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
IF (:NEW.E# > :OLD.E# + 1) THEN
RAISE_APPLICATION_ERROR (-20111,'The value of Employee number must be continuous');
END IF;
END CONTENUM;
/
Here is the format of my sample TRKEMPLOYEE table
CREATE TABLE TRKEMPLOYEE(
E# NUMBER(12) NOT NULL,
NAME VARCHAR(50) NOT NULL,
DOB DATE ,
ADDRESS VARCHAR(300) NOT NULL,
HIREDATE DATE NOT NULL,
CONSTRAINT TRKEMPLOYEE_PKEY PRIMARY KEY(E#) );
Here is my insert statement.
Currently in my table TRKEMPLOYEE there is only 15 rows thus with my insert statement, the trigger should fire but it is not happening.
INSERT INTO TRKEMPLOYEE VALUES( 17, 'David', NULL, 'GB',sysdate );
Thank you.
First of all you are checking AFTER INSERT ON TRKEMPLOYEE which will be executed after the row is inserted.
Secondly, you cannot check :OLD.E# since you are not updating and you are not using a old value.
Also you should drop the trigger at all and use SEQUENCES and let Oracle take care of the auto-increment values every time you add a new employee.
If you want to continue with your current logic, fixes that can be applied:
Change AFTER INSERT ON TRKEMPLOYEE to BEFORE INSERT ON TRKEMPLOYEE
Logic should be changed as below:
CREATE OR REPLACE TRIGGER contenum BEFORE
INSERT ON trkemployee
FOR EACH ROW
DECLARE
continuous_value EXCEPTION;
PRAGMA exception_init ( continuous_value, -20111 );
PRAGMA autonomous_transaction;
max_e# INTEGER;
BEGIN
SELECT
nvl(MAX(e#), 0)
INTO max_e#
FROM
trkemployee;
IF ( :new.e# > max_e# + 1 ) THEN
raise_application_error(-20111, 'The value of Employee number must be continuous');
END IF;
END contenum;
/
I do not recommend this solution because it will start to become slower as your table starts to grow.
My table has timestamp column. I want a trigger which sets timestamp to 0 on affected rows when a row is updated and the timestamp is not specified in the update statement.
If I use this trigger:
CREATE TRIGGER AFTER UPDATE ON mytable FOR EACH ROW
WHEN (NEW.timestamp IS NULL)
BEGIN
UPDATE mytable SET timestamp = 0 WHERE id = NEW.id;
END;
then the trigger doesn't fire for this update statement:
UPDATE mytable SET comecolumn='some'
I.e. timestamp of affected rows doesn't change to 0.
Can you please help me define the trigger?
The only way to make additional changes to a row in an UPDATE trigger is to execute another UPDATE on the same table afterwards.
The only way to detect whether a column value is changed is to compare the old and the new row values; the trigger does not know which columns actually were mentioned in the original UPDATE statement.
To prevent the trigger from triggering itself recursively, you should restrict it to be triggered by changes of all columns except the timestamp:
CREATE TRIGGER clear_timestamp
AFTER UPDATE OF all_the, other, columns ON MyTable
FOR EACH ROW
WHEN OLD.timestamp = NEW.timestamp
BEGIN
UPDATE MyTable
SET timestamp = 0
WHERE id = NEW.id;
END;
I think the problem is that in the SET statement is expanded to every column, with every column set to the current value in the database. So the original only trigger works, if the current timestamp column is NULL.
A solution could be to create another trigger that resets the timestamp column to NULL before an UPDATE.
CREATE TRIGGER "set_null"
BEFORE UPDATE ON "mytable" FOR EACH ROW
BEGIN
UPDATE mytable set timestamp = NULL where rowid = NEW.rowid;
END
This way the NEW.timestamp is NULL if it is not specified in the UPDATE SET.
Obviously now a NOT NULL constraint cannot be set on timestamp.
Another problem is that trigger recursion must be off when executing a update query:
PRAGMA recursive_triggers = OFF;
Here is another way:
import sqlite3
conn = sqlite3.connect(':memory:')
c = conn.cursor()
name = {'name':'jack'}
c.execute("""CREATE TABLE Programs (
id INTEGER PRIMARY KEY,
name VARCHAR(64) NOT NULL,
time_added INTEGER
);""")
c.execute("""CREATE TRIGGER program_time_added AFTER INSERT ON Programs
FOR EACH ROW
BEGIN
UPDATE Programs SET time_added =datetime('now', 'localtime') WHERE id = NEW.id;
END;""")
c.execute('INSERT INTO Programs (name) VALUES (?)', [name['name']])
Problem: a simplest possible update trigger writes a new value to all table rows instead of just the row being updated. Here is the table:
[names]
id INTEGER PRIMARY KEY
name TEXT
len INTEGER
Now I want to create triggers to update 'len' with the length of 'name'. This INSERT trigger seems to be doing the job corectly:
CREATE TRIGGER 'namelen' AFTER INSERT ON 'names'
BEGIN
UPDATE 'names' SET len = length(NEW.name) WHERE (id=NEW.id);
END;
Problems begin when I add a similar UPDATE trigger:
CREATE TRIGGER 'namelenupd' AFTER UPDATE ON 'names'
BEGIN
UPDATE 'names' SET len = length(NEW.name) WHERE (OLD.id=NEW.id);
END;
The update trigger writes the new length to all rows of the table, despite the WHERE clause. For example, if I say
UPDATE 'names' SET name='foo' where id=1;
then the value of 'len' becomes 3 for all rows of the table. I've looked at sqlite trigger examples and I can't see my error. What else must I do to make sure the trigger updates the 'len' column only in the row(s) that are actually updated?
Both OLD.xxx and NEW.xxx refer to the table row that caused the trigger to run.
The UPDATE statement inside the trigger runs independently; if you want to restrict it to one table row, you have to explicitly do this in its WHERE clause by filtering on that statement's table values, i.e., names.id or just id.
When the original UPDATE statement does not change the id column, the old and new id values are the same, and the expression OLD.id=NEW.id is true for all records in the table, as seen by the inner UPDATE statement.
The correct trigger looks like this:
CREATE TRIGGER "namelenupd"
AFTER UPDATE OF name ON "names"
BEGIN
UPDATE "names" SET len = length(NEW.name) WHERE id = NEW.id;
END;
Had the same issue, here's the syntax from my trigger
You would change "ALTER" to "CREATE" depending on what you already have (or not)
You have "id" as your primary key
Your dbo is "names"
Obviously, this will set the name value to "foo" (not really what you wanted). The key seems to be the last line, where you set inner join inserted on names.Id = inserted.Id.
USE [yourDBname]
ALTER TRIGGER [dbo].[yourTrigger]
ON [dbo].[names]
After INSERT, UPDATE
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
Select id from inserted
begin
update [dbo].names
set [dbo].names.name = 'foo'
from dbo.names
inner join inserted
on names.id = inserted.id
END
I'm working on a Sqlite database where a table has both a initial_date column and a due_date one. I'd like to automatically set the latter's value using a trigger every time the former one's value is changed, but something doesn't works.
Here's a simplified versione of the table DDL
CREATE TABLE timetable (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
initial_date DATETIME,
due_date DATETIME
);
As you can see, the two DATETIME columns allow NULL value; this is because in the real table I'd like to insert rows without setting the initial_date and update those rows later for setting the initial_date.
This is the trigger I added
CREATE TRIGGER [timetable-due-date] AFTER UPDATE ON timetable
BEGIN
UPDATE timetable
SET due_date = DATE( NEW.initial_date, '+ 10 days' )
WHERE id = NEW.id
AND NEW.initial_date IS NOT NULL;
END;
but it doesn't fires. If I execute UPDATE timetable SET initial_date='2013-10-04' WHERE id=1, due_date keeps the initial NULL value.
I also tried using the CREATE TRIGGER ... AFTER UPDATE OF initial_date ON ... variant, but without any luck.
I'm surely doing something really stupid here, but I can't figure what.
Thank you for your help.
The syntax of your date modifier is wrong; there must be no space between the + and the number:
CREATE TRIGGER "timetable-due-date"
AFTER UPDATE OF initial_date ON timetable
WHEN NEW.initial_date IS NOT NULL
BEGIN
UPDATE timetable
SET due_date = DATE(new.initial_date, '+10 days')
WHERE id = NEW.id;
END;
I'm trying out the Sqlite3 REPLACE (INSERT OR REPLACE) command. I like to keep a created datetime (creDT) and an update datetime (updDT). So I created a database and a trigger for INSERT (creDT & updDT) and one for UPDATE (updDT), but each REPLACE (especially the ones where the primary key already exists) ends up with the current time in both creDT and updDT. Does REPLACE DELETE and INSERT instead of UPDATE?
Is this the standard behavior or am I doing something wrong?
def createDbTables(self):
self.sqlCursor.execute("""
CREATE TABLE rfdetector (
sn TEXT PRIMARY KEY,
detector TEXT,
hex TEXT,
updDT DATE,
creDT DATE)
""")
self.sqlCursor.execute("""
CREATE TRIGGER insert_rfdetector_creDT
AFTER INSERT ON rfdetector
BEGIN
UPDATE rfdetector SET creDT = DATETIME('now','localtime') WHERE rowid = new.rowid;
UPDATE rfdetector SET updDT = DATETIME('now','localtime') WHERE rowid = new.rowid;
END;
""")
self.sqlCursor.execute("""
CREATE TRIGGER update_rfdetector_updDT
AFTER UPDATE ON rfdetector
BEGIN
UPDATE rfdetector SET updDT = DATETIME('now','localtime') WHERE rowid = new.rowid;
END;
""")
def insertSql(self, data):
self.sqlCursor.execute(
'REPLACE INTO rfdetector (sn, hex, detector) VALUES (?, ?, ?)',
(data.serialNumber, data.hex, data.detector))
Looks like SQLite performs a DELETE then INSERT on REPLACE:
REPLACE
When a UNIQUE constraint violation occurs, the REPLACE
algorithm deletes pre-existing rows that are causing the
constraint violation
prior to inserting or updating the current row and the command
continues executing normally. If a NOT NULL constraint violation
occurs, the REPLACE conflict resolution replaces the NULL value with
the default value for that column, or if the column has no default
value, then the ABORT algorithm is used. If a CHECK constraint
violation occurs, the REPLACE conflict resolution algorithm always
works like ABORT.
from: http://www.sqlite.org/lang_conflict.html