Uma necessidade comum ao usar um banco de dados é acessar os registros em ordem. Por exemplo, se eu tiver um blog, desejo reorganizar minhas postagens em ordem arbitrária. Essas entradas costumam ter muitos relacionamentos, portanto, um banco de dados relacional parece fazer sentido.
A solução comum que eu 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 obtê-las na ordem correta.
No entanto, isso parece desajeitado:
- Se eu quiser mover o registro 0 para o início, preciso reordenar todos os registros
- Se eu quiser inserir um novo registro no meio, preciso 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 nos
order
registros entre
Isso pode acontecer com bastante facilidade por vários motivos.
Esta é a abordagem que aplicativos como o Joomla adotam:
Você pode argumentar que a interface aqui é ruim e que, em vez de os humanos editarem números diretamente, eles devem usar setas ou arrastar e soltar - e você provavelmente estaria certo. Mas nos bastidores, a mesma coisa está acontecendo.
Algumas pessoas propuseram usar 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 enquanto isso ajuda 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?
fonte
orders
e o ddl.Respostas:
Não, existe 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 atualizar um valor para, digamos, 2,5. Mas varchar (n) também funciona. (Pense em 'a', 'b', 'c'; depois pense em 'ba', 'bb', 'bc'.)
Não, existe uma maneira mais simples. Apenas exclua a linha. As linhas restantes ainda serão classificadas corretamente.
Uma restrição única pode impedir isso.
As lacunas não têm efeito sobre como um dbms classifica valores em uma coluna.
Você não para até que precise . O dbms não tem problema ao classificar valores com 2, 7 ou 15 casas após o ponto decimal.
Acho que seu problema real é que você deseja ver os valores na ordem de classificação como números inteiros. Você pode fazer isso.
fonte
with cte as (select *,row_number() over (order by sort_order desc) as row from test) update cte set sort_order=row;
É 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, definaorder = 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 tiver uma cardinalidade pequena, poderá reconstruir a coluna da ordem (normalizar).
Na situação máxima com normalização (com essa estrutura), você pode ter um "buraco de cardinalidade" em 32 bits.
Lembre-se de não usar tipos de ponto flutuante - a ordem deve ser um valor preciso!
fonte
Geralmente, a encomenda é feita de acordo com algumas informações nos registros, título, ID ou o que for apropriado para essa situação específica.
Se você precisar de um pedido especial, usar uma coluna inteira não é tão ruim quanto pode parecer. Por exemplo, para deixar espaço para um registro entrar em 5º lugar, você pode fazer algo como:
update table_1 set place = place + 1 where place > 5
.Espero que você possa declarar a coluna
unique
e talvez ter um procedimento para tornar os rearranjos "atômicos". Os detalhes dependem do sistema, mas essa é a ideia geral.fonte
Quem se importa? Esses números estão disponíveis apenas para o computador lidar, portanto, não importa quantos dígitos fracionários eles tenham ou quão feios eles nos pareçam.
O uso de valores decimais significa que, para mover o item F entre os itens J e K, basta selecionar os valores da ordem para J e K, depois calculá-los como média e atualizar F. Duas instruções SELECT e uma instrução UPDATE (provavelmente feitas usando isolamento serializável para evitar bloqueios).
Se você deseja visualizar números inteiros em vez de frações na saída, calcule números inteiros no aplicativo cliente ou use as funções ROW_NUMBER () ou RANK () (se o seu RDBMS as incluir).
fonte
No meu próprio projeto, estou planejando tentar uma solução semelhante à solução de número decimal, mas usando matrizes de bytes:
A idéia é que você nunca pode ficar sem os possíveis valores intermediários, porque você apenas anexará
b"\x00"
a 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, supondo que, entre dois valores adjacentes, as diferenças seriam compactadas no final.)Por exemplo, dizer que você tem dois registros,
b"\x00"
eb"\x01"
, e você quer um registro para ir entre eles. Não há valores disponíveis entre0x00
e0x01
, portanto, você anexab"\x00"
a ambos, e agora você tem vários valores entre eles, que podem ser usados 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, criei
b"\x00"
eb"\xff"
asFIRST
e osLAST
registros, no entanto, para usá-los como valores virtuais "from" e "to" para preceder / acrescentar novos registros:fonte
Achei esta resposta muito melhor. Citando-o inteiramente:
fonte