Uma necessidade comum ao usar um banco de dados é acessar os registros em ordem. Por exemplo, se eu tiver um blog, quero poder reordenar minhas postagens em ordem arbitrária. Essas entradas geralmente têm muitos relacionamentos, portanto, um banco de dados relacional parece fazer sentido.
A solução comum que vi é adicionar uma coluna inteira order
:
CREATE TABLE AS your_table (id, title, sort_order)
AS VALUES
(0, 'Lorem ipsum', 3),
(1, 'Dolor sit', 2),
(2, 'Amet, consect', 0),
(3, 'Elit fusce', 1);
Em seguida, podemos classificar as linhas order
para colocá-las na ordem correta.
No entanto, isso parece desajeitado:
- Se eu quiser mover o registro 0 para o início, tenho que reordenar todos os registros
- Se eu quiser inserir um novo registro no meio, tenho que reordenar todos os registros depois dele
- Se eu quiser remover um registro, tenho que reordenar todos os registros depois dele
É fácil imaginar situações como:
- Dois registros têm o mesmo
order
- Existem lacunas
order
entre os registros
Isso pode acontecer facilmente por vários motivos.
Esta é a abordagem adotada por aplicativos como o Joomla:
Você poderia argumentar que a interface aqui é ruim e que, em vez de humanos editarem números diretamente, eles deveriam usar setas ou arrastar e soltar - e provavelmente você estaria certo. Mas nos bastidores, a mesma coisa está acontecendo.
Algumas pessoas propuseram o uso de um decimal para armazenar a ordem, para que você possa usar "2,5" para inserir um registro entre os registros nas ordens 2 e 3. E embora isso ajude um pouco, é sem dúvida ainda mais confuso porque você pode acabar com decimais estranhos (onde você para? 2,75? 2,875? 2,8125?)
Existe uma maneira melhor de armazenar pedidos em uma tabela?
Não, há uma maneira mais simples.
Isso é verdade, a menos que você use um tipo de dados que suporte valores "entre". Os tipos flutuante e numérico permitem que você atualize um valor para, digamos, 2,5. Mas varchar(n) também funciona. (Pense 'a', 'b', 'c'; então pense 'ba', 'bb', 'bc'.)
Não, há uma maneira mais simples. Basta excluir a linha. As linhas restantes ainda serão classificadas corretamente.
Uma restrição exclusiva pode impedir isso.
As lacunas não têm efeito sobre como um dbms classifica os valores em uma coluna.
Você não para até que seja necessário . O dbms não tem problemas em classificar valores que tenham 2, 7 ou 15 casas após o ponto decimal.
Acho que seu problema real é que você gostaria de ver os valores na ordem de classificação como números inteiros. Você pode fazer isso.
É muito simples. Você precisa ter uma estrutura de "buraco de cardinalidade":
Você precisa ter 2 colunas:
integer
bigint
( nãodouble
)Inserir/atualizar
order = round(max_bigint / 2)
.order = round("order of first record" / 2)
order = round("max_bigint - order of last record" / 2)
4) Ao inserir no meio, coloqueorder = round("order of record before - order of record after" / 2)
Este método tem uma cardinalidade muito grande. Se você tiver um erro de restrição ou se achar que tem cardinalidade pequena, poderá reconstruir a coluna de ordem (normalizar).
Em situação máxima com normalização (com esta estrutura), você pode ter "buraco de cardinalidade" em 32 bits.
Lembre-se de não usar tipos de ponto flutuante - a ordem deve ser um valor preciso!
Geralmente, a ordenação é feita de acordo com alguma informação do prontuário, título, RG ou o que for adequado para aquela situação específica.
Se você precisar de uma ordem especial, usar uma coluna inteira não é tão ruim quanto parece. Por exemplo, para abrir espaço para um recorde chegar ao 5º lugar, você pode fazer algo como:
update table_1 set place = place + 1 where place > 5
.Espero que você possa declarar que a coluna é
unique
e talvez tenha um procedimento para fazer rearranjos "atômicos". Os detalhes dependem do sistema, mas essa é a ideia geral.Quem se importa? Esses números estão lá apenas para o computador lidar, então não importa quantos dígitos fracionários eles tenham ou quão feios eles pareçam para nós.
Usar valores decimais significa que, para mover o item F entre os itens J e K, tudo o que você precisa fazer é selecionar os valores de ordem para J e K, calcular a média deles e atualizar F. Duas instruções SELECT e uma instrução UPDATE (provavelmente feito usando isolamento serializável para evitar impasses).
Se você quiser ver inteiros em vez de frações na saída, calcule inteiros no aplicativo cliente ou use as funções ROW_NUMBER() ou RANK() (se seu RDBMS os incluir).
Achei essa resposta muito melhor. Citando-o inteiramente:
Em meu próprio projeto, estou planejando tentar uma solução semelhante à solução de número decimal, mas usando matrizes de bytes:
A ideia é que você nunca pode ficar sem valores intermediários possíveis porque você apenas acrescenta um
b"\x00"
aos registros envolvidos se precisar de mais valores. (int
é ilimitado no Python 3, caso contrário, você teria que escolher uma fatia dos bytes no final para comparar, assumindo que, entre dois valores adjacentes, as diferenças seriam compactadas no final.)Por exemplo, digamos que você tenha dois registros,
b"\x00"
eb"\x01"
, e deseja que um registro fique entre eles. Não há valores disponíveis entre0x00
e0x01
, então você acrescentab"\x00"
a ambos e agora tem vários valores entre eles que pode usar para inserir novos valores.O banco de dados pode classificá-lo facilmente porque tudo acaba em ordem lexicográfica. Se você excluir um registro, ele ainda estará em ordem. No meu projeto, fiz
b"\x00"
eb"\xff"
comoFIRST
eLAST
registros, no entanto, para usá-los como valores virtuais "de" e "para" para preceder/acrescentar novos registros: