2009-04-14 11 views
16

Tengo una tabla con digamos 20 filas cada una con un número para el orden de visualización (1-20).Actualizando el orden de visualización de múltiples filas de MySQL en una o muy pocas consultas

SELECT * FROM `mytable` ORDER BY `display_order` DESC; 

Desde un área de administración puede arrastrar las filas o escribir un nuevo número manualmente para cada fila.

Seguramente no es bueno repetir una consulta de ACTUALIZACIÓN para cada fila, ¿cuál es una alternativa en una o muy pocas consultas adecuadas para actualizar una celda en 20 filas o incluso más, 50-200 +?


Editar: Un montón de buenas respuestas e ideas. Es posible que amplíe las ideas que he considerado hasta ahora:

Una cadena de matriz: podría tener el orden en una cadena que enumere las ID de fila únicas en el orden que deseo, por ejemplo, las filas 1,9,2, 6,23. Cuando la orden se actualiza, unas actualizaciones campo oculto con JavaScript y añade que a la base de datos o un archivo de texto cuando termina:

UPDATE `my_dispaly_order_table` SET `display_order`='1,9,2,6,23'; 

actualización de cada fila individual: Esto es lo que estaba tratando de evitar, pero sería sólo puede ser cambiado con muy poca frecuencia tan 20-30 llamadas en un solo golpe una vez a la semana o mes no puede ser un problema por lo que simplemente llamando UPDATE en cada fila es lo que suele hacer:

UPDATE `mytable` SET `display_order`='1' WHERE `rowId` = 1; 
UPDATE `mytable` SET `display_order`='2' WHERE `rowId` = 9; 
UPDATE `mytable` SET `display_order`='3' WHERE `rowId` = 2; 
UPDATE `mytable` SET `display_order`='4' WHERE `rowId` = 6; 
UPDATE `mytable` SET `display_order`='5' WHERE `rowId` = 23; 
+0

este es un problema de lista enlazada. tal vez implica un cambio de esquema. – Randy

Respuesta

0

se puede crear una tabla temporal y rellene con la clave principal de las filas que desea cambiar, y los nuevos valores de display_order para esas filas, luego use la versión multi-tabla de ACTUALIZAR para actualizar la tabla de una vez. No asumiría que esto es más rápido, sin embargo, no sin probar ambos enfoques.

0

Agregue un id (u otra clave) a la tabla, y actualice donde id (o clave) = id (o clave) de la fila modificada.

Por supuesto, deberá asegurarse de que no haya valores de display_order duplicados o de que esté bien con los vínculos en display_order en cualquier orden, o introduzca un segundo desempate para el orden por lista.

1

Puede intentar incluirlo en algunas afirmaciones, no creo que sea posible en una sola. Entonces, por ejemplo, digamos que va a actualizar la décima fila. Usted quiere que cada registro después de 10 sea incrementado.

UPDATE table SET col=col+1 WHERE col > 10 
UPDATE table SET col=10 WHERE id = X 
... 

Pero es realmente difícil rodar con toda la lógica necesaria. Debido a que algunos registros tal vez necesiten una disminución, etc. Desea evitar duplicados, etc.

Piense en esto en términos de tiempo de desarrollador frente a ganancia.

Porque incluso si alguien ordena esto una vez por día, la sobrecarga es mínima, en comparación con la fijación en un procedimiento almacenado, o pseudo-optimización de esta función para que no se ejecuten 20 consultas. Si esto no se ejecuta 100 veces al día, 20 consultas están perfectamente bien.

0

Puede borrar y volver a insertar todas las filas; eso haría toda la operación en solo dos consultas (o tres si necesita seleccionar todos los datos). No contaría con que sea más rápido, y tendría que hacerlo dentro de una transacción o se dirigirá a sus copias de seguridad en poco tiempo. También podría conducir a la fragmentación de la tabla.

Una mejor opción sería registrar cada cambio como una historia y luego hacer algo como esto:

ejemplo, la posición 10 se mueve hacia abajo dos a 12

UPDATE table SET display_order = display_order -1 WHERE display_order BETWEEN 10 AND 12 
UPDATE table SET display_order = 12 WHERE row_id = [id of what was row 10] 
10

primer lugar, debe asegurarse de que la columna no tiene índice ÚNICO, de lo contrario, mysql le dirá que la restricción se ha roto durante la consulta. Después de eso, puede hacer cosas como:

-- Move #10 down (i.e. swap #10 and #11) 
UPDATE mytable SET display_order = 
    CASE display_order 
    WHEN 10 THEN 11 
    WHEN 11 THEN 10 
    END CASE 
WHERE display_order BETWEEN 10 AND 11; 

-- Move #4 to #10 
UPDATE mytable SET display_order 
    CASE display_order 
    WHEN 4 THEN 10 
    ELSE display_order - 1 
    END CASE 
WHERE display_order BETWEEN 4 AND 10; 

Pero en realidad debe asegurarse de hacer las cosas en un solo paso. Si se intercambian dos pasos, la numeración se romperá si no se usan los ID. es decir .:

-- Swap in two steps will not work as demostrated here: 

UPDATE mytable SET display_order = 10 WHERE display_order = 11; 
-- Now you have two entries with display_order = 10 

UPDATE mytable SET display_order = 11 WHERE display_order = 10; 
-- Now you have two entries with display_order = 11 (both have been changed) 

Y aquí es una referencia a la CASE statement of mysql.

+0

He estado golpeando mi cabeza contra mi propio índice 'UNIQUE' tratando de usar PHP para resolver este problema de lógica. ¡Debería simplemente eliminar ese índice como dijiste! Estaba preocupado por la introducción de errores en la base de datos. – Xeoncross

0

Recoja la nueva orden en una variable temporal y coloque el botón "guardar esta orden" en el área de administración. A continuación, guarde el orden de las filas con una ronda.

Obtendrá mejores tiempos de respuesta, cambios imposibles de hacer, menos filas modificadas (porque en el nivel bajo en las bases de datos, prácticamente no era posible realizar actualizaciones, pero guarde una nueva instancia de toda la fila y elimine la anterior) .

Después de todo, sería una solución de menor costo para un reordenamiento completo y ahorraría algo de codificación en la optimización de la actualización.

1

estoy pensando en este problema, y ​​la solución que se me ocurrió es tener un número decimal como el orden y cambiar el número del cambio de artículo para un número entre el siguiente y el elemento anterior

Order Item 
----- ---- 
1  Original Item 1 
2  Original Item 2 
3  Original Item 3 
4  Original Item 4 
5  Original Item 5 

Si cambia el artículo 4 a la segunda posición, se obtiene:

Order Item 
----- ---- 
1  Original Item 1 
1.5  Original Item 4 
2  Original Item 2 
3  Original Item 3 
5  Original Item 5 

si cambia el tema 3 a la tercera posición, se obtiene:

Order Item 
----- ---- 
1  Original Item 1 
1.5  Original Item 4 
1.75  Original Item 3 
2  Original Item 2 
5  Original Item 5 

Teóricamente siempre hay un decimal entre dos decimales, pero podría enfrentar algunos límites de almacenamiento.

De esta manera solo tiene que actualizar la fila que se reordena.

+0

Supongo que dos problemas con esto es que todavía necesitarás actualizar algunos campos, pero al menos no todos ... ¡podría volverse un poco peludo después de un tiempo! Tenía un concepto similar, pero comencé en unidades de 100, por lo que al menos tenía enteros en lugar de decimales, pero no demasiado diferentes ... Diría que aún desea actualizar el orden de vez en cuando para que sean números constantes normales. Gracias por la contribución. –

+0

Puede que no funcione tan bien para la ordenación frecuente, así que sigues agregando o cambiando elementos a la parte superior (noticias, por ejemplo) para que termines con 0.1, 0.0001, pero entonces supongo que puede llegar a los miles ... aunque parece una idea ligeramente del lado izquierdo, en realidad me estoy inclinando por esta solución ... –

+0

Estoy de acuerdo en que puede ponerse un poco peludo después de un tiempo, pero no estoy seguro de si el usuario volverá a pedir tanto (I Supongo que depende del tipo de sitio, etc.) y puede ejecutar una consulta de "normalización" cada mes para mantener las cosas más limpias. –

1

Si necesita arrastrar filas, esta es una buena implementación para un linked list.

Tener sus filas ordenadas con linked list significa que va a actualizar como máximo 3 filas a la vez, incluso si mueve todo el bloque (siempre que sea contiguo).

Crear una tabla de sus filas como esto:

CREATE TABLE t_list (
     id INT NOT NULL PRIMARY KEY, 
     parent INT NOT NULL, 
     value VARCHAR(50) NOT NULL, 
     /* Don't forget to create an index on PARENT */ 
     KEY ix_list_parent ON (parent) 
) 

