How to Update Fields On Insert using Trigger - sqlite

I've created and worked with Triggers in Oracle for years however I'm unable to wrap my head around how to update a field when inserting data into a sqlite database.
All I want to do is create a trigger that automatically inserts the current DateTime into a column in the sqlite database named 'createdDate' for ANY record that is inserted.
What is the best approach to accomplish this?
Below is what I've attempted without success.
CREATE TRIGGER outputLogDB_Created_Trig
BEFORE INSERT
ON outputLog
WHEN NEW.createdDate IS NULL
BEGIN
SELECT CASE WHEN NEW.createdDate IS NULL THEN NEW.createdDate = datetime('now', 'localtime') END;
END;
The above is almost a replica of how I would implement my triggers in Oracle with some modifications of course for sqlite. The logic is basically identical.
What am I missing?
Later Edit - I can get it to work if I instead use AFTER INSERT and not using FOR EACH ROW
CREATE TRIGGER outputLog_Created_Trig
AFTER INSERT
ON outputLog
WHEN New.createdDate IS NULL
BEGIN
UPDATE outputLog
SET createdDate = datetime('now', 'localtime')
WHERE outputLog_ID = New.rowid;
END;
But why can't I just insert the record using the new value while I'm inserting it? Am I ONLY able to get this in there using an Update AFTER I've already inserted the record?
The issue I have with this is the fact that I'd like to have a NOT NULL constraint on the createdDate column. Perhaps I'm simply used to how I've done it for years in Oracle? I realize the Trigger 'should' take care of any record and force this field to NEVER be NULL. It's just that NOT being able to add the constraint for some reason makes me uneasy. Do I need to let go of this worry?

Thanks to Shawn pointing me toward an easy simple solution to my problem. All that is needed in a SQLite database to insert the current Date/Time for each record being inserted is to set the DEFAULT value on that column to CURRENT_TIMESTAMP.
Since I wanted the timestamp in my own local time see below my create table script that is the solution to my problem.
CREATE TABLE outputLog (
outputLog_ID INTEGER PRIMARY KEY ASC ON CONFLICT ROLLBACK AUTOINCREMENT
NOT NULL ON CONFLICT ROLLBACK,
outputLog TEXT,
created DATETIME DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime') )
NOT NULL )
;

Related

Alternative to using subquery inside CHECK constraint?

I am trying to build a simple hotel room check-in database as a learning exercise.
CREATE TABLE HotelReservations
(
roomNum INTEGER NOT NULL,
arrival DATE NOT NULL,
departure DATE NOT NULL,
guestName CHAR(30) NOT NULL,
CONSTRAINT timeTraveler CHECK (arrival < departure) /* stops time travelers*/
/* CONSTRAINT multipleReservations CHECK (my question is about this) */
PRIMARY KEY (roomNum, arrival)
);
I am having trouble specifying a constraint that doesn't allow inserting a new reservation for a room that has not yet been vacated. For example (below), guest 'B' checks into room 123 before 'A' checks out.
INSERT INTO HotelStays(roomNum, arrival, departure, guestName)
VALUES
(123, date("2017-02-02"), date("2017-02-06"), 'A'),
(123, date("2017-02-04"), date("2017-02-08"), 'B');
This shouldn't be allowed but I am unsure how to write this constraint. My first attempt was to write a subquery in check, but I had trouble figuring out the proper subquery because I don't know how to access the 'roomNum' value of a new insert to perform the subquery with. I then also figured out that most SQL systems don't even allow subquerying inside of check.
So how am I supposed to write this constraint? I read some about triggers which seem like it might solve this problem, but is that really the only way to do it? Or am I just dense and missing an obvious way to write the constraint?
The documentation indeed says:
The expression of a CHECK constraint may not contain a subquery.
While it would be possible to create a user-defined function that goes back to the database and queries the table, the only reasonable way to implement this constraint is with a trigger.
There is a special mechanism to access the new row inside the trigger:
Both the WHEN clause and the trigger actions may access elements of the row being inserted, deleted or updated using references of the form "NEW.column-name" and "OLD.column-name", where column-name is the name of a column from the table that the trigger is associated with.
CREATE TRIGGER multiple_reservations_check
BEFORE INSERT ON HotelReservations
BEGIN
SELECT RAISE(FAIL, "reservations overlap")
FROM HotelReservations
WHERE roomNum = NEW.roomNum
AND departure > NEW.arrival
AND arrival < NEW.departure;
END;

SQLite Trigger for Inserting values from different tables

