Disable DELETE for a table in SQL Server - sql

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

Related

How to delete records of multiple tables in one go? Sqlite

For a project i need to to delete all data from all tables in a database,
I tried
DELETE FROM table1,table2,table3,...;
But it doesnt work. Any advice ? Thank
I would like to refer You to this related post
How do I use cascade delete with SQL Server?
as You will find there several possible solutions.
When using SQL it means that Your data is relations, which means most of the records are somehow related in the different tables and this relation is expressed with foreign keys. However when attempting to delete data which is id is related with data in another table a cascade deletion should be implemented, the other way around it is add additional boolean column named isDeleted(as example ofcourse) and just alter specific rows to true in this specific column and then filter by preferences. Hopefully I have managed somehow to provide with alternative and/or possible solution to Your problem.
Leaving also this link which gives some examples on cascade deletion and guide on how to implement it. ->
https://www.sqlshack.com/delete-cascade-and-update-cascade-in-sql-server-foreign-key/
P.S. also if You want just to DELETE all the data You can either use TRUNCATE TABLE or DROP DATABASE query. With the latest option You will have to recreate the database once more.
Because you want to delete all databases from all tables, you are essentially deleting the database.
The sqlite way of doing this is to just delete the database file.
In contrast to postgres or mongodb or most databases, sqlite doesn't have a server so no need to call DROP DATABASE.
As soon as the file is gone and you open the database from your application you will be in a blank state again.

SQL Foreign Key in Another Database

I am currently creating a database at work that is essentially a child of another database. When our clients get our software they get a database, lets call it MasterBase. This database contains schema of two different smaller databases put together, lets call them MasterC and MasterF.
My job is to take all the schema that pertain to MasterF and put them in their own database. The problem is there are a lot of references to tables and views in MasterC. To work around this I will add a property that will pair MasterF with MasterC so that in my .NET solution I should be able to do things like:
Table tableName.... (table being created in MasterF)
References .... MasterC.dbo.tableC (table in MasterC)
However, in SQL this raises the error: MSG 1763 Cross-Database foreign key references are not supported.
I have read the most common work around is to add a trigger, but seeings how this is not that safe and there are MANY instances of this I was wondering if there was another way, perhaps a stored procedure or something of the sort.
If a trigger is really the best/only way and I MUST add a trigger to every table that has this problem, how would I go about writing this trigger, I know a little SQL but am hardly proficient.
Please Help!
Triggers (as you mention)...
Database Partitioning...
Duplicate the tables from MasterC (are they transactional? How often are they updated? Does MasterC need the updates? If so, can you allow for the copies to be updated every 24-hours, etc. via a "Job"?)
Per your last comment about individual builds, I guess I'd say that the threshold for when a client "needs" both is when they...well, "need" both. Sounds almost like you need a 3rd schema, for tables in MasterC that do NOT reference MasterF, for those clients you're categorizing as not needing both.
BTW -- this is all about referential integrity, yes? There are other strategies you could employ toward that goal, besides 'relationships'.
EDIT
CREATE TRIGGER myTrigger ON myTable
AFTER INSERT
AS
IF NOT EXISTS (SELECT * FROM OtherDatabase.otherschema.othertable F
JOIN inserted AS i
ON F.KeyYouAreLookingFor = i.KeyYouHave)
BEGIN
RAISERROR ('Lookup Value Not Found -- Insert Failed', 16, 1);
ROLLBACK TRANSACTION;
RETURN
END;

How to Logically DELETE a record in SQLite

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

Keep a shadow copy of a table while retaining records removed from the original

