2012-05-31 14 views
6

siempre que Tengo el siguiente conjunto de resultados de una tabla de base de datos MySQL:valores de desplazamiento de la cadena en una tabla

+----+------+-------+ 
| ID | type | value | 
+----+------+-------+ 
| 8 | A | 1435 | 
| 9 | B | 7348 | 
| 10 | A | 1347 | 
| 11 | A | 3478 | 
| 12 | A | 4589 | 
| 13 | B | 6789 | 
+----+------+-------+ 

Me gustaría eliminar fila ID 8 y empujar los valores en el campo 'valor' hacia abajo, de tal manera que cada fila tenga el valor de la entrada anterior, pero que afecte solo a aquellas en las que el campo 'tipo' es el mismo que la fila que se borra ('A' en este caso).

Es decir, la eliminación de la fila Identificación del 8 finalmente debe producir lo siguiente:

+----+------+-------+ 
| ID | type | value | 
+----+------+-------+ 
| - | - | - | * 
| 9 | B | 7348 | | 
| 10 | A | 1435 | * | 
| 11 | A | 1347 | * | 
| 12 | A | 3478 | * | 
| 13 | B | 6789 | V 
+----+------+-------+ 

ID 10 ha heredado el valor de ID 8, a continuación, ID 11 hereda de ID 10, y así sucesivamente. Sin embargo, observe cómo las filas que tienen el tipo 'B' no se ven afectadas.

Entonces, la pregunta: ¿hay alguna manera de realizar este "cambio" de valores sin tener que consultar y actualizar cada fila una por una? En un mundo ideal, haría una consulta para hacer un cambio y luego otra para eliminar la fila, pero no estoy muy seguro de si esto es posible en absoluto.

(también preferiría no utilizar disparadores, ya que tengo la intención encapsular toda la lógica de la aplicación dentro de la propia aplicación)

+0

¿En la consulta? Lo dudo. Cada actualización es una nueva consulta. Recomendaría un procedimiento SQL, pero si no los quiere, supongo que están atrapados en una construcción de bucle. Para acelerar las cosas, usa las transacciones. – clentfort

+0

Creo que es posible recopilar todas las filas que necesitan actualizarse en una temptable (#temptable). A partir de ahí, puede calcular el nuevo valor y usar una única declaración de actualización para actualizar su tabla principal de la temptable. Entonces no necesitas una contstrucción de bucle. –

+0

EDITAR: Probablemente pueda usar ROW_NUMBER() para cambiar las filas. Como esto no está disponible en mysql, compruebe la respuesta de OMGPonies en esta pregunta: http://stackoverflow.com/questions/1895110/row-number-in-mysql –

Respuesta

2
SET @remove_id = 8; 

SELECT ID, type, value FROM (
    SELECT ID, 
      type, 
      CAST(IF(type <> @type OR ISNULL(@val), value, @val) AS UNSIGNED) 
      AS value, 
      @type := IF(ID = @remove_id, type, @type), 
      @val := IF(type = @type, value, @val) 
    FROM  my_table JOIN (SELECT @type := NULL, @val := NULL) AS z 
    ORDER BY ID ASC 
) AS t 
WHERE ID <> @remove_id 

verlo en sqlfiddle.


ACTUALIZACIÓN

me había dado cuenta de que realmente querían actualizar la tabla subyacente. Para eso, puede usar algunos hackers leves para hacer lo mismo de manera efectiva en una declaración UPDATE (no se puede asignar a las variables de usuario directamente, así que en lugar de asignar a una columna la concatenación de su nuevo valor y una cadena nula formada al tomar el 0 primeros caracteres de la variable de usuario recién asignado):

SET @remove_id = 8, @type = NULL, @val = NULL; 

UPDATE my_table SET 
    value = IF(
    type <> @type OR ISNULL(@val), 
    value, 
    CONCAT(@val, LEFT(@val := value, 0)) 
), 
    type = CONCAT(type, LEFT(
    @type := IF(
     ID <> @remove_id, 
     @type, 
     CONCAT(type, LEFT(@val := value, 0)) 
    ) 
    , 0)) 
ORDER BY ID ASC; 

DELETE FROM my_table WHERE ID = @remove_id; 

verlo en sqlfiddle.

+0

Subió porque esto es muy bueno para una sola consulta, pero en realidad estaba buscando algo que no solo seleccionara sino que también grabara el resultado en la base de datos. – Mahn

+0

@Mahn: no me había dado cuenta de eso. Vea mi respuesta actualizada para una solución algo hack-ish, pero esperemos que sea un poco más fácil que su respuesta aceptada. :) – eggyal

