Prevent insertion of additional child rows
My application involves using submitting data (the "request") from a form into an SQL Server 2005 database, for later review and approval开发者_如何学Python by a supervisor. Users should have permission to insert a new request, but not be able to modify the ones they have already submitted.
With a single table, this is straightforward: grant them the INSERT privilege only without the UPDATE privilege. But the request actually spans two tables, with a one-to-many relationship. I need to prevent the user from inserting additional child rows for an existing request. Ideally, this should be enforced at the database level: allow a parent row and one or more child rows to be inserted in the same transaction, but once that transaction is committed prevent new child rows from being inserted with that foreign key.
What's the best way to achieve this? Are there any ways to enforce this special flavour of "referential integrity" without triggers? And if triggers are they only way, then how can I test that the parent row has been inserted within the current transaction?
Stored procedure or trigger
If you grant rights on the child table, then they'll be able to write.
A trigger will disallow the write and a stored procedure allows you to prevent the write in the first place because only the stored proc writes to the tables.
There is no "native" referential integrity that can capture your business logic because it's custom to your situation.
The following example illustrates how you could use a trigger to achieve this behaviour. Note that this will not work if the child rows are inserted one at a time inside the transaction, rather than in a single INSERT
statement.
CREATE TABLE parent1
(id INT PRIMARY KEY)
CREATE TABLE child1
(id INT
,parent_id INT
)
GO
ALTER TABLE child1 ADD CONSTRAINT chilld1fk FOREIGN KEY (parent_id)
REFERENCES parent1 (id)
GO
CREATE TRIGGER trg_child1
ON child1
INSTEAD OF INSERT
AS
SELECT parent_id
FROM child1 AS c
WHERE EXISTS (SELECT 1
FROM inserted AS i
WHERE i.parent_id = c.parent_id
)
IF @@ROWCOUNT > 0
BEGIN
RAISERROR('You cannot amend this request',16,1)
END
ELSE
BEGIN
INSERT child1
SELECT id
,parent_id
FROM inserted
END
GO
BEGIN TRAN
INSERT parent1
VALUES (1)
INSERT child1
(id
,parent_id
)
SELECT 10,1
UNION SELECT 11,1
COMMIT
-- attempting to insert another child outside the transaction
-- will result in an error
INSERT child1
SELECT 12,1
SELECT * FROM child1
Use stored procedures to insert data:
first stored procedure inserts parent row; it automatically adds information about inserting user and sets status field,
second stored procedure inserts child rows after checking its parent row; it raises error if calling user has no right to add items to given parent row or parent row's status disallows adding new positions.
Alternatively you can use triggers to do the checks. But this can be somewhat trickier than explicitly calling stored procedures. And people sometimes tend to forget about triggers.
精彩评论