2009-02-13 11 views
11

Tengo una aplicación que tiene tareas y puede reordenarlas. Ahora estaba luchando por cómo almacenarlos mejor. ¿Debo tener un colomn para el número de orden y volver a calcular todos ellos cada vez que cambio uno? Por favor, dígame una versión que no requiera que actualice todos los números de orden, ya que consume mucho tiempo (desde el punto de vista de las ejecuciones).¿Cómo guardo los pedidos?

Esto es especialmente malo si tengo que poner uno que está en la parte superior del pedido y luego lo arrastro hacia abajo.

  • Nombre (número de pedido)

-

  • 1Example (1)
  • 2Example (2)
  • 3Example (3)
  • 4Example (4)
  • 5Ejemplo (5)

-

  • 2Example (1) *
  • 3Example (2) *
  • 4Example (3) *
  • 5Example (4) *
  • 1Example (5) *

* hay que cambiarlo en la base de datos

también algunas tareas pueden borrarse debido a que se realizan

+0

En ese ejemplo, no necesitaría actualizar todas las posiciones. Si simplemente establece el registro 1 en la posición 6, está bien. Un mejor ejemplo podría ser tirar del registro inferior hasta la parte superior. –

Respuesta

0

Recomendaría tener una columna de orden en la base de datos. Cuando se reordena un objeto, cambie el valor del pedido en la base de datos entre el objeto que reordenó y los objetos que tienen el mismo valor de orden, de esa manera no tiene que volver a cargar todo el conjunto de filas.

Esperamos que tenga sentido ... por supuesto, esto depende de sus reglas para volver a ordenar.

9

Normalmente añadiré una columna int o smallint llamada algo así como 'Ordinal' o 'PositionOrdinal' como sugieres, y con la advertencia exacta mencionas — la necesidad de actualizar un número potencialmente significativo de registros cada vez que un solo el registro se reordena.

La ventaja es que nos dieron una llave para una tarea específica y una nueva posición para esa tarea, el código para mover un elemento está a sólo dos estados:

UPDATE `Tasks` SET Ordinal= Ordinal+1 WHERE Ordinal>[email protected] 
UPDATE `Tasks` SET Ordinal= @NewPosition WHERE TaskID= @TaskID 

Hay otras sugerencias para una lista doblemente enlazada o orden léxico Cualquiera puede ser más rápido, pero a costa de un código mucho más complicado, y el rendimiento solo tendrá importancia cuando tenga un lote de artículos en el mismo grupo.

Si el rendimiento o la complejidad del código es más importante dependerá de su situación. Si tiene millones de registros, la complejidad adicional puede valer la pena.Sin embargo, normalmente prefiero el código más simple porque los usuarios de normalmente solo ordenan listas pequeñas a mano. Si no hay tantos elementos en la lista , las actualizaciones adicionales no serán importantes. Esto normalmente puede manejar miles de registros sin ningún impacto notable en el rendimiento.

Lo que debe tener en cuenta con su ejemplo actualizado es que la columna solo se utiliza para ordenar y que, de lo contrario, no se muestra directamente al usuario. Por lo tanto, al arrastrar un elemento de arriba a abajo, como se muestra, lo único que debe cambiar es ese único registro. No importa que dejes la primera posición vacía. Esto significa que hay un pequeño potencial para desbordar su ordenamiento de enteros con suficiente reordenamiento, pero permítanme decirlo de nuevo: los usuarios de normalmente solo ordenan listas pequeñas a mano. Nunca he escuchado que este riesgo realmente cause un problema.

1

Lo hacemos con una columna de Secuencia en la base de datos.

Usamos la numeración dispersa (por ejemplo, 10, 20, 30, ...), por lo que podemos "insertar" uno entre los valores existentes. Si las filas adyacentes tienen números consecutivos, volvemos a numerar el número mínimo de filas que podemos.

Probablemente se podría utilizar números decimales - tomar el promedio de los números de secuencia de las filas adyacentes al lugar donde se va a insertar, a continuación, sólo tiene que actualizar la fila de ser "movido"

14

Es posible mantener órdenes como literales, y el uso de una especie de léxico:

1. A 
2. Z 

añadir una tarea:

1. A 
3. L 
2. Z 

Añadir más:

1. A 
4. B 
3. L 
2. Z 

Move 2 entre 1 y 4:

1. A 
2. AL 
4. B 
3. L 

etc.

de actualizar sólo un registro a la vez: solo toma una carta promedio entre los primeros que se diferencian: si se pone entre A y C, toma B, si pone entre ALGJ y ALILFG, toma ALH.

Carta junto a los recuentos existentes como existentes concatenados con el que está junto a Z. I. e. si necesita ponerlo entre ABHDFG y ACSD F, lo cuenta como entre ABH y AB(Z+), y escriba AB(letter 35/2), es decir ABP.

