Esta é uma versão arquivada/estática do antigo Blog do André. Isso significa que todo o conteúdo aqui presente não irá ser atualizado, e pode conter erros. Algumas funcionalidades poderão não estar disponíveis nesta versão arquivada.
02
Jul 2012

IDs sequenciais com PHP e MySQL (ou outros SGBD)

Por vezes, existe a necessidade de guardar um identificador numérico de forma sequencial para um determinado registo numa base de dados. Isto pode ser alcançado de várias formas, entre as quais o caso em que o ID numérico é calculado on the fly (em tempo de execução), por exemplo ordenando todos os registos por ordem de adição. No entanto, muita gente resolve este problema mudando a chave primária. Neste artigo, quero mostrar algumas implicações desta abordagem, e mostrar alternativas menos prejudiciais.

Um novo problema

Porque não se deve usar um campo chave primária para esta tarefa? Bem, porque isto vai fazer com que tenhamos mais trabalho a gerir as chaves, coisa que o SGBD pode tratar internamente. Por exemplo, quando definimos a chave primária como auto incremento, estamos a deixar sobre responsabilidade do SGBD a geração automática do identificador do registo. Assim, se apagarmos alguns registos, nunca será gerado um identificador que já foi usado anteriormente. Isto é importante, porque garante que nenhum registo antigo que tenha como chave estrangeira este identificador irá ser incorretamente associado com o novo registo. Portanto, o ideal será não mexer na chave primária para este efeito.

Ok, não mexo na chave primária. Como faço então?

No caso se não ser possível/viável gerar estes códigos em tempo de execução (uma vez que são sequenciais, tendo o primeiro termo da sequência, deve ser fácil obter os seguintes numa listagem completa), pode-se criar um campo adicional na tabela, no qual será colocado o valor sequencial para cada registo. Por exemplo:

ID (chave primária) Ordem Registo
1 1 Pão
2 2 Leite
3 3 Peixe

Desta forma, se eliminarmos um registo, por exemplo, com o ID “2″, ficaremos com a seguinte situação:

ID (chave primária) Ordem Registo
1 1 Pão
3 2 Peixe

O identificador não é alterado, mas o campo de ordem é alterado para manter a sequência. Vamos ver agora o caso de adicionar:

ID (chave primária) Ordem Registo
1 1 Pão
3 2 Peixe
4 3 Carne

Foi adicionado um novo registo. O identificador não usa nenhum código que tenha sido usado antes (para evitar ligação a referências perdidas noutras tabelas), e dessa forma, não garante a sequência pretendida. No entanto, o campo “ordem” mantém essa sequência, atualizando todos os registos.

Parece bem. Como implementar isto?

A implementação desta solução passa por criar um campo dedicado para a sequência, e ajustar a ordem dos membros a cada eliminação ou adição de dados. Isto é feito com recurso a uma ou duas queries adicionais para cada caso. Vejamos o caso de adicionar registos:

SELECT MAX(Ordem) FROM tabela
INSERT INTO tabela (Ordem, Registo) VALUES (valor + 1, 'Novo registo')

Em que “valor” é o valor obtido da primeira query. Tenham em atenção o caso em que a tabela não tem registos, o max irá devolver NULL: nessa situação, devem assumir o valor como 0, para ser incrementado no campo Ordem.

Vamos ver o caso de eliminar registos: se eliminarmos o último registo, não há problema pois os registos anteriores mantêm-se ordenados; mas se apagarmos um registo a meio da tabela, vamos ficar com um “buraco” na sequência. Isso resolve-se da seguinte forma:

SELECT Ordem FROM tabela WHERE Id = x
DELETE FROM tabela WHERE Id = x
UPDATE tabela SET Ordem = Ordem - 1 WHERE Ordem > valor

Em que “x” é o identificador do registo a ser eliminado e “valor” é o número de Ordem correspondente. Desta maneira, atualizamos todos os registos superiores para manterem a ordem (subtraindo o seu valor de Ordem atual).

Quando usar o campo de Ordem e quando usar o ID?

A questão agora é quando usar o identificador ou o campo de ordem. O ID deve servir principalmente para identificar registos entre tabelas: por não serem sequenciais, o SGBD garante que identificadores utilizados anteriormente não são novamente utilizados. Já o campo de Ordem pode ser usado para outros fins exigidos pela camada de aplicação do programa, contudo, não deve ser usados internamente para referenciar outros registos: o campo ID serve para  esse fim, pois está a sua gestão está a cargo do SGDB.

Concluindo, a intenção do campo Ordem é evitar mexer no campo ID, deixando a gestão deste último para o SGBD. Já o campo Ordem está sobre vosso controlo, sendo possível alterá-lo para manter uma certa sequência. Como nota, se tiverem muito acessos concorrentes à base de dados, pode revelar-se útil colocar todos os passos de uma inserção ou eliminação dentro de uma transação, para não haver conflitos com o vosso novo campo de ordem.

Gostou deste artigo?

Facebook Twitter Google Plus Delicious

Comentários

Não existem comentários