How to Logically DELETE a record in SQLite - sql

I would like to mark a record as deleted instead of actually deleting a record.
My intention is to use an instead of trigger, but I am getting an SQLException that neither I nor Google know how to solve this.
My code:
CREATE TRIGGER IF NOT EXISTS <Trigger>
INSTEAD OF DELETE ON <Table>
FOR EACH ROW
BEGIN
UPDATE <Table>
SET Status = 'D'
WHERE ID = old.ID;
END
My Error:
java.sql.SQLException: cannot create INSTEAD OF trigger on table: main.<Table>
at org.sqlite.NativeDB.throwex(NativeDB.java:210)
at org.sqlite.NativeDB._exec(Native Method)
at org.sqlite.Stmt.executeUpdate(Stmt.java:152)
Assist me, please?
EDIT:
What I really wanted was to activate foreign key enforcement.
Refer here: How do you enforce foreign key constraints in SQLite through Java?

You cannot use INSTEAD OF triggers on tables, and when RAISE-ing an error in BEFORE/AFTER triggers, any updates done in the trigger would also be rolled back.
You could rename your table, create a view for that table, and create lots of INSTEAD OF triggers to implement all the INSERT/UPDATE/DELETE operations.
However, it would be much easier to change your program to just execute the UPDATE when it wants to mark some record.

Instead of triggers are intended for use with views so that you can specify the underlying tables that an action should be carried out on when an insert, update our delete is issued on the view itself.
One thing you could try is to do a before delete trigger then raise an exception. The only thing is I'm not sure if this would also interfere with the update. Maybe worth a try though:
SELECT RAISE(ABORT, 'Prevent delete');

Related

Disable DELETE for a table in SQL Server

I'm currently working on creating a table for customers in an order management system for a course at university. The system relies on being able to retrieve order histories for the customers. Since we've chosen not to store these in a separate table, removing the option to delete rows from our customer table is essential.
How and where do I set this up in the CREATE statement? I suspect I'll have to create a rule about what should happen instead, but I'm not entirely sure about the specifics.
On SQL Server, you have the following options:
Deny object permissions. For example: DENY DELETE ON OBJECT::dbo.Customer TO db_datawriter;
Raise an error in a trigger: CREATE TRIGGER nodelete1 ON dbo.Customer INSTEAD OF DELETE AS RAISERROR('You can't delete from this table', 16, 10)
Rely on referential integrity without cascading updates/deletes. Note that this will only prevent deletion of a customer, if the customer has at least 1 order.
In my honest opinion, however, I think that this should be solved at the application level and not the database level. Even if using the techniques above, what would prevent someone from simply removing the trigger or grant the necessary permissions before DELETE'ing the records? Or simply dropping the entire table?
If you don't want your users to delete records from a table, simply make sure that your application does not allow them to do that. Anyone working directly with the database should know that issuing a DELETE statement could be dangerous - especially if you don't have a backup.
If the table is accessed only through an application, you can use a soft delete, to do that add a column to the table, for example IsDeleted, and check it in the software to see whether the row is live or deleted.
If the final users can access the DB, you can to change his/her login or group permission to remove the delete grant on that table.
i do it with a simple trigger on each table that i want disable delete
Create Trigger [dbo].[RollBackDelete]
ON [dbo].[Your Table Name]
INSTEAD OF DELETE
AS
BEGIN
ROLLBACK;
END
ofcourse if you have any key with cascade operation on delete or update it can not be work and you should set it to "No Action"
i hope this be be useful

nhibernate audit trigger error

I'm using triggers on my sql database to capture change information for a table, it seems to be having a problem with nhibernate though.
The table has a few columns and primary keys and triggers on it. The triggers look like this
CREATE TRIGGER [dbo].[tr_Instrument_update] ON [dbo].[Instrument] FOR UPDATE AS
BEGIN
INSERT [MyAudit].[audit].[Instrument]
SELECT 'Updated', i.*
FROM inserted
INNER JOIN [MyAudit].[dbo].[Instrument] i ON inserted.[InstrumentID] = i.[InstrumentID]
END
Basically on every change we copy the row into the audit table. I have tested and if I modify the data directly through sql management studio triggers function correctly and I get data written to the audit table, however if i update through my app I get the following:
NHibernate.StaleObjectStateException
was unhandled by user code
Message=Row was updated or deleted by
another transaction (or unsaved-value
mapping was incorrect)
I assume this is because the trigger updates another table in another database, is there anyway to make nhibernate ignore this as the change will not affect any of its data, in our mappings we have no reference to this audit data.
Figured out that the trigger was causing Nhibernate to do two identical update calls for some reason. The solution was to SET NOCOUNT ON inside the trigger. Still not sure though why nhibernate makes two updates!

FOR/AFTER in SQL triggers

I am newbie in SQL. I am reading about Triggers in SQL.I have got almost about Triggers. But in DML Triggers, we use FOR/AFTER keyword. I didn't get difference between FOR/AFTER and why we use FOR/AFTER keyword. I have already read on MSDN but didn't get the simple answer.
Can anyone explain me what is it?
Thanks in advance.
There is no difference between using FOR and AFTER.
I believe the original (pre 2000) syntax only used the FOR keyword. However, when INSTEAD OF triggers were introduced, the "FOR" keyword could seem quite confusing. "AFTER" more accurately conveys the type of trigger, and is more easily distinguished from "INSTEAD OF".
An INSTEAD OF trigger would be used if we wanted to transform what was inserted into the table, or prevent an insertion from taking place.
An AFTER trigger would more normally be used if we wanted to perform additional tasks, based on what has just occurred. For instance, you could have an "AFTER DELETE" trigger, that copied deleted rows into some kind of archive table. Basically, in an AFTER trigger, you more normally do still want the activity to occur.
From MSDN:
AFTER triggers are never executed if a constraint violation occurs; therefore, these triggers cannot be used for any processing that might prevent constraint violations.
And then:
You can request AFTER triggers by specifying either the AFTER or FOR keywords. Because the FOR keyword has the same effect as AFTER, DML triggers with the FOR keyword are also classified as AFTER triggers
It would seem there is no difference.
If I interpret your comments to the other answers correctly, you want to know why or when one uses the "FOR|AFTER" keywords.
It's simple: there are two kinds of triggers, the AFTER-trigger and the INSTEAD-OF-trigger.
The INSTEAD-OF-trigger for e.g. an insert action can be written as
create trigger myTrigger on myTable
INSTEAD OF insert
begin
(... code goes here ...)
end
and the AFTER-trigger can be written as either
create trigger myTrigger on myTable
AFTER insert
begin
(... code goes here ...)
end
or
create trigger myTrigger on myTable
FOR insert
begin
(... code goes here ...)
end
As Damien_The_Unbeliever mentions, the AFTER keyword is more readable than the FOR version, that is all.
They are the same. See this excerpt from BOL
"
FOR | AFTER
AFTER specifies that the DML trigger is fired only when all operations specified in the triggering SQL statement have executed successfully. All referential cascade actions and constraint checks also must succeed before this trigger fires.
AFTER is the default when FOR is the only keyword specified.
AFTER triggers cannot be defined on views.
"
According to what I observe, FOR is used in DDL trigger while AFTER is used in DML triggers. They have same way of working.

Should I use the template from MS SQL Management Studio to create new triggers?

If you create a new trigger in MS SQL Management Studio by using the GUI, it gives you this template:
--====================================
-- Create database trigger template
--====================================
USE <database_name, sysname, AdventureWorks>
GO
IF EXISTS(
SELECT *
FROM sys.triggers
WHERE name = N'<trigger_name, sysname, table_alter_drop_safety>'
AND parent_class_desc = N'DATABASE'
)
DROP TRIGGER <trigger_name, sysname, table_alter_drop_safety> ON DATABASE
GO
CREATE TRIGGER <trigger_name, sysname, table_alter_drop_safety> ON DATABASE
FOR <data_definition_statements, , DROP_TABLE, ALTER_TABLE>
AS
IF IS_MEMBER ('db_owner') = 0
BEGIN
PRINT 'You must ask your DBA to drop or alter tables!'
ROLLBACK TRANSACTION
END
GO
Should I use this template?
I dont know anything about triggers, but I think I need to use them. The purpose in this case is that on an insert to the table, I need to update one of the fields.
Please help me get started!
OK to begin with that is the wrong template if you want an ordinary trigger that one is a trigger on making structural changes to the table itself.
If you decide to do a trigger that affects data (as opposed to structure), there are several things you need to know. First and by far the most critical, triggers operate on sets of data not one row at time. You must write any trigger to handle multiple row inserts.updates or deletes. If you end up with any code setting the value in inserted or deleted to a variable, there is a 99% chance it will not work properly if multiple records are involved.
What is inserted or deleted you ask? That is the next thing you need to know about triggers, there are two pseudotables (inserted and deleted) that are only available in a trigger (or an output clause) which contain the new information being inserted or the updated values (in the inserted table) and the old information being deleted or being changed by an update (in the deleted table). So an insert has values in inserted, a delete has values in deleted and an update has values in both. Use these in your trigger to pull the values you need to change.
Since you don't know anything about triggers, I would say no, don't use the template.
Read the books online page for Create Trigger and write the trigger by hand.
There is probably more in that template code than you actually need. Read the manual and keep it simple.
If you don't know anything about triggers then I would strongly suggest that you read up on them before implementing them. Get Triggers right and they can make your life a lot easier; get it wrong and Triggers will cause you a lot of trouble.
I would suggest starting off with this tutorial
http://www.sqlteam.com/article/an-introduction-to-triggers-part-i
You can use the above SQL as a template or you can simply write your own. I would suggest you write your own as you'll understand what you are doing. Obviously only do this after you have done some serious reading on triggers. Check out MSDN too

Adding trigger to table with cascades

I'm trying to add a simple trigger to a table- the 1st issue i came accross was that this table has text columns - so the for delete, insert, update triggers aren't going to float. 'instead of' does though.
I am now up against the fact that the table has cascades set on it. Do you know if there's a way to get around that little gem or am I basically fubared?
Create a new table, which everyone uses instead of the cascading table. Then build your "instead of" trigger onto the new table, and update the old table within the trigger.
The old table will cascade as normal, but your new table doesn't have any cascades.
UPDATE:
You could try adding a view rather that creating another table. You could even exclude those text columns from the view.
I don't know what version of SQL Server you are on but text columns are deprecated - they will NOT be in the next version of SQL Server. If you are on any version higher than 2000, I would suggest you make it an immediate prioroity to fix those by making them nvarchar(max) (You will also need to change code that uses CONTAINS, WRITETEXT and other text type code).
That said, I always got the value of text column in a trigger by joining inserted to the actual table itself on the primary key.
I'm not sure what to do about your cascade question as we do not allow cascade delete or update for performance reasons. As far as I can tell triggers will still fire (and should definitely be written to handle multiple record inserts, updates or deletes, but I strongly feel all triggers should be written this way). What problem exactly are you running into with the cascades?