POSTGRESQL Trigger that updates a column after having altered the table - sql

I have a populated table that already has some specific columns. I'd like to alter the table structure by adding a new column which will represent a field that is the count of the rows of another table.
Is it possible to implement it through a trigger in order to do it automatically after the alter command?
I have come up with something like this but apparently doesn't update the column:
Function for the trigger:
CREATE FUNCTION update_column() RETURNS TRIGGER AS
$BODY$
DECLARE
num INTEGER;
BEGIN
SELECT COUNT(*) INTO num FROM mytable1, mytable2 WHERE mytable1.field = mytable2.field GROUP BY mytable1.field;
UPDATE mytable1 SET new_column = num;
END;
$BODY$
LANGUAGE PLPGSQL;
The trigger:
CREATE TRIGGER insert
AFTER UPDATE
ON mytable1
FOR EACH ROW
EXECUTE PROCEDURE update_column();
And the alter command:
ALTER TABLE mytable1 ADD new_column INT;

Is it possible to implement it through a trigger in order to do it automatically after the alter command?
Firstly, your trigger fires after each update on the table, not after the alter table. Second, there would not be much point for a trigger on such DDL event anyway.
If your table has data already, you might need to initialize the column after its creation, with something like:
update mytable1 t1
set new_column = (select count(*) from mytable2 t2 where t2.field = t1.field)
As for the logic you wanted in the trigger: you need to use new and old to refer to the row that was changed in the original table - and, typically, you would expect the update to affect either no rows (if field was not changed), or two rows (if it was):
update mytable1 t1
set new_column = (select count(*) from mytable2 t2 where t2.field = t1.field)
where t1.field in (new.field, old.field) and new.field <> old.field
That said, I would not recommend such set up. Maintaining such derived information is expensive and tedious (you have an update trigger, now you need an insert and a delete trigger too). Instead, you can compute the logic on the fly when needed, or create a view:
create view myview as
select t1.*,
(select count(*) from mytable2 t2 where t2.field = t1.field) as new_column
from mytable1 t1

You would not set this value automatically in a trigger based on when the column is added. You would use a trigger to maintain the value when there are changes on the second table.
The process is:
Add the column with a default value:
alter table mytable1 add new_column int default 0;
Initialize the value of the column to the current values:
update mytable1 t1
set new_column = (select count(*) from mytable2 t2 where t2.field = t1.field);
Add insert/update/delete triggers on mytable2. The key logic is:
update mytable1
set cnt = cnt + 1
where t1.field = new.field;
update mytable1
set cnt = cnt - 1
where t1.field = old.field;
If you don't have a foreign key relationship, you may also need to insert a row in mytable1 if the field value is not in the table. However, I strongly recommend an explicit foreign key constraint to prevent this from happening.
This increments/decrements the value when changes are made to the second table.

Related

Trigger works based on type of insert SQL Server

I am working in SQL Server Management Studio v18, and I have the following trigger:
CREATE TRIGGER [dbo].[update_surface]
ON [dbo].[my_table]
FOR INSERT
AS
BEGIN
SET NOCOUNT ON;
DECLARE #surface_m2 REAL
SET #surface_m2 = (SELECT cast(round(CAST(Dimension1*Dimension2 as decimal)/ cast(1000000 as decimal),3,3) as decimal(10,3)) AS surface FROM my_table WHERE Surface_m2 IS NULL)
UPDATE dbo.my_table SET Surface_m2 = #surface_m2
END
I have two columns in my_table, which are Dimesion1 and Dimension2. I want that the trigger multiplies them, and set the result to other column in the same table, which is Surface_m2, whenever this column is null. The trigger does his function, but based on the type of insert I do:
If I insert a row in my_table by the graphic environment the trigger works as I wish. With each new row, Surface_m2 has his own result.
But if I insert by INSERT INTO my_table VALUES ().... (query) the trigger updates Surface_m2 column of all previous rows with the result of each new insert.
Why is the trigger working like that? Is there any other simple way to do what I am trying to do?
Thanks.
Insert trigger gives you actual values that are inserted in a special table called... "inserted".
So what you need to do is join this table against your main table and perform the logic needed, no variables required.
Something like this untested code
create trigger...
begin
UPDATE t
SET Surface_m2 = cast(round(CAST(t.Dimension1*t.Dimension2 as decimal)
/ cast(1000000 as decimal),3,3) as decimal(10,3))
from dbo.my_table t
inner join inserted i
ON i.ID = t.ID
WHERE t.Surface_m2 IS NULL
end
This begs the question though, why can't you just insert the Surface_m2 value yourself. Or even better, change Surface_m2 to be a computed column if it's always depends on Dimension1 and Dimension2

