2008-08-21 19 views
31

Tengo una colección de objetos en una base de datos. Imágenes en una galería de fotos, productos en un catálogo, capítulos en un libro, etc. Cada objeto se representa como una fila. Quiero poder ordenar estas imágenes de forma arbitraria, almacenando ese orden en la base de datos para que cuando muestre los objetos, estén en el orden correcto.Representación de pedidos en una base de datos relacional

Por ejemplo, digamos que estoy escribiendo un libro, y cada capítulo es un objeto. Escribo mi libro, y poner los capítulos en el orden siguiente:

Introducción, Accesibilidad, Formulario contra la función, errores, consistencia, Conclusión, Índice

Se va al editor, y viene de vuelta con el siguiente orden sugerido:

Introducción, forma, función, Accesibilidad, consistencia, errores, Conclusión, Índice

¿Cómo puedo almacenar este pedido en la base de datos de una manera robusta y eficiente?

he tenido las siguientes ideas, pero no estoy encantada con cualquiera de ellos:

  1. matriz. Cada fila tiene una ID de pedido, cuando se cambia la orden (a través de una eliminación seguida de una inserción), se actualizan las ID de la orden. Esto facilita la recuperación, ya que es solo ORDER BY, pero parece fácil de romper.

    // 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

  2. lista enlazada. Cada fila tiene una columna para el ID de la siguiente fila en el orden. El recorrido parece costoso aquí, aunque es posible que de alguna manera use ORDER BY en el que no estoy pensando.

  3. matriz espaciada. Establezca el orderingID (como se usa en # 1) para que sea grande, de modo que el primer objeto sea 100, el segundo sea 200, etc. Luego, cuando ocurra una inserción, simplemente colóquelo en (objectBefore + objectAfter)/2. Por supuesto, esto debería reequilibrarse de vez en cuando, para que no tenga cosas muy juntas (incluso con flotadores, eventualmente se encontraría con errores de redondeo).

Ninguno de estos parece particularmente elegante para mí. ¿Alguien tiene una mejor manera de hacerlo?

Respuesta

1

Como la mayoría de las veces me he topado con esto con Django, he encontrado que this solution es la más viable. Parece que no hay una "forma correcta" de hacerlo en una base de datos relacional.

3

La mezcla de act_as_list en Rails maneja esto básicamente de la forma descrita en el # 1. Busca una columna INTEGER llamada posición (de la cual puede anular el nombre del curso) y la usa para hacer un ORDER BY. Cuando desee volver a pedir cosas, actualice las posiciones. Me ha servido muy bien cada vez que lo he usado.

Como nota al margen, puede eliminar la necesidad de volver a posicionar siempre en INSERTS/DELETES mediante el uso de numeración dispersa - algo así como básico en el día ... puede numerar sus posiciones 10, 20, 30, etc.y si necesita insertar algo entre 10 y 20, solo debe insertarlo en una posición de 15. Del mismo modo, al eliminar puede simplemente eliminar la fila y dejar el espacio. Solo necesita volver a numerar cuando realmente cambia el pedido o si trata de hacer un inserto y no hay espacio adecuado para insertarlo.

Por supuesto, dependiendo de su situación particular (por ejemplo, si tiene las otras filas ya cargadas en la memoria o no) puede o no tener sentido utilizar el enfoque de brecha.

+1

+1 por mencionar la numeración dispersa. He usado la gema [rank-model] (https://github.com/mixonic/ranked-model) para esto en el pasado. –

2

Haría un número consecutivo, con un gatillo en la mesa que "da lugar" a una prioridad si ya existe.

+4

¡Esto requiere una reestructuración O (n) en cada inserción! – cdleary

2

Si los objetos no están fuertemente codificados por otras tablas, y las listas son cortas, eliminar todo en el dominio y simplemente volver a insertar la lista correcta es lo más fácil. Pero eso no es práctico si las listas son grandes y tiene muchas limitaciones para ralentizar la eliminación. Creo que tu primer método es realmente el más limpio. Si lo ejecuta en una transacción, puede estar seguro de que no ocurre nada extraño mientras está en el medio de la actualización para arruinar el pedido.

1

Hice esto en mi último proyecto, pero era para una tabla que solo ocasionalmente necesitaba pedirse específicamente, y no se accedía con demasiada frecuencia. Creo que la matriz espaciada sería la mejor opción, porque el reordenamiento sería más barato en el caso promedio, con solo un cambio en un valor y una consulta en dos).

Además, me imagino que ORDER BY sería muy optimizado por los proveedores de bases de datos, por lo que aprovechar esa función sería ventajoso para el rendimiento en comparación con la implementación de la lista vinculada.

5

Otra alternativa sería (si su RDBMS lo admite) utilizar columnas de tipo array. Si bien esto rompe las reglas de normalización, puede ser útil en situaciones como esta. Una base de datos que sé que tiene matrices es PostgreSQL.

+0

No entiendo esta solución que aparentemente es la mejor respuesta. ¿Podría explicar un poco sobre cómo usar la matriz para cada fila? Gracias – Pierre

3

Sólo un pensamiento considerando opción # 1 vs # 3: ¿no la opción de matriz espaciada (# 3) solo pospone el problema de la matriz normal (# 1)? Sea cual sea el algoritmo que elija, o está roto, y tendrá problemas con el # 3 más tarde, o funciona, y luego el # 1 debería funcionar igual de bien.

2

utilizar un número de coma flotante para representar la posición de cada elemento:

Artículo 1 -> 0,0

artículo 2 -> 1,0

artículo 3 -> 2,0

artículo 4 -> 3.0

Puede colocar cualquier elemento entre los otros dos elementos por simple bisección:

Artículo 1 -> 0,0

artículo 4 -> 0,5

artículo 2 -> 1,0

artículo 3 -> 2,0

(punto Movido 4 entre los puntos 1 y 2).

El proceso de bisección puede continuar casi indefinidamente debido a la forma en que los números de coma flotante se codifican en un sistema informático.

Artículo 4 -> 0.5

Artículo 1 -> 0,75

artículo 2 -> 1,0

artículo 3 -> 2,0

(Mover el elemento 1 a la posición justo después del punto 4)

+5

Esto/no continuará/continuará indefinidamente. Para los números de coma flotante (dobles) los valores convergerán después de 53 rondas, en el caso patológico. Incluso si su DBMS utiliza decimales de precisión arbitrarios, tendrá mucha hinchazón en la estructura de datos. – cdleary

1

tuve este problema también Estuve bajo una gran presión de tiempo (no somos todos) y elegí la opción n. ° 1, y solo cambié las filas que cambiaron.

Si intercambia el elemento 1 con el elemento 10, solo haga dos actualizaciones para actualizar los números de pedido del artículo 1 y el elemento 10. Sé que es algorítmicamente simple, y es O (n) el peor caso, pero el peor de los casos es cuando tienes una permutación total de la lista. ¿Con qué frecuencia va a pasar eso? Eso es para que respondas.

0

Tuve el mismo problema y probablemente haya pasado al menos una semana sobre el modelado de datos adecuado, pero creo que finalmente lo tengo. Utilizando el tipo de datos de matriz en PostgreSQL, puede almacenar la clave principal de cada elemento ordenado y actualizar dicha matriz en consecuencia utilizando inserciones o eliminaciones cuando cambie su orden. Hacer referencia a una sola fila te permitirá mapear todos tus objetos según el orden en la columna de la matriz.

Todavía es un poco entrecortado de una solución, pero es probable que funcione mejor que la opción # 1, ya que la opción 1 requiere actualizar el número de orden de todas las otras filas al hacer el pedido de cambios.

Cuestiones relacionadas