2010-03-16 15 views
6

Hay una tabla como la siguienteprocedimiento de MySQL para actualizar la referencia numérica en las filas anteriores cuando uno se actualiza

 
______________________ 
| id | title | order | 
|----------------------| 
| 1 | test1 | 1 | 
|-----|--------|-------| 
| 2 | test2 | 2 | 
|-----|--------|-------| 
| 3 | test3 | 3 | 
|-----|--------|-------| 
| 4 | test4 | 4 | 
'----------------------' 

cuando me presento en mis MySQL pagar una sola actualización de una fila

 $sql> UPDATE `table` SET order=1 WHERE id=3;

Y a continuación, el procedimiento o el método remuestrea la columna de orden en los valores inferiores antes de actualizar para obtener su orden renovada como esta

 
______________________ 
| id | title | order | 
|----------------------| 
| 1 | test1 | 2 | 
|-----|--------|-------| 
| 2 | test2 | 3 | 
|-----|--------|-------| 
| 3 | test3 | 1 | 
|-----|--------|-------| 
| 4 | test4 | 4 | 
'----------------------' 

¡Cualquier ayuda sería apreciada, gracias!

+0

¿Cuál es tu pregunta? ¿Que estás tratando de hacer? ¿Está tratando de devolverlos en el orden del valor de la columna de orden? (ORDEN POR orden) – Theresa

+2

La idea es (creo): establecer el número de orden de id = 3 a 1 (de 3); ahora, vuelva a ordenar los registros entre los valores nuevos y antiguos (orden = 1, 2) para conservar el orden anterior, pero luego de id = 3. Entonces, cuando los datos se seleccionan con 'ORDER BY order', la secuencia será id = 3, 1, 2, 4. –

+0

Estoy buscando un procedimiento o método que actualice los valores de orden para ordenarlo según la actualización valor. Actualizo una sola fila, y luego valores más bajos que el orden real de esta fila y más alto que la orden actualizada obtendrá +1 en su valor – markcial

Respuesta

4

Hay dos casos a considerar, pienso:

  1. Mover una fila por lo que aparece antes en el pedido.
  2. Mueva una fila para que aparezca más adelante en el orden.

No es trivial de ninguna manera. No está claro si hay una restricción única en la columna 'orden'; El resultado final ciertamente se supone que tiene un orden único.

Notación:

  • 'On' se refiere a la fila con valor 'orden = n' en los viejos valores
  • 'Nn' se refiere a la fila con 'order = n' en los nuevos valores

En el ejemplo (ilustrativa de caso 1):

  • O3 -> N1
  • O1 -> N2
  • O2 -> N3

Como alternativa, considere mover id = 2, de modo que tiene orden = 4:

  • O2 -> N4
  • O3 -> N2
  • O4 -> N3

Usted es básicamente sumar o restar uno de los 'otros' filas, donde los son las filas en el orden anterior entre la posición anterior de la fila movida y la nueva posición de la fila movida. En un pseudo-código, usando $ antiguo y $ nueva para identificar el antes y después de las posiciones de la fila movido, y tratar con el caso 1 (edad> $ nueva $):

UPDATE AnonymousTable 
    SET order = CASE 
       WHEN order = $old THEN $new 
       WHEN order >= $new AND order < $old THEN order + 1 
       END CASE 
WHERE order BETWEEN $new AND $old; 

El código correspondiente para el caso 2 ($ $ antigua < nuevo) es:

UPDATE AnonymousTable 
    SET order = CASE 
       WHEN order = $old THEN $new 
       WHEN order > $new AND order <= $old THEN order - 1 
       END CASE 
WHERE order BETWEEN $old AND $new; 

Teniendo en cuenta la cláusula WHERE de las correspondientes actualizaciones en su conjunto, puede ser capaz de eliminar el segundo cuando en el caso y reemplazarlo con una simple cosa.

UPDATE AnonymousTable 
    SET order = CASE 
       WHEN order = $old THEN $new 
       ELSE     order + 1 
       END CASE 
WHERE order BETWEEN $new AND $old; 

UPDATE AnonymousTable 
    SET order = CASE 
       WHEN order = $old THEN $new 
       ELSE     order - 1 
       END CASE 
WHERE order BETWEEN $old AND $new; 