How can I add prefix to SQLite auto_increment field?

Is there a way to add prefix to an auto increment field in SQLite so that when new records are added, the auto increment value contain the prefix?
Is there a way to add prefix to an auto increment field in sqlite so
that when new records are added, the auto increment value contain the
prefix?
No.
Such a column is a special column that is an alias of the rowid column. Such a column MUST be an integer value.
The intended use of such a column is really to be able to uniquely identify a row.
BUT.....
However, it would be possible to always return a value with a prefix e.g. by using
SELECT 'prefix'||mycolumn FROM mytable;
Working Example
:-
DROP TABLE IF EXISTS mytable;
CREATE TABLE IF NOT EXISTS mytable (mycolumn INTEGER PRIMARY KEY, myothercolumn TEXT);
INSERT INTO mytable (myothercolumn) VALUES('Fred'),('Mary'),('Jane'),('andsoon');
SELECT 'myprefix'||mycolumn, myothercolumn FROM mytable;
Note there is no need for the AUTOINCREMENT keyword that only imposes a constraint but is inefficient AUTOINCREMENT
Result
Mimicking what you want
It would be possible to replicate what you want e.g. :-
DROP TRIGGER IF EXISTS mytable2manager;
DROP TABLE IF EXISTS mytable2;
CREATE TABLE IF NOT EXISTS mytable2 (mycolumn TEXT, myothercolumn TEXT);
CREATE TRIGGER IF NOT EXISTS mytable2manager AFTER INSERT ON mytable2 BEGIN
UPDATE mytable2 SET mycolumn = 'myprefix'||(SELECT count() FROM mytable2) WHERE rowid = new.rowid;
END;
INSERT INTO mytable2 (myothercolumn) VALUES('Fred'),('Mary'),('Jane'),('andsoon');
SELECT * FROM mytable2;
This creates a table along with a TRIGGER and then inserts the same data as above.
The TRIGGER is actioned whenever a row is inserted and in this case it mimics what the SQLite auto-generation of the rowid value does prefixing the value
it could alternatively use SET mycolumn = 'myprefix'||rowid
Without the TRIGGER mycolumn would be null
Result
Additional
You could even have the TRIGGER in the mimicking method apply different prefixes. The following example uses a different prefix when the value in the myothercolumn starts with an M
:-
DROP TRIGGER IF EXISTS mytable2manager;
DROP TABLE IF EXISTS mytable2;
CREATE TABLE IF NOT EXISTS mytable2 (mycolumn TEXT, myothercolumn TEXT);
CREATE TRIGGER IF NOT EXISTS mytable2manager AFTER INSERT ON mytable2 BEGIN
UPDATE mytable2 SET mycolumn = CASE WHEN substr(new.myothercolumn,1,1) = 'M' THEN 'myprefix' ELSE 'myotherprefix' END||(SELECT count() FROM mytable2) WHERE rowid = new.rowid;
-- UPDATE mytable2 SET mycolumn = 'myprefix'||rowid WHERE rowid = new.rowid;
END;
INSERT INTO mytable2 (myothercolumn) VALUES('Fred'),('Mary'),('Jane'),('andsoon');
SELECT * FROM mytable2;
Result
The simplest method is probably to use a view:
create view v_t as
select ('prefix' || id) as new_id, t.*
from t;

Create a trigger that updates a column in a table when a different column in the same table is updated - SQL

How to create a trigger that updates a column in a table when a different column in the same table is updated.
So far I have done the following which works when any new data is created. Its able to copy data from "Purchase Requisition" to "PO_Number" however when data has been modified in "Purchase Requisition" , no changes is made to "PO_Number" and the value becomes NULL. Any kind help will be seriously appreciated.
ALTER TRIGGER [dbo].[PO_Number_Trigger]
ON [dbo].[TheCat2]
AFTER INSERT
AS BEGIN
UPDATE dbo.TheCat2 SET PO_Number=(select Purchase_Requisition from inserted) where DocNo= (Select DocNo from inserted);
END
You need to add 'UPDATE' as well as insert to the trigger, otherwise it will only execute on new data, not updated data. Also added 'top 1' to the select statements from the inserted table to allow this to be 'safe' on batch updates, however it will only update 1 record.
ALTER TRIGGER [dbo].[PO_Number_Trigger]
ON [dbo].[TheCat2]
AFTER INSERT, UPDATE
AS BEGIN
UPDATE dbo.TheCat2 SET PO_Number=(select top 1 Purchase_Requisition from inserted) where DocNo= (Select top 1 DocNo from inserted);
END
This might do what you want:
Your trigger is altering all rows in TheCat2. Presumably, you only want to alter the new ones:
ALTER TRIGGER [dbo].[PO_Number_Trigger]
ON [dbo].[TheCat2] AFTER INSERT
AS
BEGIN
UPDATE tc
SET PO_Number = Purchase_Requisition
FROM dbo.TheCat2 tc JOIN
inserted i
on tc.DocNo = i.DocNo ;
END;
However, perhaps a computed column is sufficient for your purposes:
alter table add PO_Number as Purchase_Requisition;