Si se queda sin longitud de cadena, siempre puede realizar un nuevo pedido completo.

Actualización:

También puede mantener sus datos como una lista enlazada.

Ver el artículo en mi blog sobre cómo hacerlo en MySQL:

En pocas palabras:

/* This just returns all records in no particular order */ 

SELECT * 
FROM t_list 

id  parent 
------- -------- 
1  0 
2  3 
3  4 
4  1 

/* This returns all records in intended order */ 

SELECT @r AS _current, 
     @r := (
     SELECT id 
     FROM t_list 
     WHERE parent = _current 
     ) 
FROM (
     SELECT @r := 0 
     ) vars, 
     t_list 

_current id 
------- -------- 
0  1 
1  4 
4  3 
3  2 

Al mover los elementos, se le necesita actualizar como máximo 4 filas.

Esta parece ser la forma más eficiente de mantener una lista ordenada que se actualiza con frecuencia.

+1

Nunca me he encontrado con un algoritmo elegante para hacerlo (¡feliz de ser iluminado!) Así que prefiero usar un número de coma flotante y un "promedio" de los dos valores adyacentes. – Kristen

+0

String = [Char] = [Entero] – Henrik

+1

Esto es tan inteligente. Desearía haber visto esto antes de hacer MI implementación :-( –

2

Fuera de sus respuestas me ocurrió con una mezcla que va de la siguiente manera:

Digamos que tenemos:

  • 1Example (1)
  • 2Example (2)
  • 3Example (3)
  • 4Example (4)
  • 5Example (5)

Ahora bien, si puedo ordenar algo entre 4 y 5 se vería así:

  • 2Example (2)
  • 3Example (3)
  • 4Example (4)
  • 1Example (4.5)
  • 5Example (5)

vez en cuando algo entre 1 y 5

  • 3Example (3)
  • 4Example (4)
  • 1Example (4,5)
  • 2Example (4,75)
  • 5Example (5)

será siempre tomar la media de la diferencia entre los números

Espero que funcione, por favor, corríjanme;)

+0

Eso debería funcionar, pero la precisión de un doble es finita. (Así es el tamaño de un int, así que no digo que sea malo: algo de lo que hay que estar enterado). –

+0

Además, esto significa hacer una búsqueda de al menos tres elementos: el artículo para reordenar, más los artículos 1 mayor y 1 menor que la posición nueva del artículo principal, para que pueda usar sus valores para el nuevo promedio. Dependiendo de cómo haga las cosas, esto puede ser tan costoso o incluso más que una simple actualización. –

+0

No estoy seguro Estoy de acuerdo: "Actualización simple" de, digamos, 10 elementos requiere algún medio de asignarles un nuevo número (lo que podría significar primero colocarlos en una tabla temporal, y asignarles un número basado en un orden de clasificación, y luego UNIÉNDOLO a la tabla original para ACTUALIZARLO con un nuevo número) – Kristen

1

Esto no es un problema fácil. Si tiene un número bajo de elementos ordenables, simplemente los restablecería a todos en su orden nueva.

De lo contrario, parece que tomaría tanto trabajo o más "probar y configurar" para modificar solo los registros que han cambiado.

Puede delegar este trabajo en el lado del cliente. Pídale al cliente que mantenga el orden de clasificación anterior y el orden de clasificación nuevo y determine qué fila [de orden de clasificación] debe actualizarse; luego pasa esas tuplas a la interfaz PHP-mySQL.

Se podría mejorar este método de la siguiente manera ( no requieren flotadores):

  1. Si todos los elementos que se pueden ordenar en una lista se inicializan en un criterio de ordenación en función de su posición en el lista, establezca el orden de clasificación de cada elemento en algo como fila [orden de clasificación] = fila [orden de clasificación * K] donde K es un número> número promedio de veces que espera que la lista se reordene. O (N), N = número de elementos, pero aumenta la capacidad de inserción al menos N * K con al menos K ranuras abiertas entre cada par de elementos salientes.

  2. Luego, si desea insertar un elemento entre otros dos, es tan simple como cambiar su orden de clasificación para que sea uno> el elemento inferior y < el superior. Si no hay "espacio" entre los elementos, simplemente puede volver a aplicar el algoritmo "spread" (1) presentado en el párrafo anterior. Cuanto mayor sea K, menos se aplicará.

El algoritmo K se aplicaría de forma selectiva en el script PHP mientras que la elección de la nueva especie de orden que se haría por el cliente (Javascript, tal vez).

+0

Muy cerca de la realidad. De hecho, es algo complejo de manejar adecuadamente, especialmente si tiene que administrar algunas listas ordenadas que pueden cambiarse por usuarios múltiples al mismo tiempo. –

Cuestiones relacionadas