Jak przetestować instrukcję SQL Update przed jej uruchomieniem?

95

W niektórych przypadkach uruchomienie instrukcji UPDATE w środowisku produkcyjnym może uratować sytuację. Jednak zepsuta aktualizacja może być gorsza niż początkowy problem.

Jakie są opcje, oprócz korzystania z testowej bazy danych, aby sprawdzić, co zrobi instrukcja aktualizacji przed jej uruchomieniem?

static_rtti
źródło

Odpowiedzi:

53

Oprócz korzystania z transakcji, jak powiedział Imad (co i tak powinno być obowiązkowe), możesz również sprawdzić poprawność, na które wiersze wpływa uruchomienie selekcji przy użyciu tej samej klauzuli WHERE, co UPDATE.

Więc jeśli UPDATE to

UPDATE foo
  SET bar = 42
WHERE col1 = 1
  AND col2 = 'foobar';

Poniższe informacje pokażą, które wiersze zostaną zaktualizowane:

SELECT *
FROM foo
WHERE col1 = 1
  AND col2 = 'foobar';
koń bez imienia
źródło
1
Korzystanie z transakcji jest wtedy lepsze do sprawdzania danych. Zakładając, że chce sprawdzić wynik, dochodzę do wniosku, że jego stwierdzenie jest bardziej złożone niż
``
3
@ImadMoqaddem: Zgadzam się i dlatego napisałem „ Oprócz korzystania z transakcji, jak powiedział Imad
a_horse_with_no_name
A jeśli FOREIGN KEY UPDATE CASCADEtwój sql zawodzi
Green
@Green: co masz na myśli mówiąc „porażka”?
a_horse_with_no_name
77

A co z transakcjami? Mają funkcję ROLLBACK.

@zobacz https://dev.mysql.com/doc/refman/5.0/en/commit.html

Na przykład:

START TRANSACTION;
SELECT * FROM nicetable WHERE somthing=1;
UPDATE nicetable SET nicefield='VALUE' WHERE somthing=1;
SELECT * FROM nicetable WHERE somthing=1; #check

COMMIT;
# or if you want to reset changes 
ROLLBACK;

SELECT * FROM nicetable WHERE somthing=1; #should be the old value

Odpowiedź na pytanie od @rickozoe poniżej:

Generalnie te linie nie będą wykonywane raz. W PHP np. Napisałbyś coś takiego (może trochę czyściej, ale chciałem odpowiedzieć szybko ;-)):

$MysqlConnection->query('START TRANSACTION;');
$erg = $MysqlConnection->query('UPDATE MyGuests SET lastname='Doe' WHERE id=2;');
if($erg)
    $MysqlConnection->query('COMMIT;');
else
    $MysqlConnection->query('ROLLBACK;');

Innym sposobem byłoby użycie zmiennych MySQL (patrz https://dev.mysql.com/doc/refman/5.7/en/user-variables.htm l i https://stackoverflow.com/a/18499823/1416909 ):

# do some stuff that should be conditionally rollbacked later on

SET @v1 := UPDATE MyGuests SET lastname='Doe' WHERE id=2;
IF(v1 < 1) THEN
    ROLLBACK;
ELSE
    COMMIT;
END IF;

Ale sugerowałbym użycie opakowań językowych dostępnych w Twoim ulubionym języku programowania.

Marcel Lange
źródło
1
Będzie to miało nieoczekiwane rezultaty w przypadku transakcji zagnieżdżonych.
bułeczki
Czy możesz podać przykład?
Marcel Lange
@JCM i inni, skąd możesz wiedzieć, czy instrukcja aktualizacji zakończyła się powodzeniem w linii 3, abyś mógł zatwierdzić i wycofać?
ricko zoe
57

Autocommit OFF ...

MySQL

set autocommit=0;

Ustawia automatyczne zatwierdzanie dla bieżącej sesji.

Wykonujesz instrukcję, widzisz, co się zmieniło, a następnie wycofujesz, jeśli jest źle, lub zatwierdzasz, jeśli tego się spodziewałeś!

EDYCJA: Zaletą używania transakcji zamiast uruchamiania zapytania wybierającego jest to, że można łatwiej sprawdzić wynikowy zestaw.

Imad Moqaddem
źródło
4
@dystroy: każdy rozsądny DBMS obsługuje transakcje.
a_horse_with_no_name
7
Pamiętaj tylko, aby szybko zatwierdzić lub wycofać transakcję, w przeciwnym razie ryzykujesz zablokowanie innych transakcji - aw najgorszym przypadku zatrzymanie aplikacji. Nie jest dobrym pomysłem wykonanie zapytania, zjedzenie lunchu i powrót do wyników! :-)
Gary McGill
@GaryMcGill: oczekująca transakcja (przynajmniej we współczesnym DBMS) blokuje tylko inne transakcje zapisu .
a_horse_with_no_name
5
@dystroy: Niestety MyISAM jest używany wszędzie, a ja nie jestem administratorem baz danych.
static_rtti
1
Dodane oświadczenie Sql :)
Imad Moqaddem
11

