2012-05-31 10 views
6

a condizione che ho il seguente set di risultati da una tabella di database mysql:valori di spostamento lungo la catena in una tabella

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

Vorrei eliminare la riga ID 8 e spingere i valori nel campo 'valore' in basso, in modo tale che ogni riga abbia il valore della voce precedente, ma influisce solo su quelli in cui il campo 'tipo' è uguale alla riga che si sta eliminando ('A' in questo caso).

Vale a dire, la cancellazione fila id 8 alla fine dovrebbe produrre il seguente:

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

ID 10 ha ereditato il valore ID 8, quindi ID 11 eredita da ID 10, e così via. Si noti tuttavia come le righe con tipo "B" non sono interessate.

Quindi la domanda: esiste un modo per eseguire questo "spostamento" dei valori senza dover interrogare e aggiornare ogni riga una per una? In un mondo ideale vorrei fare una query per fare shift e poi un'altra per cancellare la riga, ma non sono del tutto sicuro se questo è possibile.

(Anche io preferirei non utilizzare i trigger, dal momento che ho intenzione incapsulare tutta la logica dell'applicazione all'interno dell'applicazione stessa)

+0

Su richiesta? Ne dubito. Ogni aggiornamento è una nuova query. Vorrei raccomandare una procedura SQL, ma se non vuoi quelli, credo che sei bloccato ad una costruzione ad anello. Per velocizzare le cose, usa le transazioni. – clentfort

+0

Penso che sia possibile raccogliere tutte le righe che devono essere aggiornate in un temptable (#temptable). Da lì, è possibile calcolare il nuovo valore e utilizzare una singola istruzione di aggiornamento per aggiornare la tabella principale dal temptable. Quindi non hai bisogno di una istruzione di loop. –

+0

MODIFICA: puoi probabilmente utilizzare ROW_NUMBER() per spostare le righe. Dal momento che questo non è disponibile in mysql, controlla la risposta di OMGPonies a questa domanda: http://stackoverflow.com/questions/1895110/row-number-in-mysql –

risposta

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 

vedi sul sqlfiddle.


UPDATE

Non avevo capito che in realtà ha voluto aggiornare la tabella sottostante. Per fare ciò, è possibile utilizzare un po 'di hackery per fare effettivamente la stessa cosa in un'istruzione UPDATE (non è possibile assegnare direttamente le variabili utente, quindi assegnare a una colonna la concatenazione del suo nuovo valore e una stringa nulla formata dall'assunzione del primi 0 caratteri della variabile utente appena assegnato):

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; 

Vedi sulla sqlfiddle.

+0

Upvoted perché questo è piuttosto interessante per una singola query, ma in realtà stavo cercando qualcosa che non solo avrebbe selezionato, ma avrebbe anche registrato il risultato sul DB. – Mahn

+0

@Mahn: non me ne ero reso conto. Vedere la mia risposta aggiornata per un po 'hack-ish, ma si spera una soluzione un po' più semplice della risposta accettata. :) – eggyal

+0

Bello, grazie. Sembra che ci sia molto che si possa fare con le espressioni condizionali all'interno delle query stesse, probabilmente dovrei esaminarle maggiormente. – Mahn

0

vorrei suggerire di utilizzare le tabelle InnoDB in modo da poter eseguire le 2 domande in una singola transazione. farei questo:

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 

Spero che questo aiuti

+0

Anche se usi una transazione, molte query significano ancora diversi viaggi al DB che volevo evitare. Grazie per aver risposto. – Mahn

2

Questa operazione viene eseguita molto facilmente utilizzando le funzioni finestra/analitiche. Dato che MySQL non ha tale, possono essere simulati con la seguente restrizione:

  • si dovrebbe avere un campo unico su cui ordinare.

Ho utilizzato id per questo scopo.

La seguente query enumarte ogni riga della tabella, utilizzando type come indicatore di partizione:

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; 

Ho aggiunto ORDER BY solo per visibilità, non è richiesto nella query finale.

interrogazione successiva consente di unire 2 risultati ed avere modo di “valori di spostamento”, il modo necessario:

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; 

Infine, il vostro compito è compiuta con i seguenti 2 domande, UPDATE e 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 

È possibile verificare questa query su SQL Fiddle (non gli aggiornamenti).

+0

Questo fa il trucco, grazie! Anche se temo che possa diventare un dolore da mantenere, ma probabilmente è il massimo che può ottenere. – Mahn

+0

Ho accettato la risposta precedente perché apprezzo la brevità (più facile da mantenere), ma tu hai comunque la mia upvote. – Mahn

Problemi correlati