+0

Hermoso, gracias. Parece que hay mucho que se puede hacer con las expresiones condicionales dentro de las consultas, probablemente debería analizarlas más detenidamente. – Mahn

0

Yo sugeriría que utilice las tablas InnoDB para que pueda ejecutar las 2 consultas en una sola transacción. Me gustaría hacer esto:

Step 1: Start a transaction on the table 
Step 2: Get the record that is to be deleted (e.g. ID #8) 
Step 3: Issue a query DELETE FROM tablename WHERE `ID`=$record_id 
Step 4: Issue a query UPDATE tablename SET `value`='former_value' WHERE `type`='former_type' LIMIT 1 
Step 5: if all operations were successful, commit the transaction else rollback 
Step 6: End the transaction 

Esperanza esto ayuda

+0

Incluso si usa una transacción, varias consultas aún significan varios viajes. al DB que quería evitar Gracias por responder sin embargo. – Mahn

2

Esta tarea se lleva a cabo muy fácilmente usando funciones de ventana/analíticas. Como MySQL no tiene tales, se pueden simular con la siguiente restricción:

  • debe tener un campo único para ordenar.

He usado id para este propósito.

La siguiente consulta enumarte cada fila de la tabla, usando type como un indicador de partición:

SELECT t.id, t.type, t.value, 
    (SELECT count(*) FROM testbed WHERE type = t.type AND id <= t.id) AS rownum 
    FROM testbed t 
ORDER BY t.type, t.id; 

He añadido ORDER BY sólo para visibilidad, no se requiere en la consulta final.

consulta siguiente le permite unir 2 resultados y tienen lugar a “valores de desplazamiento” de la manera necesaria:

SELECT c.id AS c_id, c.type AS c_type, c.value AS c_value, 
     p.id AS p_id, p.type AS p_type, p.value AS p_value 
    FROM (SELECT t.id, t.type, t.value, 
       (SELECT count(*) FROM testbed 
       WHERE type = t.type AND id <= t.id) AS rownum 
      FROM testbed t) AS c 
    LEFT JOIN (SELECT t.id, t.type, t.value, 
        (SELECT count(*) FROM testbed 
         WHERE type = t.type AND id <= t.id) AS rownum 
       FROM testbed t) AS p 
     ON c.type = p.type AND c.rownum = p.rownum + 1 
ORDER BY c.type, c.id; 

Por último, su tarea se logra con las siguientes 2 consultas, UPDATE y DELETE:

UPDATE testbed AS t 
JOIN (
    SELECT c.id AS c_id, c.type AS c_type, c.value AS c_value, 
     p.id AS p_id, p.type AS p_type, p.value AS p_value 
    FROM (SELECT t.id, t.type, t.value, 
       (SELECT count(*) FROM testbed 
        WHERE type = t.type AND id <= t.id) AS rownum 
      FROM testbed t) AS c 
    LEFT JOIN (SELECT t.id, t.type, t.value, 
         (SELECT count(*) FROM testbed 
         WHERE type = t.type AND id <= t.id) AS rownum 
       FROM testbed t) AS p 
      ON c.type = p.type AND c.rownum = p.rownum + 1 
) AS s 
    ON t.id = s.c_id 
    SET t.value = s.p_value 
WHERE t.value = 'A'; -- you can use more complex predicate(s) here 

DELETE FROM testbed WHERE id = 8; -- make sure both predicate(s) match 

Puede consultar esta consulta en SQL Fiddle (no en las actualizaciones).

+0

Esto hace el truco, gracias! Aunque me temo que puede ser difícil de mantener, pero es probable que sea tan bueno como puede ser. – Mahn

+0

Acepté la respuesta anterior porque valoro la brevedad (más fácil de mantener), pero aún así tengo mi voto positivo. – Mahn

Cuestiones relacionadas