Set a due date column using a trigger - sqlite

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;

Related

PL/SQL not functioning

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.

sqlite3 default on update conditionally

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).

SQLite trigger after update

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']])

UpdateParameters with datetime

I am trying to use the sqldatasource.UpdateParameters.Add() to add a date/time to my database table.
The column data type is smalldatetime and it is displaying in a gridview just fine. If I try end edit this date/time, then save the changes the other fields update, but the date/time remains the same. I have tried using the DateTime.Parse methods with no success.
sqlds1.UpdateParameters.Add("MeasurementDateTime", Data.DbType.DateTime, currentMsrmntDateTime.Text)
I use the above code for each respective value and parameter pair, but the date/time field doesn't update. The update statement again works for all parameters except the date/time.
CREATE PROCEDURE [dbo].[UpdateCathodeTemps]
(
#MeasurementTypeCode int
,#MeasurementDateTime smalldatetime
,#Value nvarchar(50)
,#Comments nvarchar(512)
,#IsMeasurementChecked bit
,#MeasurementCheckedBy nvarchar(50)
,#BakeDetailsID int
-- 7 Params
) AS
BEGIN TRANSACTION
UPDATE tblMeasurementsAtPeriod
SET MeasurementDateTime = #MeasurementDateTime,
Value = #Value,
Comments = #Comments,
MeasurementCheckedBy = #MeasurementCheckedBy,
IsMeasurementChecked = #IsMeasurementChecked,
MeasurementTypeCode = #MeasurementTypeCode,
BakeDetailsID = #BakeDetailsID
WHERE MeasurementDateTime = #MeasurementDateTime AND MeasurementTypeCode = #MeasurementTypeCode
IF ##ERROR <> 0
BEGIN
ROLLBACK
RETURN
END
COMMIT
No errors are thrown when I hit the save button, all the other fields are updated, but the date/time remains the same. I have checked the values as they are being passed to the stored procedure, and they are as I expect.
Here's your problem in the WHERE clause of your stored procedure:
WHERE MeasurementDateTime = #MeasurementDateTime AND
MeasurementTypeCode = #MeasurementTypeCode
So you are updating to the exact same date!
The where clause picks those rows with a given MeasurementDateTime, you then update those rows but the MeasurementDateTime that you are updating to is the same (obviously).
Perhaps you want 2 params:
,#OriginalMeasurementDateTime smalldatetime
,#NewMeasurementDateTime smalldatetime
I think your UPDATE query may be flawed -
You are trying to update MeasurementDateTime, but your WHERE clause is looking for records with the same value that you are trying to update MeasurementDateTime to, so you won't be updating the same records. You'll either update no records (if no records have that same value), or you'll be updating OTHER records.
That may be what you're trying to do, but it seems odd to me. It seems more logical to have another parameter for the previous value to search for and update them to the new value:
UPDATE tblMeasurementsAtPeriod
SET MeasurementDateTime = #NewMeasurementDateTime, /* NEW date value */
Value = #Value,
Comments = #Comments,
MeasurementCheckedBy = #MeasurementCheckedBy,
IsMeasurementChecked = #IsMeasurementChecked,
MeasurementTypeCode = #MeasurementTypeCode,
BakeDetailsID = #BakeDetailsID
WHERE MeasurementDateTime = #OldMeasurementDateTime /* OLD date value */
AND MeasurementTypeCode = #MeasurementTypeCode

How to have an automatic timestamp in SQLite?

I have an SQLite database, version 3 and I am using C# to create an application that uses this database.
I want to use a timestamp field in a table for concurrency, but I notice that when I insert a new record, this field is not set, and is null.
For example, in MS SQL Server if I use a timestamp field it is updated by the database and I don't have to set it by myself. Is this possible in SQLite?
Just declare a default value for a field:
CREATE TABLE MyTable(
ID INTEGER PRIMARY KEY,
Name TEXT,
Other STUFF,
Timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);
However, if your INSERT command explicitly sets this field to NULL, it will be set to NULL.
You can create TIMESTAMP field in table on the SQLite, see this:
CREATE TABLE my_table (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
name VARCHAR(64),
sqltime TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
INSERT INTO my_table(name, sqltime) VALUES('test1', '2010-05-28T15:36:56.200');
INSERT INTO my_table(name, sqltime) VALUES('test2', '2010-08-28T13:40:02.200');
INSERT INTO my_table(name) VALUES('test3');
This is the result:
SELECT * FROM my_table;
Reading datefunc a working example of automatic datetime completion would be:
sqlite> CREATE TABLE 'test' (
...> 'id' INTEGER PRIMARY KEY,
...> 'dt1' DATETIME NOT NULL DEFAULT (datetime(CURRENT_TIMESTAMP, 'localtime')),
...> 'dt2' DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%d %H:%M:%S', 'now', 'localtime')),
...> 'dt3' DATETIME NOT NULL DEFAULT (strftime('%Y-%m-%d %H:%M:%f', 'now', 'localtime'))
...> );
Let's insert some rows in a way that initiates automatic datetime completion:
sqlite> INSERT INTO 'test' ('id') VALUES (null);
sqlite> INSERT INTO 'test' ('id') VALUES (null);
The stored data clearly shows that the first two are the same but not the third function:
sqlite> SELECT * FROM 'test';
1|2017-09-26 09:10:08|2017-09-26 09:10:08|2017-09-26 09:10:08.053
2|2017-09-26 09:10:56|2017-09-26 09:10:56|2017-09-26 09:10:56.894
Pay attention that SQLite functions are surrounded in parenthesis!
How difficult was this to show it in one example?
Have fun!
you can use triggers. works very well
CREATE TABLE MyTable(
ID INTEGER PRIMARY KEY,
Name TEXT,
Other STUFF,
Timestamp DATETIME);
CREATE TRIGGER insert_Timestamp_Trigger
AFTER INSERT ON MyTable
BEGIN
UPDATE MyTable SET Timestamp =STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') WHERE id = NEW.id;
END;
CREATE TRIGGER update_Timestamp_Trigger
AFTER UPDATE On MyTable
BEGIN
UPDATE MyTable SET Timestamp = STRFTIME('%Y-%m-%d %H:%M:%f', 'NOW') WHERE id = NEW.id;
END;
To complement answers above...
If you are using EF, adorn the property with Data Annotation [Timestamp], then
go to the overrided OnModelCreating, inside your context class, and add this Fluent API code:
modelBuilder.Entity<YourEntity>()
.Property(b => b.Timestamp)
.ValueGeneratedOnAddOrUpdate()
.IsConcurrencyToken()
.ForSqliteHasDefaultValueSql("CURRENT_TIMESTAMP");
It will make a default value to every data that will be insert into this table.
you can use the custom datetime by using...
create table noteTable3
(created_at DATETIME DEFAULT (STRFTIME('%d-%m-%Y %H:%M', 'NOW','localtime')),
title text not null, myNotes text not null);
use 'NOW','localtime' to get the current system date else it will show some past or other time in your Database after insertion time in your db.
Thanks You...
If you use the SQLite DB-Browser you can change the default value in this way:
Choose database structure
select the table
modify table
in your column put under 'default value' the value: =(datetime('now','localtime'))
I recommend to make an update of your database before, because a wrong format in the value can lead to problems in the SQLLite Browser.

Resources