Represent Ordering in a Relational Database

database, django, django-models, sql

Solution

Since I've mostly run into this with Django, I've found this solution to be the most workable. It seems that there isn't any "right way" to do this in a relational database.

Problem

I have a collection of objects in a database. Images in a photo gallery, products in a catalog, chapters in a book, etc. Each object is represented as a row. I want to be able to arbitrarily order these images, storing that ordering in the database so when I display the objects, they will be in the right order. For example, let's say I'm writing a book, and each chapter is an object. I write my book, and put the chapters in the following order: Introduction, Accessibility, Form vs. Function, Errors, Consistency, Conclusion, Index It goes to the editor, and comes back with the following suggested order: Introduction, Form, Function, Accessibility, Consistency, Errors, Conclusion, Index How can I store this ordering in the database in a robust, efficient way? I've had the following ideas, but I'm not thrilled with any of them: Array. Each row has an ordering ID, when order is changed (via a removal followed by an insertion), the order IDs are updated. This makes retrieval easy, since it's just `ORDER BY`, but it seems easy to break. `// REMOVAL` `UPDATE ... SET orderingID=NULL WHERE orderingID=removedID` `UPDATE ... SET orderingID=orderingID-1 WHERE orderingID > removedID` `// INSERTION` `UPDATE ... SET orderingID=orderingID+1 WHERE orderingID > insertionID` `UPDATE ... SET orderID=insertionID WHERE ID=addedID` Linked list. Each row has a column for the id of the next row in the ordering. Traversal seems costly here, though there may by some way to use `ORDER BY` that I'm not thinking of. Spaced array. Set the orderingID (as used in #1) to be large, so the first object is 100, the second is 200, etc. Then when an insertion happens, you just place it at `(objectBefore + objectAfter)/2`. Of course, this would need to be rebalanced occasionally, so you don't have things too close together (even with floats, you'd eventually run into rounding errors). None of these seem particularly elegant to me. Does anyone have a better way to do it?

Original source