creo que es un procedimiento almacenado con el fin - la elección entre los dos estados en base a los parámetros de entrada $ edad, $ nueva. Es posible que pueda hacer algo con una combinación juiciosa de expresiones como '($old - $new)/ABS($old - $new)' y 'MIN($old, $new)' y 'MAX($old, $new)' donde el MIN/MAX no son agregados sino funciones de comparación para un par de valores (como se encuentran en Fortran, entre otros) lenguajes de programación).

Tenga en cuenta que asumo que mientras se está ejecutando una sola declaración de SQL, la restricción de exclusividad (si existe) no se aplica ya que cada fila se cambia, solo cuando la instrucción se completa. Esto es necesario ya que no puede controlar realmente el orden en que se procesan las filas. Sé de DBMS donde esto causaría problemas; Sé de otros donde no sería así.


Todo se puede hacer en una sola instrucción SQL - pero usted quiere un procedimiento almacenado para solucionar los parámetros a la declaración. Uso IBM Informix Dynamic Server (11.50.FC6 en MacOS X 10.6.2), y ese es uno de los DBMS que impone la restricción única en la columna 'orden' al final de la instrucción. Hice el desarrollo del SQL sin la restricción ÚNICA; eso funcionó también, por supuesto. (Y sí, IDS le permite deshacer declaraciones DDL como CREAR TABLA y CREAR PROCEDIMIENTO. ¿Qué dijo? ¿Su DBMS no lo hace? ¡Qué pintoresco!)

BEGIN WORK; 
CREATE TABLE AnonymousTable 
(
    id  INTEGER NOT NULL PRIMARY KEY, 
    title VARCHAR(10) NOT NULL, 
    order INTEGER NOT NULL UNIQUE 
); 
INSERT INTO AnonymousTable VALUES(1, 'test1', 1); 
INSERT INTO AnonymousTable VALUES(2, 'test2', 2); 
INSERT INTO AnonymousTable VALUES(3, 'test3', 3); 
INSERT INTO AnonymousTable VALUES(4, 'test4', 4); 

SELECT * FROM AnonymousTable ORDER BY order; 

CREATE PROCEDURE move_old_to_new(old INTEGER, new INTEGER) 
    DEFINE v_min, v_max, v_gap, v_inc INTEGER; 
    IF old = new OR old IS NULL OR new IS NULL THEN 
     RETURN; 
    END IF; 
    LET v_min = old; 
    IF new < old THEN 
     LET v_min = new; 
    END IF; 
    LET v_max = old; 
    IF new > old THEN 
     LET v_max = new; 
    END IF; 
    LET v_gap = v_max - v_min + 1; 
    LET v_inc = (old - new)/(v_max - v_min); 
    UPDATE AnonymousTable 
     SET order = v_min + MOD(order - v_min + v_inc + v_gap, v_gap) 
    WHERE order BETWEEN v_min AND v_max; 
END PROCEDURE; 

EXECUTE PROCEDURE move_old_to_new(3,1); 
SELECT * FROM AnonymousTable ORDER BY order; 
EXECUTE PROCEDURE move_old_to_new(1,3); 
SELECT * FROM AnonymousTable ORDER BY order; 

INSERT INTO AnonymousTable VALUES(5, 'test5', 5); 
INSERT INTO AnonymousTable VALUES(6, 'test6', 6); 
INSERT INTO AnonymousTable VALUES(7, 'test7', 7); 
INSERT INTO AnonymousTable VALUES(8, 'test8', 8); 

EXECUTE PROCEDURE move_old_to_new(3,6); 
SELECT * FROM AnonymousTable ORDER BY order; 
EXECUTE PROCEDURE move_old_to_new(6,3); 
SELECT * FROM AnonymousTable ORDER BY order; 
EXECUTE PROCEDURE move_old_to_new(7,2); 
SELECT * FROM AnonymousTable ORDER BY order; 
EXECUTE PROCEDURE move_old_to_new(2,7); 
SELECT * FROM AnonymousTable ORDER BY order; 

ROLLBACK WORK; 

Los pares de invocaciones del procedimiento almacenado con los números invertidos restablecieron el orden original cada vez. Claramente, podría redefinir la variable v_inc para que en lugar de ser solo ± 1, fuera 'LET v_inc = v_inc - v_min + v_gap;' y luego la expresión MOD sería simplemente 'MOD(order + v_inc, v_gap)'. No he comprobado si esto funciona con números negativos.

La adaptación a MySQL u otro DBMS se deja como un ejercicio para el lector.

+0

lo intentaré, pero parece que la solución que estaba buscando, la veré resuelta cuando la tenga funcionando, ¡muchas gracias! – markcial