SQL Trigger update another table

I am newbie to triggers... can anybody help me with a trigger?
I have Table:
Name | Number
I want to write a trigger when my table receives a query like
update MyTable
set Number = Number + 1
where Name = 'myname'
When this query is running, the trigger should update another table for example:
Update MyTable 2
set Column = 'something'
where Name = 'myname (above name)
Thank you very much !
You will need to write an UPDATE trigger on table 1, to update table 2 accordingly.
Be aware: triggers in SQL Server are not called once per row that gets updated - they're called once per statement, and the internal "pseudo" tables Inserted and Deleted will contain multiple rows, so you need to take that into account when writing your trigger.
In your case, I'd write something like:
-- UPDATE trigger on "dbo.Table1"
CREATE TRIGGER Table1Updated
ON dbo.table1 FOR UPDATE
AS
BEGIN
-- update table2, using the same rows as were updated in table1
UPDATE t2
SET t2.Column = 'something'
FROM dbo.Table2 t2
INNER JOIN Inserted i ON t2.ID = i.ID
END
GO
The trick is to use the Inserted pseudo table (which contains the new values after the UPDATE - it has the exact same structure as your table the trigger is written for - here dbo.Table1) in a set-based fashion - join that to your dbo.Table2 on some column that they have in common (an ID or something).
create a trigger on table 1 for update:
CREATE TRIGGER dbo.update_trigger
ON table1
AFTER UPDATE
AS
BEGIN
DECLARE #Name VARCHAR(50)
SELECT #Name=Name FROM INSERTED
Update MyTable 2
SET Column = 'something'
WHERE Name = #Name
END
GO
try this ;)

How to keep in a database the number of calls of each record?

For example, I want to know at any moment most popular records that users searched in the database.
I expect that for each record I need to introduce a new number field. Thus, the record will be like this:
key - value - counter
How I can to increase the value of counter inside a database?
I think it's something like calling a stored procedure while a query, but I'm not sure. Perhaps the question is quite simple, I'm just a beginner and I apologize in that case.
You should use a trigger for this. Triggers are commands that execute on events, everytime an INSERT, UPDATE or DELETE statement is executed, even if their calls do not modify any records. Due tot this, you can't directly create a trigger for updating the count field of a record when you SELECT (read) it.
But, you can try a workaround in which you also have a date field in your table, and update it everytime a record is called. Use your application to send this datetime value to the database, which will trigger an UPDATE.
By making an UPDATE statement, your trigger is called and this way you can add your code to modify the count column.
CREATE TRIGGER tafter AFTER INSERT OR DELETE ON tbl1 FOR EACH ROW UPDATE SET counter = counter + 1 where key = 'keyval';
Firstly, this sounds like an awful performance problem. Every time you select a record you have to update it if you're tracking the selects with a single number, which just stores total selects, otherwise you have to insert timestamped values into another table to be able to analyse when the rows were read.
Anyway, you can do this with a common table expression in which you update a counter in the table and return the results to the main query: http://sqlfiddle.com/#!1/1aa41/6
Code something like:
create table my_table(col1 varchar(30), col2 numeric, select_count numeric);
insert into my_table values ('A',1,0);
insert into my_table values ('B',2,0);
insert into my_table values ('C',3,0);
insert into my_table values ('D',4,0);
insert into my_table values ('E',5,0);
with upd as (
update my_table
set select_count = select_count+1
where col1 = 'A'
returning *)
select *
from upd;
with upd as (
update my_table
set select_count = select_count+1
where col1 = 'B'
returning *)
select *
from upd;
with upd as (
update my_table
set select_count = select_count+1
where col1 = 'A'
returning *)
select *
from upd;
with upd as (
update my_table
set select_count = select_count+1
returning *)
select count(*)
from upd;
with upd as (
update my_table
set select_count = select_count+1
returning *)
select sum(col2)
from upd;
with upd as (
update my_table
set select_count = select_count+1
returning *)
select *
from upd;