I'm new to triggers in PostgreSQL and I don't know if what I want to do is a trigger job, but was suggestion of my teacher.
I have the following link table:
id | link | visited | filtered | broken | visiting
The last four attributes are boolean and default to false. Currently I'm setting it to true on an UPDATE and there is no more use for it (the row).
The idea of new design is let the link table only with id and link attributes, and the others attributes to an archive tables (visitedLinksTable, brokenLinksTable, filteredLinksTable and visitingTable).
Is trigger util for this? They said to move it to another table (insert into some archive table and delete from the link table)
Something along these lines should work. The particulars will depend on your specific schema, etc.
CREATE FUNCTION update_function() RETURNS TRIGGER AS $$
BEGIN
IF NEW.visited IS TRUE
OR NEW.filtered IS TRUE
OR NEW.broken IS TRUE
OR new.visiting IS TRUE THEN
INSERT INTO archive_table (id,link,visited,filtered,broken,visiting)
VALUES NEW.id,NEW.link,NEW.visited,
NEW.filtered,NEW.broken,NEW.visiting;
DELETE FROM table WHERE id=NEW.id;
RETURN NULL;
END IF;
RETURN NEW
END
$$ LANGUAGE plpgsql;
CREATE TRIGGER update_trigger
BEFORE UPDATE ON table
FOR EACH ROW EXECUTE PROCEDURE
update_function();
A trigger wouldn't really work for this. Presumably you'd need some way to determine which table (visited, broken, filtered, visiting) the link should be moved to when you delete it but there's no way to tell the trigger where the link should go.
You could use a couple non-trigger functions to encapsulate a process like this:
Link goes into the link table.
Move the link to the "visiting" table.
Depending on the result of trying the link, move it it from "visiting" to the "visited", "broken", or "filtered" tables.
You could use a stored procedure to take care of each of the transitions but I don't know if you'd gain anything over manual INSERT ... SELECT and DELETE statements.
However, if you really have a thing for triggers (and hey, who doesn't like triggers?) then you could use your original six column table, add a last-accessed timestamp, and periodically do some sort of clean-up:
delete from link_table
where last_accessed < some_time
and (visited = 't' or filtered = 't' or broken = 't')
Then you could use a DELETE trigger to move the link to one of your archive tables based on the boolean columns.
You could use views and view triggers on recent PostgreSQL, I suppose. In general, I think it is best to encapsulate your storage logic inside your data logic anyway, and views are a useful way to do this.
Another way would be to have access to/from the table be through a function instead. That way you can maintain a consistent API while changing storage logic as necessary. This is the approach I usually use, but it has a few different tradeoffs compared to the view approach:
The view/trigger approach works better with ORMs, while the procedural approach dispenses with the need for an ORM altogether.
There are different maintenance issues that arise with each approach. Being aware of them is key to managing them.
Related
Let me describe my scenario here.
I am having a table with multiple records, where the name is the same, as it's gonna be records for the same person updated on daily basis.
Right now, I am trying to find out the easiest way to update all the names accordingly.
Name is going to be updated (TestName -> RealName)
I want this change to be applied to all the records with the same name, in this case, "TestName"
I can do a single query, but I am trying to find if there's an automatic way to do this correctly.
Been trying using a triggers, but in most cases, I am ending with an infinite loop, as I am trying to update the table, where a trigger is actually bound to, so it's invoking another update and so on.
I don't need an exact solution, just give me some ropes about how it can be achieved, please.
The problem may be simply resolved by using the function pg_trigger_depth() in the trigger, e.g.:
create trigger before_update_on_my_table
before update on my_table
for each row
when (pg_trigger_depth() = 0) -- this prevents recursion
execute procedure before_update_on_my_table();
However, it seems that the table is poorly designed. It should not contain names. Create a table with names (say user_name) and in the old table store a reference to the new one, e.g.:
create table user_name(id serial primary key, name text);
create table my_table(id serial primary key, user_id int references user_name(id));
You can use event triggers in postgresql https://www.postgresql.org/docs/9.3/sql-createeventtrigger.html
I have two Triggers for a table that share identical functionality. A record is inserted into or updated in said table, and some of its fields are Inserted into another table, or elsewhere into the same table. If it is a new record, the Insert Trigger triggers. If it is an already-existing record, the Update Trigger triggers.
It isn't rocket science.
However, I'm entertaining the idea of simplifying these Triggers by having each of them call a Stored Procedure that will replicate the above functionality. I'm going this route because a separate table I'm working with doesn't allow an After Insert Trigger to be used in this manner with the code it contains, and I'd like to find a workaround. I'm not sure if it'll work or not, but I'd like to give it a shot.
I have no idea how to go about it, though. I utilize variables that are populated with data from tables Joined to the triggering one, and N.field or O.field throughout the Triggers. Is this possible if the Trigger calls a Proc? If it's called For Each Row, are the New and Old fields available to be used within the Proc? If so, how? Input and Output parameters with regard to Stored Procedures are new to me. I don't even know if what I'm asking makes as much sense written out as it does in my head.
For example, we'll say I'm working with a table called Cars.
ID |VIN |CATEGORY
----------------------------------------------
1 A1234 A
2 A1235 A
3 B1234 B
If a record is Inserted into Cars, the first character of CARS.VIN should be Inserted into CARS.CATEGORY. If a record is Updated, CARS.CATEGORY should be, as well, referencing N.VIN. I have a Trigger for each of these and they work fine.
The minutiae of the 'why' aside, is it possible to contain the functionality of these Triggers into a Proc that either can call? How would I go about the initial steps of creation for said Proc?
Instead of using a trigger, consider defining CATEGORY as a generated column, for example
CREATE TABLE cars (
id INT GENERATED ALWAYS AS IDENTITY,
vin VARCHAR(17),
category CHAR(1) GENERATED ALWAYS AS ( LEFT( vin, 1 ) )
)
I'm not sure if there is a debate about this.
When I read books, I'm advised to use triggers to follow up inserts into other tables. On the other hand, my mentor uses stored procedures to insert into the other tables.
My question here, which is the best method? Or is there a better way?
If You want of Insert data on table you can do it only by stored procedure not by triggers because Triggers can't accept parameters or anything at runtime.
you can call a stored procedure from inside another stored procedure but you can't directly call another trigger within a trigger.
So I think you should use Stored procedure rather then triggers
For Details you can visit to following link.
http://www.codeproject.com/Tips/624566/Differences-between-a-Stored-Procedure-and-a-Trigg
Please Reply for my answer.
You don't need either. Start a transaction, make all your inserts (parent table first, child tables afterwards), end the transaction with COMMIT, and you are done.
Use stored procedures if you want to bundle this and ensure some kind of consistency (such that there is always at least one child table for a parent table for instance). But this can get complicated. Say you want to insert a new product with all its colors, sizes, suppliers and selling markets. Certain colors/sizes will be supplied by one or more suppliers and not the others, same with selling markets. To show these relations we usually use tables, but now you'll have to put these into parameters somehow in order to get them inserted into tables. I was told that some people have all their database writes in procedures. This is probably possible but has its limits.
As to auto-inserts by triggers: You can use them to log data, so as to get a history or the like, but you don't use them to insert business data. Think of an order with its positions: You insert the order header (order date, client number, ...), but how shall the trigger know which items were ordered? Or vice versa: You insert an order position (item number, price) and want the header be created automatically, but how shall the trigger know the client number? Triggers are not appropriate for such things.
As mentioned: Usually you'd just work with plain SQL in transactions and that's it.
I'm implementing CRUD on my silverlight application, however I don't want to implement the Delete functionality in the traditional way, instead I'd like to set the data to be hidden instead inside the database.
Does anyone know of a way of doing this with an SQL Server Database?
Help greatly appreciated.
You can add another column to the table "deleted" which has value 0 or 1, and display only those records with deleted = 0.
ALTER TABLE TheTable ADD COLUMN deleted BIT NOT NULL DEFAULT 0
You can also create view which takes only undeleted rows.
CREATE VIEW undeleted AS SELECT * FROM TheTable WHERE deleted = 0
And you delete command would look like this:
UPDATE TheTable SET deleted = 1 WHERE id = ...
Extending Lukasz' idea, a datetime column is useful too.
NULL = current
Value = when soft deleted
This adds simple versioning that a bit column can not which may work better
In most situations I would rather archive the deleted rows to an archive table with a delete trigger. This way I can also capture who deleted each row and the deleted rows don't impact my performance. You can then create a view that unions both tables together when you want to include the deleted ones.
You could do as Lukasz Lysik suggests, and have a field that serves as a flag for "deleted" rows, filtering them out when you don't want them showing up. I've used that in a number of applications.
An alternate suggestion would be to add an extra status assignment if there's a pre-existing status code. For example, in a class attendance app we use internally an attendance record could be "Imported", "Registered", "Completed", "Incomplete", etc.* - we added a "Deleted" option for times where there are unintentional duplicates. That way we have a record and we're not just throwing a new column at the problem.
*That is the display name for a numeric code used behind the scenes. Just clarifying. :)
Solution with triggers
If you are friends with DB trigger, then you might consider:
add a DeletedAt and DeletedBy columns to your tables
create a view for each tables (ex: for table Customer have a CustomerView view, which would filter out rows that have DeletedAt not null (idea of gbn with date columns)
all your CRUD operations perform as usual, but not on the Customer table, but on the CustomerView
add INSTEAD OF DELETE trigger that would mark the row as delete instead of physically deleting it.
you may want to do a bit more complex stuff there like ensuring that all FK references to this row are also "logically" deleted in order to still have logical referential integrity
I you choose to use this pattern, I would probably name my tables differently like TCustomer, and views just Customer for clarity of client code.
Be careful with this kind of implementation because soft deletes break referential integrity and you have to enforce integrity in your entities using custom logic.
Is it possible to prevent deletion of the first row in table on PostgreSQL side?
I have a category table and I want to prevent deletion of default category as it could break the application. Of course I could easily do it in application code, but it would be a lot better to do it in database.
I think it has something to do with rules on delete statement, but I couldn't find anything remotely close to my problem in documentation.
You were right about thinking of the rules system. Here is a link to an example matching your problem. It's even simpler than the triggers:
create rule protect_first_entry_update as
on update to your_table
where old.id = your_id
do instead nothing;
create rule protect_first_entry_delete as
on delete to your_table
where old.id = your_id
do instead nothing;
Some answers miss one point: also the updating of the protected row has to be restricted. Otherwise one can first update the protected row such that it no longer fulfills the forbidden delete criterion, and then one can delete the updated row as it is no longer protected.
You want to define a BEFORE DELETE trigger on the table. When you attempt to delete the row (either match by PK or have a separate "protect" boolean column), RAISE an exception.
I'm not familiar with PostgreSQL syntax, but it looks like this is how you'd do it:
CREATE FUNCTION check_del_cat() RETURNS trigger AS $check_del_cat$
BEGIN
IF OLD.ID = 1 /*substitute primary key value for your row*/ THEN
RAISE EXCEPTION 'cannot delete default category';
END IF;
END;
$check_del_cat$ LANGUAGE plpgsql;
CREATE TRIGGER check_del_cat BEFORE DELETE ON categories /*table name*/
FOR EACH ROW EXECUTE PROCEDURE check_del_cat();
The best way I see to accomplish this is by creating a delete trigger on this table. Basically, you'll have to write a stored procedure to make sure that this 'default' category will always exist, and then enforce it using a trigger ON DELETE event on this table. A good way to do this is create a per-row trigger that will guarantee that on DELETE events the 'default' category row will never be deleted.
Please check out PostgreSQL's documentation about triggers and stored procedures:
http://www.postgresql.org/docs/8.3/interactive/trigger-definition.html
http://www.postgresql.org/docs/8.3/interactive/plpgsql.html
There's also valuable examples in this wiki:
http://wiki.postgresql.org/wiki/A_Brief_Real-world_Trigger_Example
You could have a row in another table (called defaults) referencing the default category. The FK constraint would not let the deletion of the default category happen.
Keep in mind how triggers work. They will fire off for every row your delete statement will delete. This doesn't mean you shouldn't use triggers just keep this in mind and most importantly test your usage scenarios and make sure performance meets the requirements.
Should I use a rule or a trigger?
From the official docs:
"For the things that can be implemented by both, which is best depends on the usage of the database. A trigger is fired for any affected row once. A rule manipulates the query or generates an additional query. So if many rows are affected in one statement, a rule issuing one extra command is likely to be faster than a trigger that is called for every single row and must execute its operations many times. However, the trigger approach is conceptually far simpler than the rule approach, and is easier for novices to get right."
See the docs for details.
http://www.postgresql.org/docs/8.3/interactive/rules-triggers.html