zapytanie dotyczące połączenia aktualizacji i zapytania wstawiania w jedno zapytanie w mysql

9

chcę śledzić historię zmian dla użytkownika, aby za każdym razem, gdy zmienia on swój profil, muszę brać stare dane i przechowywać je w historii oraz aktualizować o nowe dane.

Mogę użyć a, selectaby uzyskać stare dane, insertprzejść do historii i wreszcie updatezmienić dane.

czy mogę mieć to wszystko w jednym zapytaniu w mysql bez użycia procedur przechowywanych, wyzwalaczy itp., takich jak używanie blokad itp., jeśli tak, to daj mi małą próbkę.

Saravanan
źródło
1
@savaranan: To pytanie jest warte +1, ponieważ stanowi silne przypomnienie dla DBA i programistów, aby korzystali z transakcji i w pełni korzystali z właściwości ACID bazy danych.
RolandoMySQLDBA
2
@savaranan: We wszystkich zamiarach i celach Jack podał TYLKO wiarygodną możliwą odpowiedź. W rzeczywistości Jack Douglas zrobił dodatkowy krok i wymusił przerywaną blokadę w każdym rzędzie o id = 10 dla dodatkowej ochrony MVCC, wykonując WYBIERZ ... DO AKTUALIZACJI. Jego odpowiedź dodatkowo podkreśla punkt, który Jack i ja mówiliśmy przez cały czas: UPDATE i INSERT nie mogą być ani nigdy nie mogą być pojedynczym zapytaniem, mogą być tylko pojedynczą transakcją dla zachowania SQL, które proponuje twoje pytanie.
RolandoMySQLDBA

Odpowiedzi:

13

Aby to zrobić bez ryzyka zablokowania innego użytkownika próbuje zaktualizować sam profil w tym samym czasie, trzeba zablokować wiersz w t1pierwszym, a następnie użyć transakcji (jak Rolando podkreśla w komentarzach do Twojego pytania):

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;
Jack mówi, że spróbuj topanswers.xyz
źródło
Jest to po prostu genialne w dalszym blokowaniu każdego wiersza o id = 10. To powinno być +2. Wszystko, co mogę dać, to +1!
RolandoMySQLDBA
1

Nie wierzę, że istnieje sposób na połączenie wszystkich trzech stwierdzeń. Najbliższa rzecz tak naprawdę nie pomaga, a jest to WYBÓR ZESTAWU. Twój najlepszy zakład to wyzwalacz. Poniżej znajduje się przykład wyzwalacza, którego często używam do utrzymania właśnie takiej ścieżki audytu (zbudowanej z 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 ;';
Bryan Agee
źródło
-3

Przekonałem się, że to zapytanie działa na serwerach SQL i MySQL INSERT INTO t2 SELECT * FROM t1 WHERE id=10; UPDATE t1 SET id=11 WHERE id=10;

Mam nadzieję, że przyda się to innym także w przyszłości.

Saravanan
źródło
4
To nie jest tak naprawdę zapytanie. W rzeczywistości są to dwa zapytania, które należy traktować jako transakcję.
RolandoMySQLDBA
@rolandomysqldba: to działa dobrze jako pojedyncze zapytanie, gdy wysyłam do serwera db z kodu aplikacji, gdzie traktuję ten zestaw jako pojedyncze zapytanie. Dlaczego tak mówisz?. czy możesz to obalić z ważnych powodów ...
Saravanan
2
@ Saravanan: W oczach InnoDB lub dowolnego RDBMS zgodnego z ACID (Oracle, SQLServer, PostreSQL, Sybase itp.), niemożliwe jest wywołanie tych dwóch instrukcji SQL jednym zapytaniem. Jako baza danych zgodna z ACID traktowałaby je jak dwie instrukcje. Domyślnie InnoDB ma włączone automatyczne zatwierdzanie. Pierwsza instrukcja, INSERT, zostałaby wykonana jako pojedyncza transakcja. Generowane byłyby dane wieloczęściowej kontroli współbieżności (MVCC) w celu przechowywania kopii oryginalnych danych w tabeli t2 na zasadzie rząd po rzędzie. Jeśli MySQL ulegnie awarii podczas wykonywania INSERT, InnoDB wykorzystuje dane MVCC do przywrócenia t2 do pierwotnego stanu.
RolandoMySQLDBA
1
@saravanan: Załóżmy, że INSERT działa poprawnie. Dane, które wynikły z INSERT, zostały zatwierdzone (z włączonym automatycznym zatwierdzaniem) i tabela ochronna t2 MVCC jest odrzucana. Podczas wykonywania UPDATE, MVCC jest generowany na podstawie tabeli t1 i wykonywana jest UPDATE. Jeśli MySQL ulegnie awarii podczas aktualizacji, InnoDB używa danych MVCC na t1 do wycofania aktualizacji. Nawet jeśli AKTUALIZACJA zmienia tylko jeden wiersz, istnieje możliwość przeniesienia rekordów z t1 do t2 o id 10 i niezmieniania id 10 na id 11 w t1. Aby zapobiec temu wyjątkowemu scenariuszowi, musisz wykonać następujące czynności ...
RolandoMySQLDBA 30.06.11
@savaranan: Traktuj dwie instrukcje SQL jako pojedynczą transakcję. Prostym sposobem na to jest: ROZPOCZNIJ; WSTAW DO t2 WYBIERZ * Z t1 GDZIE id = 10; AKTUALIZACJA t1 SET id = 11 GDZIE id = 10; POPEŁNIĆ; Najsilniejszym powodem traktowania dwóch instrukcji SQL jako pojedynczej transakcji jest fakt, że MVCC utworzone dla INSERT pozostałoby podczas UPDATE. Jeśli awaria MySQL wystąpi podczas aktualizacji w ramach transakcji (BEGIN; ... COMMIT; block), MVCC przywróci wszystkie zmiany do spójnego stanu. Jeśli zarówno INSERT, jak i UPDATE są zakończone, MVCC jest odrzucane w ostatniej chwili.
RolandoMySQLDBA