query relativa alla combinazione di un aggiornamento e una query di inserimento in una singola query in mysql


9

Voglio tenere traccia della cronologia delle modifiche per un utente, in modo che ogni volta che cambia il suo profilo, devo prendere i vecchi dati e archiviarli nella cronologia e aggiornarli con nuovi dati.

Posso usare a selectper ottenere i vecchi dati, uno insertper la cronologia e infine un updateper modificare i dati.

posso avere tutto questo in una singola query in mysql senza usare stored procedure, trigger, ecc. come usare i blocchi ecc. se così mi dai un piccolo campione.


1
@savaranan: questa domanda è degna di un +1 perché presenta un forte promemoria a DBA e sviluppatori per utilizzare le transazioni e sfruttare appieno le proprietà ACID del database.
RolandoMySQLDBA,

2
@savaranan: A tutti gli effetti, Jack ha fornito l'UNICA risposta plausibile che ci sia. In effetti, Jack Douglas ha fatto un ulteriore passo e ha forzato un blocco intermittente su ogni riga con id = 10 per una protezione MVCC aggiuntiva facendo SELEZIONARE ... PER AGGIORNARE. La sua risposta accentua ulteriormente il punto che Jack e io abbiamo sempre detto: un AGGIORNAMENTO e un INSERT non possono essere, né essere mai, una singola query, possono essere solo una singola transazione per il comportamento SQL proposto dalla tua domanda.
RolandoMySQLDBA

Risposte:


13

Per fare questo, senza il rischio di bloccare un altro utente tenta di aggiornare lo stesso profilo, allo stesso tempo, è necessario bloccare la fila in t1prima, quindi utilizzare una transazione (come sottolinea Rolando nei commenti alla tua domanda):

start transaction;
select id from t1 where id=10 for update;
insert into t2 select * from t1 where id=10;
update t1 set id = 11 where id=10;
commit;

Questo è semplicemente geniale nel bloccare ulteriormente ogni riga con id = 10. Dovrebbe essere un +2. Tutto quello che posso dare è un +1 !!!
RolandoMySQLDBA

1

Non credo ci sia un modo per combinare tutte e tre le affermazioni. La cosa più vicina a ciò non ti aiuta davvero, e questo è SET SELECT. La tua scommessa migliore è un trigger. Di seguito è riportato un esempio di trigger che utilizzo spesso per mantenere solo una tale pista di controllo (costruita con PHP):

$trigger = "-- audit trigger --\nDELIMITER $ \n".
    "DROP TRIGGER IF EXISTS `{$prefix}_Audit_Trigger`$\n".
    "CREATE TRIGGER `{$prefix}_Audit_Trigger` AFTER UPDATE ON `$this->_table_name` FOR EACH ROW BEGIN\n";

foreach ($field_defs as $field_name => $field) {
    if ($field_name != $id_name) {
       $trigger .= "IF (NOT OLD.$field_name <=> NEW.$field_name) THEN \n".'INSERT INTO AUDIT_LOG ('.
                    'Table_Name, Row_ID, Field_Name, Old_Value, New_Value, modified_by, DB_User) VALUES'.
                    "\n ('$this->_table_name',OLD.$this->_id_name,'$field_name',OLD.$field_name,NEW.$field_name,".
                    "NEW.modified_by, USER()); END IF;\n";
    }
}
$trigger .= 'END$'."\n".'DELIMITER ;';

-3

Ho scoperto che questa query funziona su server SQL e MySQL INSERT INTO t2 SELECT * FROM t1 WHERE id=10; UPDATE t1 SET id=11 WHERE id=10;

Spero che questo sia utile a qualcun altro anche in futuro.


4
Questa non è davvero una domanda. In realtà si tratta di due query che devono essere trattate come una transazione.
RolandoMySQLDBA il

@rolandomysqldba: funziona perfettamente come una singola query quando invio a un server db dal codice dell'applicazione, dove considero questo set come una singola query. perchè dici così?. puoi confutare questo con forti ragioni ..
Saravanan,

2
@saravanan: Agli occhi di InnoDB o di qualsiasi RDBMS conforme ACID (Oracle, SQLServer, PostreSQL, Sybase, ecc.), è impossibile chiamare quelle due istruzioni SQL una query. Come un database conforme ACID li tratterebbe come due dichiarazioni. Per impostazione predefinita, InnoDB ha autocommit attivato. La prima istruzione, INSERT, verrebbe eseguita come una singola transazione. I dati di controllo della concorrenza multiversione (MVCC) verrebbero generati per mantenere una copia dei dati originali nella tabella t2 su una riga per riga. Se MySQL si arresta in modo anomalo durante l'esecuzione di INSERT, InnoDB utilizza i dati MVCC per ripristinare t2 allo stato originale.
RolandoMySQLDBA

1
@saravanan: Supponiamo che INSERT abbia funzionato correttamente. I dati risultanti da INSERT sono stati sottoposti a commit (con autocommit ON) e la tabella di protezione MVCC t2 viene scartata. Quando si esegue UPDATE, MVCC viene generato sulla tabella t1 e viene eseguito UPDATE. Se MySQL si arresta in modo anomalo durante UPDATE, InnoDB utilizza i dati MVCC su t1 per eseguire il rollback di UPDATE. Anche se l'AGGIORNAMENTO cambia solo una riga, esiste la possibilità one-in-a-milloion di spostare i record da t1 a t2 con id 10 e non cambiare id 10 in id 11 in t1. Per evitare questo scenario unico, è necessario effettuare le seguenti operazioni ...
RolandoMySQLDBA

@savaranan: considera le due istruzioni SQL come un'unica transazione. Un modo semplice per farlo è: INIZIA; INSERISCI IN t2 SELEZIONA * DA t1 DOVE id = 10; AGGIORNAMENTO t1 SET id = 11 DOVE id = 10; COMMETTERE; Il motivo più forte per trattare le due istruzioni SQL come un'unica transazione è il fatto che l'MVCC creato per INSERT rimarrebbe in vigore durante l'AGGIORNAMENTO. Se dovesse verificarsi un arresto anomalo di MySQL durante l'AGGIORNAMENTO all'interno di una transazione (INIZIO; ... COMMIT; blocco) MVCC eseguirà il rollback di tutte le modifiche a uno stato coerente. Se INSERT e UPDATE sono completati, MVCC viene scartato all'ultimo momento.
RolandoMySQLDBA,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.