A linguagem de montagem de consultas SQL é uma linguagem com alto poder de expressão, ou seja, com ela podemos fazer manipulações complexas nos dados utilizando poucos comandos. A linguagem SQL, que trabalha com as tabelas definidas no modelo físico de banco de dados, possui várias funcionalidades. Uma das funcionalidades mais complexas e poderosas são os joins, que permitem manipulações nos dados contidos em mais de uma tabela ao mesmo tempo, sempre de acordo com algum relacionamento entre as tabelas. Estes relacionamentos devem ser definidos no modelo lógico do banco de dados, durante a modelagem do mesmo.

Neste artigo vamos discutir a atualização automática de mais de uma tabela e o uso de joins em instruções UPDATE e DELETE através de exemplos práticos.

Atualização automática

Os joins são aplicados, na maioria das vezes, durante uma instrução SQL chamada SELECT, que faz o retorno de dados. Na instrução SELECT devemos definir as tabelas nas quais os dados estão armazenados assim como quais os valores de colunas vão ser retornados, além das condições necessárias para o retorno dos dados. Como mais de uma tabela vai ser utilizada na instrução, devemos representar a relação entre as colunas das tabelas através das opções da cláusula join, aplicada à instrução SELECT.

A utilização de join em instruções SELECT é o uso mais comum do join. Mas existem situações onde queremos modificar, ou mesmo apagar, dados de determinadas tabelas com bases nas suas relações com outras tabelas. Como as relações entre as tabelas são especificadas no modelo lógico do banco de dados, e reforçadas pela implementação de constraints, existem casos em que certas ações são propagadas automaticamente, o que chamamos de ações em cascata.

Vamos ver um exemplo. Suponham que temos o diagrama Entidade Relacionamento apresentado na Figura 1, criado em um banco de dados do SQL Server 2000. A tabela ITENSPEDIDOS possui uma chave primária composta nas colunas PED_COD e PROD_COD. Existem duas chaves estrangeiras na tabelas ITENSPEDIDO. A primeira chave estrangeira relaciona a coluna PROD_COD da tabela ITENSPEDIDOS com a coluna PROD_COD da tabela PRODUTOS. A segunda chave estrangeira relaciona a coluna PED_COD, da tabela ITENSPEDIDOS, com a coluna PED_COD da tabela PEDIDOS.

sql-26-06-2008pic01.JPG
Figura 1. Relacionamento entre as tabelas PEDIDOS, ITENSPEDIDOS e PRODUTOS

As chaves estrangeiras da tabela ITENSPEDIDOS foram criadas com a opção de atualização e deleção em cascata. Esta opção funciona da seguinte maneira: se alguma instrução UPDATE alterar o valor da coluna PED_COD, em alguma linha da tabela PEDIDOS, este novo valor vai ser alterado nas linhas correspondentes (que possuem o mesmo valor) da tabela INTENSPEDIDOS, desde que não haja violação da chave primária composta da tabela ITENSPEDIDOS.

Mas o inverso não é verdadeiro, ou seja, se o valor da coluna PED_COD da tabela ITENSPEDIDOS for alterado para um valor que não existe na coluna PED_COD, da tabela PEDIDOS, teremos um erro de violação de chave estrangeira. Esta regra funciona de maneira análoga para a coluna PROD_COD, da tabela PRODUTOS e da tabela ITENSPEDIDOS.

Relacionando tabelas

Discutimos até agora a atualização automática em duas colunas de tabelas distintas com uma única instrução. Esta atualização foi feita nas colunas que são responsáveis pelo relacionamento entre as tabelas e que fazem parte da chaves primárias e das chaves estrangeiras. Mas, e se desejarmos atualizar os valores de uma coluna que não faz parte dasconstraints, levando em consideração as relações da tabela que contém a coluna? Para estes casos podemos utilizar duas abordagens: o uso de subquerys ou o uso de joins.

Utilizando como exemplo as tabelas do diagrama da Figura 1, vamos supor que, por um erro de sistema, todos os produtos que foram utilizados em algum pedido possuem o valor com 10% a mais. Devemos retirar 10% do valor de cada produto que esteja em algum pedido. Para atualizar a tabela PRODUTOS vamos fazer um relacionamento na instrução UPDATE, de modo a obter somente os produtos que constam em algum pedido. A Listagem 1 mostra primeiro a instrução UPDATE que faz a atualização na tabela PRODUTOS utilizando uma subquery. Na seqüência, a instrução UPDATE utiliza um join que relaciona as tabelas PRODUTOS e ITENSPEDIDOS.

