views:

264

answers:

6

May be a stupid questions but can I protect a row of data in a SQL Server database from being deleted or updated without setting user permissions?

This is for a default row of data that can be referenced for its default values?

Thanks

+1  A: 

You could make a trigger that raises an error if that row is updated or deleted.

SLaks
This will not check perms automatically
DVK
Triggers are ignored when executing truncate table.
Simon Svensson
Don't you need a separate level of permissions to truncate table than to delete all rows? If so, he can permission that separately to be oof-limits to all users except admin (as it ought to be).
DVK
Just as I thought. After a quick Google, MSDN sez: "The minimum permission required is ALTER on table_name. TRUNCATE TABLE permissions default to the table owner, members of the sysadmin fixed server role, and the db_owner and db_ddladmin fixed database roles, and are not transferable. "
DVK
No, you can do this by relational integrity. Always avoid triggers unless you really need them
Cruachan
A: 

Let's say that your MyTable is on Primary.

Put the first row into a new table MyTableReadOnly, move that table to it's own File Group, and make the file group read only.

Remove the first row from the MyTable

Now create a view that

SELECT Columns From MyTableNew
UNION
SELECT Columns From MyTable

Access everything through the view. If you want to update or delete from the view, you can do that on MyTable and ignore anything for MyTableNew. If you want to work with the view, you can use INSTEAD-OF triggers.

Raj More
Two problems with this approach: You must maintain your view (e.g. the TDD instead of data) if the list of restricted rows changes. And you must create a new view for each separate set of perms (same drawback as my own approach as peter noted).
DVK
A: 
  • Create a second table that has rows with the same unique IDs as the rows you are trying to protect.

  • Permission that SECOND table as you wish

  • Add a trigger on the first table which will delete/update both tables if a matching row exists in the second table.

This way, unless you have a perm on the second table, you will not be able to modify the "linked" rows as the trigger will terminate due to permissions violation on the second table

NOTE: The contrast of this method with the other main methods (using VIEWs) is that it allows easy maintenance of the set of "fixed" rows unlike the VIEW approach and avoids various performance problems usually associated with views.

DVK
Just a note: to address Simon Svensson's concern regarding a different solution mentioning a trigger, table truncation is not an issue since it requires separate perms from row deletion.
DVK
This is nice. It allows you to grant permissions on a row set. Though, for each distinct (row set, permissions) pair, you would need another table. For example, if two users had row level permissions, but not on the same set of rows, you would need a separate control table for each. Of have I misunderstood the implementation?
Peter
Yes, that is the case. But this is the only approach I'm aware of **that uses native table level permissions**. Anything else requires either stored-in-a-table permissions (e.g. have the trigger check your userID against a table containing rowIDs mapped to permissions, possibly using DB's user groups if feasible or directly to user IDs if not).
DVK
You don't need to use a trigger, you can do this using a very similar approach but with relational integrity - triggers are great in their place but because keeping track of them is hard it's best to avoid them if you can. See my answer
Cruachan
The original question asks for a solution without setting user permissions.
Raj More
Raj - he asked about solution without setting user permissions on original table - presumably to avoid stopping users from maintaining other rows. So please remove your downvote if that was your reason.
DVK
@Cruachan - please see AlexKuznetsov comment on your answer. It prevents deletion but not updates - which the trigger solution can do.Also, I fail to see how a trigger is any more of a pain to create/maintain than a table constraint, it was never so in any DB environment I worked with.
DVK
+1  A: 

One possible approach that I once used is described in my blog post:

"Suppose that you need to enforce the following business rule: contracts cannot be changed after you have started working on them (let us assume that that particular business operates in the perfect world). You can use a ROWVERSION column, a persisted computed one, and a foreign key constraint to implement this rule - Using ROWVERSION to enforce business rules

AlexKuznetsov
That's a sweet technique.
Peter
A little overkill for his situation (since you would need the RowVersion column for all rows, not just the first row he is trying to protect), but it would work!
Peter
+1  A: 

Do this by relational integrity - do NOT use triggers as they're always a real pain to maintain afterwards (they have their place, just not here). Relation integrity will do everything you need.

Using relational integrity can be quite elegent, but what you need to do is slightly counter-intuitive so easily missed.

Create your main table table, tblMain, with a numeric primary key. For simplicity I tested this with a table with one column, intID, and I populated it with values 0,1 and 2.

Next create a second table, tblGuard, with a similar numeric primary key. I added one row into this table, value 1.

Now the reverse logic bit. Create a foreign key on the tblGuard table that reference the tblMain table

ALTER TABLE [dbo].[tblGuard] ADD 
    CONSTRAINT [FK_tblGuard_tblMain] FOREIGN KEY 
    (
     [intID]
    ) REFERENCES [dbo].[tblMain] (
     [intID]
    )

The constraint will ensure that the row with intID value 1 cannot be deleted from the tblMain table because the tblGuard table referential integrity requires that the value 1 exists in tblMain. This works with deletes and truncates.

Cruachan
This only prevent deletes, it does not protect your row from updates. The requirement is: "protect a row of data in a SQL Server database from being deleted or updated"
AlexKuznetsov
A: 

I say do this "programmatically". For example let the row with id 1 always be the default row, then add to all UPDATE or DELETE queries "WHERE id != 1" or do the equivalent in whatever language you are using to write your logic (PHP, C, VB, etc.)

rolf