ON CASCADE Considere as tabelas criadas na aula anterior criadas com os seguintes comandos:
CREATE TABLE Clientes (
id_cliente INT AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(100) NOT NULL
);
CREATE TABLE Pizzas (
id_pizza INT AUTO_INCREMENT PRIMARY KEY,
nome VARCHAR(100) NOT NULL,
preco DECIMAL(10,2) NOT NULL
);
CREATE TABLE Pedidos (
id_pedido INT AUTO_INCREMENT PRIMARY KEY,
id_cliente INT NOT NULL,
id_pizza INT NOT NULL,
CONSTRAINT FK_Cliente FOREIGN KEY (id_cliente) REFERENCES Clientes(id_cliente),
CONSTRAINT FK_Pizza FOREIGN KEY (id_pizza) REFERENCES Pizzas(id_pizza)
);
Podemos ver que a tabela de pedidos tem duas chaves estrangeiras: id_cliente e id_pizzas. Sabemos que isto quer dizer que a coluna id_cliente da tabela Pedidos faz uma referência à coluna id_cliente da tabela Clientes.
Mas o quê essas restrições influenciam na prática?[1]
Essas restrições previnem que existam dados inválido no bando de dados.
As consequências são duas restrições:
Não podemos incluir um pedido com um id_pizza que não existe.
Da direção da tabela filha (aquela que tem a restrição) para a tabela pai. Na tabela filha não podemos inserir uma entrada com uma referência à algo que não existe na tabela pai.
Vamos tentar incluir um Pedido e ver qual é a resposta do banco de dados:
INSERT INTO Pedidos(id_cliente, id_pizza)
VALUES (10, 1);
1452 - Cannot add or update a child row: a foreign key constraint fails (`bd_pizzaria_napoli`.`pedidos`, CONSTRAINT `FK_Cliente` FOREIGN KEY (`id_cliente`) REFERENCES `clientes` (`id_cliente`))
Ou seja: Cannot add or update a child row: a foreign key constraint fails: (não podemos adicionar ou atualizar uma linha filha: falha de chave estrangeira.)
Não podemos deletar ou modificar o id_cliente de um cliente que tenha, pelo menos, um pedido fazendo referência a ele.
Da direção da tabela pai (aquela para a qual alguma tabela faça referência) para a tabela filha. Na tabela pai não podemos modificar uma entrada onde alguma tabela filha faça referência a ela.
Vamos ver uma tentativa de modificação:
UPDATE Pizzas
SET id_pizza=10
WHERE id_pizza=1;
1451 - Cannot delete or update a parent row: a foreign key constraint fails (`bd_pizzaria_napoli`.`pedidos`, CONSTRAINT `FK_Pizza` FOREIGN KEY (`id_pizza`) REFERENCES `pizzas` (`id_pizza`))
Neste caso Cannot delete or update a parent row: a foreign key constraint fails: (não podemos deletar ou modificar uma linha pai: uma chave estrangeira faz referência a ela)
Se tentarmos deletar teremos a mesma resposta.
DELETE FROM Pizzas
WHERE id_pizza=1;
#1451 - Cannot delete or update a parent row: a foreign key constraint fails (`bd_pizzaria_napoli`.`pedidos`, CONSTRAINT `FK_Pizza` FOREIGN KEY (`id_pizza`) REFERENCES `pizzas` (`id_pizza`))
A restrição que o banco de dados nos impõe é muito útil para proteger a consistência dos dados. Mas será que é possível uma situação onde precisamos anular estas restrições?
A primeira restrição não queremos resolver pois nunca será válido inserir uma linha que faz referência à uma entrada que não existe.
Porém, para a segunda podemos ter que pensar em uma solução.
E se precisarmos modificar ou apagar dados da tabela pai, como apagar uma pizza? Ou modificar o id de uma pizza?
Para isto teremos os comando ON CASCADE.
ON DELETE e ON UPDATE Quando definimos as restrições na tabela podemos escolher algumas opções em relação a apagar ON DELETE e à modificação ON UPDATE[2]
[CONSTRAINT [symbol]] FOREIGN KEY
[index_name] (col_name, ...)
REFERENCES tbl_name (col_name,...)
[ON DELETE reference_option]
[ON UPDATE reference_option]
reference_option:
RESTRICT | CASCADE | SET NULL
Vamos ver cada uma das opções:
CASCADE: Quando a linha da tabela pai for modificada ou apagada, esta operação será replicada em todas tabelas filhas
SET NULL: Quando a linha da tabela pai for modificada ou apagada, as referências da tabela filha serão valoradas para NULL. Se você escolher esta opção, se certifique que o valor da tabela filha não está com a restrição de NOT NULL.
RESTRICT: Rejeita as operações que entrarem em conflito. Está é a opção padrão, a mesma coisa que não escolher nenhuma.
Vamos criar um banco de dados chamado bd_pizzaria_napoli_cascade com as mesmas características da aula anterior, mas com as restrições de chave estrangeira com os valores ON DELETE CASCADE e ON UPDATE CASCADE:
CREATE TABLE Pedidos (
id_pedido INT AUTO_INCREMENT PRIMARY KEY,
id_cliente INT NOT NULL,
id_pizza INT NOT NULL,
CONSTRAINT FK_Cliente
FOREIGN KEY (id_cliente)
REFERENCES Clientes(id_cliente)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT FK_Pizza
FOREIGN KEY (id_pizza)
REFERENCES Pizzas(id_pizza)
ON DELETE CASCADE
ON UPDATE CASCADE
);
Agora vamos tentar as mesmas modificações.
No primeiro caso vamos tentar colocar uma entrada em Pedidos com um id inexistente:
INSERT INTO Pedidos(id_cliente, id_pizza)
VALUES (10, 1);
Ainda teremos o mesmo erro, o que é bom, nunca queremos que seja possível fazer uma entrada com a referência quebrada.
#1452 - Cannot add or update a child row: a foreign key constraint fails (`bd_pizzaria_napoli_cascade`.`pedidos`, CONSTRAINT `FK_Cliente` FOREIGN KEY (`id_cliente`) REFERENCES `clientes` (`id_cliente`) ON DELETE CASCADE ON UPDATE CASCADE)
Na segunda restrição esperamos que consigamos modifica ou apagar sem causar uma inconsistência do banco de dados.
Então vamos ver a tabela pedidos e clientes
| id_cliente | nome |
|---|---|
| 1 | Ana |
| 2 | Beto |
| 3 | Carla |
| id_pedido | id_cliente | id_pizza |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 1 | 1 |
| 3 | 1 | 2 |
| 4 | 2 | 1 |
Vamos tentar mudar o id_cliente da Ana para 10.
UPDATE Clientes
SET id_cliente=10
WHERE id_cliente=1;
| id_cliente | nome |
|---|---|
| 2 | Beto |
| 3 | Carla |
| 10 | Ana |
| id_pedido | id_cliente | id_pizza |
|---|---|---|
| 1 | 10 | 1 |
| 2 | 10 | 1 |
| 3 | 10 | 2 |
| 4 | 2 | 1 |
Podemos ver que a modificação foi propagada para a tabela pedidos.
Agora vamos tentar apagar a cliente Ana.
DELETE FROM Clientes
WHERE id_cliente=10;
| id_cliente | nome |
|---|---|
| 2 | Beto |
| 3 | Carla |
| id_pedido | id_cliente | id_pizza |
|---|---|---|
| 4 | 2 | 1 |