Duas abordagens para a atualização do preço de produtos
Listagem 1. Duas abordagens para a atualização do preço de produtos

Notem que na abordagem que utiliza a subquery a função IN() verifica se o valor de uma coluna está contido em um conjunto de valores, retornados por um SELECT na tabela ITENSPEDIDOS.

Na abordagem que utiliza join o relacionamento entre as tabelas PRODUTOS e ITENSPEDIDOS foi feito pelo tipo de join chamado equi-join, onde somente as linhas que possuírem valores idênticos para as colunas PROD_COD serão retornadas.

Comparando as duas abordagens, podemos dizer que a utilização de subquerys torna a instrução mais fácil de ser lida e compreendida. Se desejarmos referenciar mais de duas tabelas na instrução UPDATE, a complexidade da instrução vai aumentar, pois para cada tabela devemos inserir uma nova subquerys e, em alguns casos, relacionar o conteúdo de cada subquery com a instrução UPDATE para alinhar todas as linhas das tabelas. Para utilizarmos colunas de outras tabelas na cláusula SET devemos criar mais subquerys que, dependendo da situação, devem ser relacionadas com a tabela que está sendo alterada. A abordagem que utiliza subquerys é considerada compatível com o padrão SQL-92 por utilizar somente recursos que podem ser executados em qualquer banco de dados que siga o padrão.

A abordagem que utiliza o join facilita o uso de mais de uma coluna para fazer a relação com as tabelas, nos casos de chaves primárias compostas, e também a utilização de colunas de qualquer tabela que for referenciada na cláusula SET da instrução. Para simplificar, utilizamos o alias P para a tabela PRODUTOS e o alias I para a tabela ITENSPEDIDOS, facilitando a identificação de qual coluna pertence a qual tabela na instrução. A abordagem que utiliza o join faz uso de extensões do Transact-SQL, um dialeto do SQL-92 utilizado pelo SQL Server.

No que diz respeito ao desempenho, tanto para o acesso aos dados como para o tempo de execução, geralmente recomenda-se a abordagem que utiliza join, pois isso indica para o otimizador de consultas que ele pode fazer uso de algoritmos e heurísticas mais apropriadas para a obtenção de um plano de execução melhor. Esta recomendação pode ser comprovada através de experimentos.

É importante notar que, nas duas abordagens, somente a tabela PRODUTOS foi alterada. Esta é uma característica da instrução UPDATE: mesmo com várias tabelas sendo utilizadas na instrução, somente colunas de uma tabela podem ser alteradas. A instrução DELETE permite que as linhas de uma única tabela sejam apagadas, quando utilizamos join na instrução DELETE.

Vamos ver agora um exemplo de instrução DELETE que relaciona mais de uma tabela. Vamos supor que desejamos apagar todos os produtos que não estejam em nenhum pedido. A Listagem 2 apresenta as duas abordagens para apagar os produtos.

Duas abordagens para apagar os produtos
Listagem 2. Duas abordagens para apagar os produtos

A abordagem que utiliza subquerys agora faz uso da negação do resultado da função IN(), que vai indicar se o valor de uma coluna não pertence a uma lista de valores.

Para a abordagem que utiliza join, foi feito o uso de um LEFT OUTER JOIN, tipo de join que relaciona todas as linhas da tabela à esquerda do comando JOIN mesmo que não haja correspondência dos valores com a tabela à direita do comando JOIN. Para obter somente os produtos que não fazem parte da de nenhum pedido, um filtro na coluna PROD_COD, da tabela ITENSPEDIDO, foi feito utilizando o operador IS NULL.

Uma dica: antes de executarem instruções UPDATE ou DELETE que relacionam tabelas, façam o teste em instruções SELECT, pois assim podemos testar se o conjunto de linhas que queremos alterar ou apagar está sendo selecionado corretamente.

Neste artigo discutimos a atualização de dados com base no relacionamento entre tabelas. As atualizações automáticas de linhas, através deconstraintsque permitem modificações em cascata, foram apresentadas assim como a utilização de relacionamentos entre tabelas nas instruções UPDATE e DELETE. Duas abordagens para a modificação em tabelas relacionadas foram apresentadas: a que utiliza join e a que utiliza subquery. As abordagens foram apresentadas e comparadas através de exemplos práticos.