+0

Es una buena idea usar el "entre" para renumerar solo las filas en el rango que está cambiando. –

+0

En mysql (que parece estar usando), la restricción de exclusividad se impone a medida que se cambia cada fila, pero ** puede ** controlar la orden de procesamiento colocando un "ORDER BY" en la ACTUALIZACIÓN. –

0

tal vez hacer 2 sentencias de actualización:

UPDATE `table` SET ord=ord+1 WHERE ord >= 1 order by ord desc; 
UPDATE `table` SET ord=1 WHERE id=3; 

(probablemente desea agrupar estas dos operaciones en una sola transacción en lugar de utilizar la confirmación automática)

EDIT: añadir "ordenar por" a la primera actualización controle el orden de actualización, evite el "problema de clave duplicada. (también, evite la palabra clave" order "en los nombres de columna :-)

+1

Eso se mueve de 4 a 5. Si hay una restricción única en el orden, entonces hay una violación de esa restricción por lo que la primera instrucción falla. –

+0

Jonathan Leffler tiene razón, pero veo un patrón que podría intentar, use 2 restricciones en la cláusula where. Intentaré algo y responderé aquí con resultados, ¡gracias! – markcial

+0

Puede controlar el orden en que ocurren las actualizaciones colocando un "orden por" en la ACTUALIZACIÓN. Vea el ejemplo en http://dev.mysql.com/doc/refman/5.1/en/update.html –

0

Suponiendo que ha pasado en el id de la fila para cambiar como change_row_id y el nuevo orden como new_order:

current_order = SELECT order from 'table' WHERE id=change_row_id; 
if (current_order > new_order) 
    UPDATE `table` SET order=order+1 WHERE (order > new_order AND 
    order < current_order); 
else 
    [you'll have to figure out how you want to handle this. Do you want the 
    orders to all be sequential with no breaks?] 
ENDYIF; 

UPDATE 'table' SET order=new_order WHERE id=change_row_id; 

No sé mysql, por lo que es posible que deba modificar el sql. Y definitivamente quiere hacer esto en una sola transacción. O cometer todo o nada.

+0

¿Qué sucede si quiere reducir el orden de un elemento? es decir: de la orden 3 a la 4 – OverLex

0

Supongo que lo que está tratando de lograr se realiza mejor no con una consulta DB, sino con una construcción de clasificación simple.

Debe guardar sus registros de db como objetos en una colección o estructura/matriz/lista de algún tipo, depende de su idioma de elección.

Luego crea un algoritmo de clasificación simple, ordena todos los registros en base al orden modificando todos los pedidos de las otras líneas y crea un procedimiento que actualiza todas las líneas modificadas automáticamente después de pasarlo a la misma colección .

0

pseudo código:

CREATE TRIGGER reorder AFTER UPDATE ON `table` 
    FOR EACH ROW BEGIN 
    UPDATE `table` SET order=order+1 WHERE id < 3 ORDER BY id DESC LIMIT 1; 
    END; 
| 

delimiter ; 

anterior Id:

UPDATE `table` SET order=order+1 WHERE id < 3 ORDER BY id DESC LIMIT 1; 
2

Un enfoque diferente es el uso de números de punto flotante en lugar de números enteros para la clasificación. En esta configuración, necesita actualizar solo una fila al cambiar la clasificación. Vamos a empezar con esto:

id order 
1  1 
2  2 
3  3 
4  4 

ahora quiere cambiar el orden de modo que el punto 2 aparece entre el punto 3 y 4. Todo lo que tiene que hacer es actualizar elemento 2 de forma que su nuevo order es un valor entre 3 y 4, por ejemplo 3.5:

id order 
1  1 
2 3.5 
3  3 
4  4 
+0

Interesante pensamiento fuera de la caja! –

+0

+1 para un enfoque interesante -1 para un rendimiento de DB horrible al ordenar por flotadores – Andreas

0

para todos los que tiene el mismo problema que la threadstarter pero no utiliza un IDS como DBMS como @ Jonathan Leffler: Aquí es un Pastebin de estas normas para MySQL http://pastebin.com/AxkJQmAH

+0

¡Bienvenido a StackOverflow! Incluya el código en su respuesta para que todos tengan acceso a él. Consulte [Cómo responder] (http://stackoverflow.com/help/how-to-answer) para obtener más consejos. –

Cuestiones relacionadas