id parent value 

1 0  Value1 
2 3  Value2 
3 4  Value3 
4 1  Value4 

y utilizar esta consulta MySQL para seleccionar las filas en orden:

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

Este atravesará su lista enlazada y devolver los artículos pedidos :

id parent value 

1 0  Value1 
4 1  Value4 
3 4  Value3 
2 3  Value2 

Para mover una fila, deberá actualizar el parent de la fila, el parent de su hijo actual, y el parent de la fila que está insertando anteriormente.

Ver esta serie de artículos en mi blog en cómo hacerlo de manera eficiente en MySQL:

Hay un montón de artículos porque hay algunos problemas con el bloqueo de filas que deben ser manejados de forma ligeramente diferente para cada caso.

+0

Esto parece muy complicado ... mi idea principal es agregar más trabajo en lugar de actualizar el orden de un elemento, actualiza 3 campos por cada cambio de orden ... –

+0

3 campos en lugar de 1 no es un problema en 2009 :) Si va a tener más de 1,000 filas y actualizaciones frecuentes, la lista enlazada es la mejor decisión, ya que siempre hay 3 campos sin importar qué. Otras soluciones requerirán la actualización de todos los campos o una clasificación binaria que probablemente se quede sin valores. – Quassnoi

11

La respuesta de soulmerge me hizo pensar y creo que esta es una solución mejor. Lo que debe hacer es seleccionar las filas con el ID usando IN() y luego usar CASE para establecer el valor.

UPDATE mytable SET display_order = 
    CASE id 
    WHEN 10 THEN 1 
    WHEN 23 THEN 2 
    WHEN 4 THEN 3 
    END CASE 
WHERE id IN (10, 23, 4) 

Tengo una necesidad de esto en mi aplicación actual. En PHP, obtengo un conjunto serializado (y ordenado) de identificadores de la función Clasable integrada de jQuery UI. Así que la matriz se ve así:

$new_order = array(4, 2, 99, 15, 32); // etc 

para generar la única consulta de actualización de MySQL, hago esto:

$query = "UPDATE mytable SET display_order = (CASE id "; 
foreach($new_order as $sort => $id) { 
    $query .= " WHEN {$id} THEN {$sort}"; 
} 
$query .= " END CASE) WHERE id IN (" . implode(",", $new_order) . ")"; 

El "implosión" al final sólo me da a mi lista de ID para IN() . Esto funciona maravillosamente para mí.

+1

¡Esto es inteligente! Lanzo una actualización incluso cuando se ordena la lista o se elimina un elemento de la lista, solo una consulta. – Kumar

+0

No creo que la sintaxis sea correcta aquí. Por ejemplo, debería ser simplemente "END" y no "END CASE". – Andrew

+0

Para MySQL, debe usar 'END CASE'. Ref: http://dev.mysql.com/doc/refman/5.7/en/case.html –

1

Esta es una gran manera de ordenar elementos en una base de datos. Exactamente lo que estaba buscando;)

Aquí hay una versión mejorada de la versión de Jamon Holmgren. Esta función es totalmente dinámica:

function reOrderRows($tablename, $ordercol, $idsarray){ 

    $query = "UPDATE $tablename SET $ordercol = (CASE $ordercol "; 
    foreach($idsarray as $prev => $new) { 
     $query .= " WHEN $prev THEN $new\n"; 
    } 
    $query .= " END) WHERE $ordercol IN (" . implode(",", array_keys($idsarray)) . ")"; 

    mysql_query($query); 
} 

Esto supone que tiene una columna $ ordercol en su mesa $ nombredetabla sólo para propósitos de pedidos.

El $ idsarray es una matriz en la matriz de formato ("current_order_num" => "updated_order_num").

Así que si solo quieres intercambiar dos líneas en tu tabla (imagina que tienes, por ejemplo, un ícono de "mover hacia arriba" y "mover hacia abajo" en tu página web), puedes usar esta función encima de la anterior :

function swapRows($tablename, $ordercol, $firstid, $secondid){ 
    $swaparray = array("$firstid" => "$secondid", "$secondid" => "$firstid"); 
    reOrderRows($tablename, $ordercol, $swaparray); 
} 
4

un poco tarde, pero puede ser útil para otra persona:

UPDATE mytable SET display_order = FIND_IN_SET(rowId, '1,9,2,6,23') WHERE rowId in (1,9,2,6,23) 
Cuestiones relacionadas