Wiem, że to powtórzenie innych odpowiedzi, ale ma pewne wsparcie emocjonalne, aby wykonać dodatkowy krok w testowaniu aktualizacji: D

W przypadku aktualizacji testowej hash # jest twoim przyjacielem.

Jeśli masz oświadczenie o aktualizacji, takie jak:

UPDATE 
wp_history
SET history_by="admin"
WHERE
history_ip LIKE '123%'

Haszujesz AKTUALIZACJĘ i USTAWIASZ do testów, a następnie haszujesz je z powrotem w:

SELECT * FROM
#UPDATE
wp_history
#SET history_by="admin"
WHERE
history_ip LIKE '123%'

Działa w przypadku prostych instrukcji.

Dodatkowym praktycznie obowiązkowym rozwiązaniem jest uzyskanie kopii (kopii zapasowej duplikatu) przy każdym użyciu update na stole produkcyjnym. Phpmyadmin> operacje> kopiuj: table_yearmonthday. W przypadku tabel <= 100 mln zajmuje to tylko kilka sekund.

Johan
źródło
5

Nie jest to bezpośrednia odpowiedź, ale widziałem wiele sytuacji związanych z danymi prod, których można było uniknąć, wpisując WHEREnajpierw klauzulę ! Czasami WHERE 1 = 0może też pomóc w bezpiecznym złożeniu oświadczenia roboczego. Przydatne może być przyjrzenie się szacunkowemu planowi wykonania, który oszacuje wiersze, na które ma to wpływ. Poza tym w transakcji, którą wycofujesz, jak powiedzieli inni.

David M.
źródło
2
@SystemParadox - nic, ale WHERE 1 = 0jest bardziej przenośne, jeśli ktoś napotka to, kto pracuje z innym DBMS. Na przykład SQL Server nie zaakceptuje WHERE FALSE.
David M
2

W takich przypadkach, które chcesz przetestować, warto skupić się tylko na bieżących wartościach kolumn i wartościach kolumn, które wkrótce zostaną zaktualizowane .

Proszę spojrzeć na poniższy kod, który napisałem, aby zaktualizować ceny WHMCS:

# UPDATE tblinvoiceitems AS ii

SELECT                        ###  JUST
    ii.amount AS old_value,   ###  FOR
    h.amount AS new_value     ###  TESTING
FROM tblinvoiceitems AS ii    ###  PURPOSES.

JOIN tblhosting AS h ON ii.relid = h.id
JOIN tblinvoices AS i ON ii.invoiceid = i.id

WHERE ii.amount <> h.amount   ### Show only updatable rows

# SET ii.amount = h.amount

W ten sposób wyraźnie porównujemy już istniejące wartości z nowymi wartościami.

Mohammad Naji
źródło
1

Uruchom zapytanie wybierające na tej samej tabeli ze wszystkimi wherewarunkami stosowanymi w zapytaniu aktualizującym.

manurajhada
źródło
0

zrobić SELECTz tego,

jak jeśli masz

UPDATE users SET id=0 WHERE name='jan'

przekonwertować na

SELECT * FROM users WHERE name='jan'

EaterOfCode
źródło
0

Jeszcze jedną opcją jest poproszenie MySQL o plan zapytań. To mówi ci dwie rzeczy:

  • Czy w zapytaniu występują błędy składniowe, jeśli tak, samo polecenie planu zapytania zakończy się niepowodzeniem
  • Jak MySQL planuje wykonać zapytanie, np. Jakich indeksów użyje

W MySQL i większości baz danych SQL polecenie planu zapytania to describe, więc należy:

describe update ...;
Yawar
źródło