SQLite update trigger changes all rows in the table - sqlite

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

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

update and insert triggers in sqlite

I want to have riggers that set the last_modified column automatically each time a row in updated or inserted.
Lets say I have an ID that is unique to each row.
This is my query:
CREATE TRIGGER insert_trigger
AFTER INSERT ON TABLE_NAME
BEGIN
update TABLE_NAME set last_modified =strftime('%Y-%m-%d %H:%M:%S:%s','now', 'localtime') where id = old.id;
END;
After creating this trigger, when I try to insert I get the error:
no such column: old.id
I can understand why I get this error, but how can I create a proper trigger?
When inserting, there is no old row.
To get the ID of the new row, use NEW.id.

Modify a column to NULL - Oracle

I have a table named CUSTOMER, with few columns. One of them is Customer_ID.
Initially Customer_ID column WILL NOT accept NULL values.
I've made some changes from code level, so that Customer_ID column will accept NULL values by default.
Now my requirement is that, I need to again make this column to accept NULL values.
For this I've added executing the below query:
ALTER TABLE Customer MODIFY Customer_ID nvarchar2(20) NULL
I'm getting the following error:
ORA-01451 error, the column already allows null entries so
therefore cannot be modified
This is because already I've made the Customer_ID column to accept NULL values.
Is there a way to check if the column will accept NULL values before executing the above query...??
You can use the column NULLABLE in USER_TAB_COLUMNS. This tells you whether the column allows nulls using a binary Y/N flag.
If you wanted to put this in a script you could do something like:
declare
l_null user_tab_columns.nullable%type;
begin
select nullable into l_null
from user_tab_columns
where table_name = 'CUSTOMER'
and column_name = 'CUSTOMER_ID';
if l_null = 'N' then
execute immediate 'ALTER TABLE Customer
MODIFY (Customer_ID nvarchar2(20) NULL)';
end if;
end;
It's best not to use dynamic SQL in order to alter tables. Do it manually and be sure to double check everything first.
Or you can just ignore the error:
declare
already_null exception;
pragma exception_init (already_null , -01451);
begin
execute immediate 'alter table <TABLE> modify(<COLUMN> null)';
exception when already_null then null;
end;
/
You might encounter this error when you have previously provided a DEFAULT ON NULL value for the NOT NULL column.
If this is the case, to make the column nullable, you must also reset its default value to NULL when you modify its nullability constraint.
eg:
DEFINE table_name = your_table_name_here
DEFINE column_name = your_column_name_here;
ALTER TABLE &table_name
MODIFY (
&column_name
DEFAULT NULL
NULL
);
I did something like this, it worked fine.
Try to execute query, if any error occurs, catch SQLException.
try {
stmt.execute("ALTER TABLE Customer MODIFY Customer_ID nvarchar2(20) NULL");
} catch (SQLException sqe) {
Logger("Column to be modified to NULL is already NULL : " + sqe);
}
Is this correct way of doing?
To modify the constraints of an existing table
for example... add not null constraint to a column.
Then follow the given steps:
1) Select the table in which you want to modify changes.
2) Click on Actions.. ---> select column ----> add.
3) Now give the column name, datatype, size, etc. and click ok.
4) You will see that the column is added to the table.
5) Now click on Edit button lying on the left side of Actions button.
6) Then you will get various table modifying options.
7) Select the column from the list.
8) Select the particular column in which you want to give not null.
9) Select Cannot be null from column properties.
10) That's it.

Resources