HiveBrain v1.2.0
Get Started
← Back to all entries
patternsqlMinor

Features and Patterns for Managing Ordered Lists

Submitted by: @import:stackexchange-dba··
0
Viewed 0 times
managingpatternsforlistsandfeaturesordered

Problem

I have a common scenario in app design, where I need to store an ordered list, and make the ordering easily accessible and adjustable via an application UI.

The ordering operations are simple and operate on only one item at a time - move to top, move to bottom, move up one position, or down one position. These positioning operations may be interspersed in with typical add / edit / delete list operations. For discussion, this is a single-user app and db store.

Because the objects can be highly complex, I'd like to avoid caching the entire list client-side, and tracking newly created objects in memory until a big save operation occurs.

I am using MS-SQL almost exclusively on these projects, and I'm wondering if there are db features, SQL features, or design patterns I should be using to better support the management of ordered lists.

My current approach is to use a DECIMAL column to store the list positions, which become fractional during the item move process. Then I sort the list and re-generate integer positions to normalize list. Following this operation, it's ready for another item move.

In an example implementation, I create a column named Seq for position tracking. Once decimal place is enough for the .5 storage.

[Seq] [decimal](10, 1) NOT NULL


When an item is re-positioned in the app UI,

  • I UPDATE that record with new Seq value



  • I retrieve the full table, sorted ascending by Seq and regenerate all Seq values in the table from a 1-based integer series



The new Seq value is easily calculated-

  • Move to top = 0



  • Move to bottom = MAX(Seq) + 1



  • Move up = subtract 1.5 from the current Seq value



  • Move down = add 1.5 to the current Seq value



As lists get longer, updates get slower, since I'm doing N UPDATE statements, where N is the number of rows in the table, so it seems only suitable for small tables.

  • Is there some kind of in-built capability for managing an ordered list in SQL or MS-SQL?



  • Is there a m

Solution

The last is a technique used quite often, and that works. If you're afraid of running out of decimals you can refine it to detect when the average between the next two previous/following values cannot be distinguished from one of them (i.e.: you run out of decimals).

When this happens, you should resequence as many rows up or down as needed.

Let's assume you work with just 2 decimals, and you have two rows with Seq 1.00 and 1.01; the next value being 2.00. If you want to move a row using your technique between these two, you would have Seq = (1.00, 1.005, 1.01, 2.00). This would need more decimals than are available and, depending on the rounding rules, 1.005 would either be rounded to 1.00 or 1.01. In any case, you can detect that the inserted value is not distinguishable from either 1.00 or 1.01.

In that case, you need to "push down (or up) and make room". That is, you should change one more row, and have (1.00, 1.25, 1.50, 2.00). You've spread the change to two rows instead of just one.

This might need to be done recursively (or iteratively) if you ever run into a situation such as (1.01, 1.02, 1.03, 1.04, ...).

With this extensions to your original technique, you don't need to renormalize. This is taken care of only in an "as needed" basis, and scales quite well.

NOTEs:

-
Implementing the algorithm might be a bit tricky, because you need to take into account when you're at the first or last rows, and how to handle those, and when your Seq numbers reach the maximum or minimum available values.

-
The algorithm works with any number of decimals, including 0. That is, you can use it as well with integer values instead of decimal, which might prove more computing efficient, or, depending on how decimals are implemented, be also more space efficient.

Context

StackExchange Database Administrators Q#176570, answer score: 6

Revisions (0)

No revisions yet.