I need to be able to store a large list of ordered items in the DB. So far that\'s straight-forward:
ID Position OtherFields
1 45 ...
2 4736
If you relax the constraint that the Position
column must contain integers from 1 to N and instead allow it to contain any numbers then you can do both searches and updates efficiently.
You can insert an item between two other items with position A and B by calculating the average (A + B) DIV 2. For example if A is 10000 and B is 12000 then your new position is 11000. Occasionally you will run out of gaps due to clustering, at which point you can run through the whole table redistributing the positions more evenly.
What about using a decimal for position? If you do, you could use the following method to put it between other positions:
Original records are:
ID Position Otherfields
--------------------------
1 1.0
2 2.0
.
.
.
5000 5000.0
Then say you move ID 1 to just before 5000
ID Position Otherfields
--------------------------
1 4999.9
2 2.0
.
.
.
5000 5000.0
Now lets say you want to put ID 2 between 1 and 5000:
ID Position Otherfields
--------------------------
1 4999.9
2 4999.91
.
.
.
5000 5000.0
This way you are only changing one record...
UPDATE:
After re-reading @Mark Byers' suggestion it appears that our solutions are very similar, though using a decimal seems much simpler to me...
Another solution would be to use lexical ranking. Whenever there is no value between two strings, one would simply add a new character. The only disadvantage is, that it consumes more memory compared to a numerical solution, which are named in other answers.
A normalization isn't necessary for an error free database, but can be optionally done to reduce the string lenght.
Here is an interesting video, which explains it in detail: https://www.youtube.com/watch?v=OjQv9xMoFbg&feature=youtu.be
The following might be helpful. It does not answer your questions directly, but could shed some light on how to do this (if it is possible for your requirements):
ranking entries in mysql table