I would like to create a trigger on SQLite to update one table with values from more than one table. I have tried the code below but navicat for Sqlite wont save my trigger. Could someone help?
BEGIN
INSERT INTO hdClassSet (setNo, class, type, regdate, acYear)
VALUES (((SELECT max(setNo) FROM hdClassSet)+1),
old.clID,
0,
(date('now','localtime')),
(SELECT acID FROM tblAcad WHERE actv1=1));
END
I managed to save and test the trigger without the last value and it works. How can I reference another table that does not start or affected by the trigger? I will be grateful!

ORA-04091: table name is mutating

I get ORA-04091 Error while inserting data into table A. Table A records are refferencing other records in the same table 1:N.
Father records have fk_id = null and child records have fk not null.
create or replace trigger TRBI_A
BEFORE INSERT ON A
for each row
BEGIN
IF :new.fk_id IS NOT NULL then
UPDATE A SET actualTS = CURRENT_TIMESTAMP WHERE id = :new.fk_id;
END IF;
END;
ORA-04091: table name is mutating, trigger/function may not see it
The problem could is probably caused by trigger which tried to modify or query a table that is currently being modified by the statement that fired the trigger.
Does anyone know how to modify the trigger to have it correct?
You know what the problem is, so just read your code a little: you update the same table you are putting the trigger on.
I guess in your case you just need to put :NEW.actualTS:=current_timestamp, without using the update statement.

How do you write a good stored procedure for update?

I want to write a stored procedure (SQL server 2008r2), say I have a table:
person
Columns:
Id int (pk)
Date_of_birth date not null
Phone int allow null
Address int allow null
Name nvarchat(50) not null
Sample data:
Id=1,Date_of_birth=01/01/1987,phone=88888888,address=null,name='Steve'
Update statement in Stored procedure, assume
The parameters are already declare:
Update person set
Date_of_birth=#dob,phone=#phone,address=#address,name=#name where id=#id
The table has a trigger to log any changes.
Now I have an asp.net update page for updating the above person table
The question is, if user just want to update address='apple street' , the above update statement will update all the fields but not check if the original value = new value, then ignore this field and then check the next field. So my log table will log all the event even the columns are not going to be updated.
At this point, my solutions
Select all the value by id and store them into local variables.
Using if-else check and generate the update statement. At last,
dynamically run the generated SQL (sp_executesql)
Select all the value by id and store them into local variables.
Using if-else check and update each field seperately:
If #dob <> #ori_dob
Begin
Update person set date_of_birth=#dob where id=#id
End
May be this is a stupid question but please advice me if you have better idea, thanks!
This is an answer to a comment by the OP and does not address the original question. It would, however, be a rather ugly comment.
You can use a statement like this to find the changes to Address within an UPDATE trigger:
select i.Id, d.Address as OldAddress, i.Address as NewAddress
from inserted as i inner join
deleted as d on d.Id = i.Id
where d.Address <> i.Address
One such statement would be needed for each column that you want to log.
You could accumulate the results of the SELECTs into a single table variable, then summarize the results for each Id. Or you can use INSERT/SELECT to save the results directly to your log table.

sqlite UPDATE trigger firing on INSERT statements also?

I'm working on setting up a simple SQLite database to access via Python. So far I have one basic table, and a couple of triggers - I want to have one trigger update a field column 'date_added' when a new record is added, and another one to update a column 'date_updated' when a record is later updated. Here is my SQLite syntax for the triggers:
CREATE TRIGGER add_contact AFTER INSERT ON contact_info
BEGIN
UPDATE contact_info SET date_added = DATETIME('NOW') WHERE pkid = new.pkid;
END;
CREATE TRIGGER update_contact AFTER UPDATE ON contact_info
BEGIN
UPDATE contact_info SET date_updated = DATETIME('NOW') WHERE pkid = new.pkid;
END;
The 'add_contact' trigger seems to be working fine... it fires when I add a new record via an sql INSERT command, as planned.
The problem seems to be the 'update_contact' trigger... it fires both when I update a record via an sql UPDATE command (as planned) and when I add a new record also:
i.e. when I add a new record I get this in the 'date_added' and 'date_updated' columns:
2010-07-12 05:00:06|2010-07-12 05:00:06
and when I update that record, it changes like so:
2010-07-12 05:00:06|2010-07-12 05:14:26
I guess I'm not getting why the UPDATE trigger fires on INSERT also?
TIA,
Monte
Edited to add: Any hints on how to make it work as intended?
A better way to avoid the original problem is to use a DEFAULT ( DATETIME('NOW') )
clause for the date_added column in the table definition, instead of using an INSERT trigger.
You have an UPDATE in your INSERT trigger. So the INSERT causes an UPDATE. Which you have hooked with a different trigger.

Resources