Impedir a inserção de linhas infantis adicionais
-
22-09-2019 - |
Pergunta
Meu aplicativo envolve o uso de dados de envio (a "solicitação") de um formulário em um banco de dados SQL Server 2005, para revisão e aprovação posteriores por um supervisor. Os usuários devem ter permissão para inserir uma nova solicitação, mas não conseguir modificar os que já enviaram.
Com uma única tabela, isso é direto: conceda a eles o Insira apenas privilégio sem o privilégio de atualização. Mas o pedido realmente abrange duas mesas, com um relacionamento um para muitos. Preciso impedir que o usuário inserisse linhas infantis adicionais para uma solicitação existente. Idealmente, isso deve ser aplicado no nível do banco de dados: permita que uma linha pai e uma ou mais linhas infantis sejam inseridas no mesma transação, mas uma vez que essa transação é comprometida, impede que novas linhas infantis sejam inseridas com essa chave estrangeira.
Qual é a melhor maneira de conseguir isso? Existem maneiras de aplicar esse sabor especial de "integridade referencial" sem gatilhos? E se os gatilhos são apenas da maneira, como posso testar que a linha pai foi inserida na transação atual?
Solução
Procedimento armazenado ou gatilho
Se você conceder direitos sobre a tabela infantil, eles poderão escrever.
Um gatilho não permitirá a gravação e um procedimento armazenado permite impedir a gravação em primeiro lugar, porque apenas o Procrações armazenadas grava nas tabelas.
Não existe uma integridade referencial "nativa" que possa capturar sua lógica de negócios porque é personalizada para sua situação.
Outras dicas
O exemplo a seguir ilustra como você pode usar um gatilho para alcançar esse comportamento. Observe que isso não funcionará se as linhas infantis forem inseridas uma de cada vez dentro da transação, e não em um único INSERT
declaração.
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 procedimentos armazenados para inserir dados:
O procedimento armazenado primeiro insere a linha dos pais; Ele adiciona informações automaticamente sobre a inserção do usuário e define o campo de status,
O segundo procedimento armazenado insere as linhas infantis depois de verificar a linha dos pais; Isso levanta o erro se o usuário de chamada não tiver o direito de adicionar itens à linha de pai ou do status da linha pai ou da linha dos pais, não compensa a adição de novas posições.
Como alternativa, você pode usar gatilhos para fazer as verificações. Mas isso pode ser um pouco mais complicado do que chamar explicitamente os procedimentos armazenados. E as pessoas às vezes tendem a esquecer os gatilhos.