I have a table
CREATE TABLE order (
id SERIAL PRIMARY KEY NOT NULL,
total_price DECIMAL(12, 2),
date TIMESTAMP WITH TIME ZONE DEFAULT now(),
product_amount INT
);
And another table for order history
CREATE TABLE order_history (
id SERIAL PRIMARY KEY NOT NULL,
order_id INT,
total_price DECIMAL(12, 2),
date TIMESTAMP WITH TIME ZONE DEFAULT now(),
product_amount INT
);
I need a trigger function for ORDER table which can trigger on event when I insert some data into ORDER table or update data. Moreover this procedure must persist data into table ORDER_HISTORY.
Are you looking for something like this?
CREATE OR REPLACE FUNCTION save_order_in_history()
RETURNS trigger
AS $$
BEGIN
INSERT INTO order_history(order_id, total_price, product_amount)
VALUES (NEW.id, NEW.total_price, NEW.product_amount);
RETURN NULL;
END $$ LANGUAGE plpgsql;
CREATE TRIGGER order_trigger AFTER INSERT OR UPDATE
ON "order" FOR EACH ROW EXECUTE PROCEDURE save_order_in_history();
Here is SQLFiddle demo
Further reading:
CREATE TRIGGER
Trigger Procedures
Related
i want to make column Customers_Balance on TBL_CUSTOMERS to show by defult the result of that stored procedures....
TBL_CUSTOMERS which it have the info of the customer, and it created as like that
CREATE TABLE TBL_CUSTOMERS
(
Customers_ID int PRIMARY KEY,
Customers_Name varchar(100) NOT NULL,
Customers_Phone varchar(100),
Customers_Address varchar(100),
Customers_Web varchar(100),
Customers_Balance decimal(16,0) not null,
);
TBL_CUSTOMERS_DETAILS which it have the details of all customer transactions , and it created as like that
CREATE TABLE TBL_CUSTOMERS_DETAILS
(
Customers_Details_ID int PRIMARY KEY,
Customers_ID int,
Customers_Details_Tybe varchar(50) not null,
Customers_Details_Date date not null,
Customers_Details_Amount decimal(16,0) not null,
);
i have created stored procedures to calculate the result of sum of customer's transactions balance and worked fine, and it created as like that
CREATE PROC SP_SUM_CUSTOMERS_DETAILS_AMOUNT
#ID INT
AS
SELECT SUM(Customers_Details_Amount)
FROM TBL_CUSTOMERS_DETAILS
Where Customers_ID = #ID
NOW
i want to make column Customers_Balance on TBL_CUSTOMERS to show by defult the result of that stored procedures....
how i can make something like that ??
Materializing values that can be calculated by other materialize values is usually a bad idea as it bears the risk of inconsistencies.
So you best drop the column Customers_Balance in TBL_CUSTOMERS and the procedure and then create a view which includes the customer's data and their balance. You can do so by a join and aggregation.
ALTER TABLE TBL_CUSTOMERS
DROP COLUMN Customers_Balance;
DROP PROCEDURE SP_SUM_CUSTOMERS_DETAILS_AMOUNT;
CREATE VIEW VW_CUSTOMERS
AS
SELECT C.Customers_ID,
C.Customers_Name,
C.Customers_Phone,
C.Customers_Address,
C.Customers_Web,
sum(CD.Customers_Details_Amount) Customers_Balance
FROM TBL_CUSTOMERS C
INNER JOIN TBL_CUSTOMERS_DETAILS CD
ON CD.Customers_ID = C.Customers_ID
GROUP BY C.Customers_ID,
C.Customers_Name,
C.Customers_Phone,
C.Customers_Address,
C.Customers_Web;
You are looking for a Computed Column
What you need to do is to create a scalar function rather than a stored procedure (simply change your current stored procedure into a scalar function), and then use this function in your computed column. This would give you an auto-updated results on your computed column.
So, redoing your work should be something like this :
-- CREATE THE SCALAR FUNCTION FIRST
CREATE FUNCTION SUM_CUSTOMERS_DETAILS_AMOUNT (#ID INT)
RETURNS INT
AS
BEGIN
RETURN (
SELECT SUM(Customers_Details_Amount)
FROM TBL_CUSTOMERS_DETAILS
WHERE Customers_ID = #ID
)
END
GO
-- NOW DROP THE CURRENT Customers_Balance COLUMN
ALTER TABLE TBL_CUSTOMERS
DROP COLUMN Customers_Balance
GO
-- CREATE THE COMPUTED COLUMN WITH THE FUNCTION
ALTER TABLE TBL_CUSTOMERS
ADD Customers_Balance AS dbo.SUM_CUSTOMERS_DETAILS_AMOUNT (Customers_ID)
GO
Is there a way so that in a CREATE TABLE table ( ... ) I can force a procedure or a function to run on every insert?
-- For exmaple:
CREATE TABLE table (
ID INT IDENTITY (1,1) PRIMARY KEY,
Pass varchar(200) -- Can I do Proc(Pass) to return the output instead?
)
You can run a trigger on every new insert and do what you want..You have access to virtual table called inserted inside trigger which contains inserted values
Create trigger name
on tbl
after insert
as
begin
---select * from inserted
--your proc here
end
You might also take a look at computed columns. They are virtual columns in a table computed by an expression defined when the table is created. E.g.:
CREATE TABLE products
(id INT,
price MONEY,
tax AS price * .15);
-- the column tax is computed as 15% of the price whenever a price is changed (inserts or updates). Though having limitations they come in handy in simple cases.
I'm trying to set the ValidFrom range for the current record in a temporal table. I'm doing this because I'm rebuilding history from another system (non SQL) into a data warehouse so the current version of records may be "as of" a date that's in the past. If I can't get this to work, my fall back is to add a row in the history table that fills in the gap but I'm thinking there's a way to get this to work. Maybe there are some ways with alter columns?
/******** CURRENT TIME=2018-03-10 15:32:26 *****/
CREATE TABLE TestHist(
ID int NOT NULL,
Name varchar(max),
--Temporal Stuff
ValidFrom datetime2(7) NOT NULL,
ValidTo datetime2(7) NOT NULL
)
GO
CREATE TABLE Test(
ID int IDENTITY(1,1) NOT NULL,
Name varchar(max),
--Temporal Stuff
ValidFrom datetime2(7) GENERATED ALWAYS AS ROW START NOT NULL,
ValidTo datetime2(7) GENERATED ALWAYS AS ROW END NOT NULL,
PRIMARY KEY CLUSTERED (ID ASC) ,
PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo)
)
WITH( SYSTEM_VERSIONING = ON ( HISTORY_TABLE = dbo.TestHist ) )
GO
ALTER TABLE Test SET (SYSTEM_VERSIONING = OFF)
go
--THIS WORKS BUT SETS THE VALIDFROM TO CURRENT TIME
insert into Test(name) values ('fred')
--AND BTW, THIS IS HOW I LOAD THE HISTORY (THIS WORKS TOO)
insert into TestHist(ID,Name,ValidFrom,ValidTo) values (1,'joe',null,'1/1/18','1/15/18')
insert into TestHist(ID,Name,ValidFrom,ValidTo) values (1,'steve','fred','2/1/18','3/1/18')
But the problem is that it sets the current ValidFrom time arbitrarily to when you do your insert statement:
select * from test
ID Name ParentName ValidFrom ValidTo
1 fred NULL 2018-03-10 15:32:26.4403041 9999-12-31 23:59:59.9999999
And here's what I wish I could do:
--THIS DOESN'T WORK
insert into Test(name,ValidFrom,ValidTo) values ('fred','2/1/18','9999-12-31 23:59:59.997')
I get this error:
Msg 13536, Level 16, State 1, Line 38
Cannot insert an explicit value into a GENERATED ALWAYS column in table 'CodeAnalytics.dbo.Test'. Use INSERT with a column list to exclude the GENERATED ALWAYS column, or insert a DEFAULT into GENERATED ALWAYS column.
You cannot update ValidFrom on the temporal table. However, you can update ValidFrom on the history table that keeps track of changes. You create a record there by changing any value in the temporal table.
So you can do following steps:
Change anything in rows of your temporal table where you want to change the value of the ValidFrom column. This step creates a record in the history table for every changed record in the original table.
Set system versioning off for your temporal table.
Update ValidFrom in your history table.
Set system versioning back on for your temporal table.
Edit: oops. it's 2019 now. anyway, i needed to do this, so leaving here in case anyone else finds useful.
maybe something like this is what you're looking for?
CREATE TABLE TestHist(
ID int NOT NULL,
Name varchar(max),
--Temporal Stuff
ValidFrom datetime2(7) NOT NULL,
ValidTo datetime2(7) NOT NULL
)
GO
CREATE TABLE Test(
ID int IDENTITY(1,1) NOT NULL,
Name varchar(max),
--Temporal Stuff
ValidFrom datetime2(7) GENERATED ALWAYS AS ROW START NOT NULL,
ValidTo datetime2(7) GENERATED ALWAYS AS ROW END NOT NULL,
PRIMARY KEY CLUSTERED (ID ASC) ,
PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo)
)
WITH( SYSTEM_VERSIONING = ON ( HISTORY_TABLE = dbo.TestHist ) )
GO
ALTER TABLE Test SET (SYSTEM_VERSIONING = OFF);
insert into TestHist(ID,Name,ValidFrom,ValidTo) values (1,'steve','2/1/18','3/1/18')
insert into TestHist(ID,Name,ValidFrom,ValidTo) values (1,'joe','1/1/18','1/15/18')
SET IDENTITY_INSERT Test ON;
insert into Test(id, name) values (1, 'fred')
--after dropping period one can update validfrom on temporal table from max history
alter table Test DROP PERIOD FOR SYSTEM_TIME;
GO
update test set validfrom =(select max(validto) from TestHist where id=test.ID);
--now add period and turn system versioning back on
alter table test ADD PERIOD FOR SYSTEM_TIME (ValidFrom, ValidTo);
GO
alter table test set (system_versioning = on (HISTORY_TABLE=dbo.TestHist));
GO
--think this gives what you want
select * from test for system_time all
Hi guys i AM trying to do a trigger statement (i havnt done any before) so i am basically looking to insert values into a table and m using the following code but its not working any help will be do me good thanks in advance: code m using is following:
CREATE TRIGGER updatemovie
ON movies
FOR INSERT
AS
BEGIN
INSERT INTO movies values (
movieID, movieName, movieTime, movieDate, movieTime, movieType, Duration, ratings)
END
table i am looking to update is:
CREATE TABLE movies(
movieID int IDENTITY (1,1)NOT NULL,
movieName varchar (50),
movieTime Time,
movieDate Date,
movieType varchar (20),
duration varchar (10),
rating varchar (10),
PRIMARY KEY (perID),
);
A trigger will allow you to either cancel the update/insert/delete depending on a condition or it will allow you to keep a record of the update/insert/delete. So if you have a second table (audit ) you can do the following:
update/insert/delete
CREATE TRIGGER updatemovie
ON movies
FOR INSERT
AS
BEGIN
INSERT INTO movies_audit values (
inserted.movieID, inserted.movieName, inserted.movieTime, inserted.movieDate, inserted.movieTime, inserted.movieType, inserted.Duration, inserted.ratings)
END
The inserted table is created automatically and contains (for the duration of the trigger) the rows that were just inserted.
I want the code to be able to automatically update the time stamp when a new row is inserted as I can do in MySQL using CURRENT_TIMESTAMP.
How will I be able to achieve this in PostgreSQL?
CREATE TABLE users (
id serial not null,
firstname varchar(100),
middlename varchar(100),
lastname varchar(100),
email varchar(200),
timestamp timestamp
)
To populate the column during insert, use a DEFAULT value:
CREATE TABLE users (
id serial not null,
firstname varchar(100),
middlename varchar(100),
lastname varchar(100),
email varchar(200),
timestamp timestamp default current_timestamp
)
Note that the value for that column can explicitly be overwritten by supplying a value in the INSERT statement. If you want to prevent that you do need a trigger.
You also need a trigger if you need to update that column whenever the row is updated (as mentioned by E.J. Brennan)
Note that using reserved words for column names is usually not a good idea. You should find a different name than timestamp
You'll need to write an insert trigger, and possible an update trigger if you want it to change when the record is changed. This article explains it quite nicely:
http://www.revsys.com/blog/2006/aug/04/automatically-updating-a-timestamp-column-in-postgresql/
CREATE OR REPLACE FUNCTION update_modified_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.modified = now();
RETURN NEW;
END;
$$ language 'plpgsql';
Apply the trigger like this:
CREATE TRIGGER update_customer_modtime BEFORE UPDATE ON customer FOR EACH ROW EXECUTE PROCEDURE update_modified_column();
Updating timestamp, only if the values changed
Based on E.J's link and add a if statement from this link (https://stackoverflow.com/a/3084254/1526023)
CREATE OR REPLACE FUNCTION update_modified_column()
RETURNS TRIGGER AS $$
BEGIN
IF row(NEW.*) IS DISTINCT FROM row(OLD.*) THEN
NEW.modified = now();
RETURN NEW;
ELSE
RETURN OLD;
END IF;
END;
$$ language 'plpgsql';
Using 'now()' as default value automatically generates time-stamp.
To automatically update the timestamp field in PostgresSQL whenever a new row is inserted, you can set the current_timestamp as its default value:
CREATE TABLE users (
id serial not null,
firstname varchar(100),
middlename varchar(100),
lastname varchar(100),
email varchar(200),
timestamp timestamp default current_timestamp
)
In addition to this, you might want to prevent anyone from updating this field in the future, and this can be done by creating an update trigger and applying it:
CREATE OR REPLACE FUNCTION stop_change_on_timestamp()
RETURNS trigger AS
$BODY$
BEGIN
-- always reset the timestamp to the old value ("actual creation time")
NEW.timestamp := OLD.timestamp;
RETURN NEW;
END;
$BODY$
CREATE TRIGGER prevent_timestamp_changes
BEFORE UPDATE
ON users
FOR EACH ROW
EXECUTE PROCEDURE stop_change_on_timestamp();
For update and Liquibase YAML:
databaseChangeLog:
- changeSet:
id: CREATE FUNCTION set_now_to_timestamp
author: Konstantin Chvilyov
changes:
- sql:
sql: CREATE OR REPLACE FUNCTION set_now_to_timestamp() RETURNS TRIGGER LANGUAGE plpgsql AS 'BEGIN NEW.timestamp = NOW(); RETURN NEW; END;'
- changeSet:
id: CREATE TRIGGER update_users_set_now_to_timestamp
author: Konstantin Chvilyov
changes:
- sql:
sql: CREATE TRIGGER update_users_set_now_to_timestamp BEFORE UPDATE ON users FOR EACH ROW EXECUTE PROCEDURE set_now_to_timestamp();