This is probably laughably easy for an SQL expert, but SQL (although I can use it) is not really my thing.
I've got a table in a DB. (Let's call it COMPUTERS)
About 10.000 rows. 25 columns. 1 unique key: Column ASSETS.
Occasionally an external program will delete 1 or more of the rows, but isn't supposed to do that, because we still need to know some info from those rows before we can really delete the items.
We can't control the behavior of the external application so we came up with a different idea:
We want to create a second identical table (COMPUTERS_BACKUP) and initially fill this with a one-on-one copy of COMPUTERS.
After that, once a day copy new records from COMPUTERS to COMPUTERS_BACKUP and update those records in COMPUTERS_BACKUP where the original in COMPUTERS has changed (ASSETS column will never change).
That way we keep the last state of a record deleted from COMPUTERS.
Can someone supply the code for a stored procedure that can be scheduled to run once a day? I can probably figure this out myself, but it would take me several hours or so and I'm very pressed for time.
just create a trigger for insert computers table
CREATE TRIGGER newComputer
ON [Computers]
AFTER INSERT
Begin
INSERT INTO COMPUTERS_BACKUP
SELECT * FROM Inserted
End
It'll work when you insert new computer to computers table and it'll also insert the record to bakcup table
When you update computers you could change computers backup too with update trigger
CREATE TRIGGER newComputer
ON [Computers]
AFTER UPDATE
Begin
//can access before updating the record through SELECT * FROM Deleted
//can access after updating the record through SELECT * FROM Inserted
UPDATE Computers_BACKUP SET
(attributes) = inserted.(attribute)
WHERE id = inserted.id
End
At the end I guess you don't want to delete the backup when original record is deleted from computers table. You can chech more examples from msdn using triggers.
When a record removed from computers table
CREATE TRIGGER computerDeleted ON [Computers] AFTER DELETE
Begin
INSERT INTO Computers_BACKUP
SELECT * FROM Deleted
End
Besides creating triggers, you may look into enabling Change Data Capture, which is available in SQL Server Enterprise Edition. It may be an overshot, but it should be mentioned and you may find it useful for other tables and objects.
IMHO a possible solution, if you never delete records (only update) from that table in your application, can be to introduce an INSTEAD OF DELETE trigger
CREATE TRIGGER tg_computers_delete ON computers
INSTEAD OF DELETE AS
DELETE computers WHERE 1=2;
It will prevent the deletion of the records.
Here is SQLFiddle demo.
A trigger for Before Delete event can help you to guard this table:
CREATE TRIGGER backup_row_before_delete ON COMPUTERS_Table FOR Delete
as
INSERT INTO Computers_Backup
SELECT deleted.* from deleted
You can change deleted.* for deleted.col1, deleted.col2 if you want to keep certain columns only.
will delete 1 or more of the rows, but isn't supposed to do that
Then you have permission and integrity issues.
You can most certainly use a trigger to record deletions (and updates of course) but I would not recommend you use it purely to keep a copy of stuff you didn't want deleted in the first place!
Remove delete permissions if you have to or beef up your data integrity if you can. Without your schema it's hard to tell exactly how though.
Finally, use your (INSTEAD OF) trigger to check whatever conditions you need to prevent the delete when appropriate.

Is it Possible to Enforce Read-Only Fields in SQL Server 2008 R2?

I'd want to prevent UPDATE querys that write over ceartain fields in my database. I also want to prevent UPDATE or DELETE querys of any kind on certain tables, as these tables contain permanent information that needs to persist indefinitely.
Can these configurations be implemented in SQL Server Management Studio?
In order to prevent updates on certain fields, you'll probably have to have an AFTER UPDATE trigger on that table that would check if any of the "read-only" fields is about to be updated; if so, abort the transaction.
CREATE TRIGGER triggerName
ON dbo.YourTable AFTER UPDATE
AS
IF UPDATE(somefield)
ROLLBACK TRANSACTION
To prevent certain users access to certain tables, just don't grant those users (or a database role they belong to) the UPDATE and/or DELETE permission on those tables.
Remove all permissions for read only tables. No-one can make changes then
Consider stored procs or views to control writes, again remove direct permissions
See marc_s answer
If your users connect as dbo or sa then there is nothing you can do that is effective. Users can disable trigegrs or delete them. With sa permissions are never checked anyway.
#marc_s is right, if you want to prevent changes in some fields you should use a trigger or limit the user's permissions if you are the dba. In any case, instead of an AFTER trigger i would use an INSTEAD OF trigger, so it wouldn't be necessary to rollback the transaction.
CREATE TRIGGER triggerName
ON dbo.YourTable INSTEAD OF UPDATE, INSERT
AS
IF UPDATE(somefield)
-- do nothing or whatever
In any case, if you find this answer correct, please accept the answer provided by #marc_s, this is just something extra over that answer