Как проверить инструкцию SQL Update перед ее запуском?

95

В некоторых случаях выполнение оператора UPDATE в производственной среде может спасти положение. Однако неудачное обновление может быть хуже, чем первоначальная проблема.

Какие есть варианты, кроме тестовой базы данных, чтобы узнать, что будет делать оператор обновления перед его запуском?

static_rtti
источник

Ответы:

53

В дополнение к использованию транзакции, как сказал Имад (которая в любом случае должна быть обязательной), вы также можете выполнить проверку работоспособности, на какие строки влияет запуск выбора с использованием того же предложения WHERE, что и UPDATE.

Итак, если вы ОБНОВЛЕНИЕ

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

Следующее покажет вам, какие строки будут обновлены:

SELECT *
FROM foo
WHERE col1 = 1
  AND col2 = 'foobar';
a_horse_with_no_name
источник
1
Тогда для проверки данных лучше использовать транзакции. Предполагая, что он хочет проверить результат, я прихожу к выводу, что его утверждение сложнее, чем «SET bar = 42», поэтому во время сеанса он сможет сделать несколько запросов для проверки полученного набора данных ...
Имад Мокаддем
3
@ImadMoqaddem: Я согласен, и поэтому я написал « Помимо использования транзакции, как сказал Имад »
a_horse_with_no_name
И если у вас не FOREIGN KEY UPDATE CASCADEполучается sql
Green
@Green: что вы имеете в виду под «провалом»?
a_horse_with_no_name
76

А как насчет транзакций? У них есть функция ROLLBACK.

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

Например:

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

Ответ на вопрос от @rickozoe ниже:

Как правило, эти строки не будут выполняться один раз. В PHP fe вы бы написали что-то вроде этого (возможно, немного чище, но хотелось бы быстро ответить ;-)):

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

Другой способ - использовать переменные MySQL (см. Https://dev.mysql.com/doc/refman/5.7/en/user-variables.htm l и 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;

Но я бы посоветовал использовать языковые оболочки, доступные на вашем любимом языке программирования.

Марсель Ланге
источник
1
Это приведет к неожиданным результатам с вложенными транзакциями.
scones
Не могли бы вы привести пример?
Марсель Ланге
@JCM и другие, как узнать, успешно ли завершился оператор обновления в строке 3, чтобы можно было зафиксировать и выполнить откат?
ricko zoe
57

Автокоммит ВЫКЛ ...

MySQL

set autocommit=0;

Он отключает автоматизацию для текущего сеанса.

Вы выполняете свой оператор, видите, что он изменился, а затем откатываетесь, если он неверен, или фиксируете, если это то, что вы ожидали!

РЕДАКТИРОВАТЬ: Преимущество использования транзакций вместо выполнения запроса выбора заключается в том, что вы можете легче проверить полученный набор.

Имад Мокаддем
источник
4
@dystroy: любая разумная СУБД поддерживает транзакции.
a_horse_with_no_name
7
Просто не забудьте быстро зафиксировать или откатить транзакцию, иначе вы рискуете заблокировать другие транзакции - и в худшем случае это приведет к остановке вашего приложения. Не рекомендуется выполнять запрос, потом пообедать и вернуться, чтобы посмотреть результаты! :-)
Gary McGill
@GaryMcGill: ожидающая транзакция (по крайней мере, в современных СУБД) блокирует только другие транзакции записи .
a_horse_with_no_name
5
@dystroy: К сожалению, MyISAM используется везде, и я не администратор базы данных.
static_rtti
1
Добавлен оператор
sql
11

Я знаю, что это повторение других ответов, но у него есть некоторая эмоциональная поддержка, чтобы сделать дополнительный шаг для тестирования обновления: D

Для тестирования обновления хеш # - ваш друг.

Если у вас есть заявление об обновлении, например:

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

Вы хешируете UPDATE и SET для тестирования, а затем снова хешируете их:

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

Это работает для простых утверждений.

Дополнительным практически обязательным решением является получение копии (дубликата резервной копии) при каждом обновлении производственной таблицы. Phpmyadmin> операции> копировать: table_yearmonthday. Для таблиц <= 100M требуется всего несколько секунд.

Йохан
источник
5

Это не прямой ответ, но я видел много проблем с данными prod, которых можно было избежать, если WHEREсначала ввести предложение ! Иногда WHERE 1 = 0может помочь и безопасное составление рабочего утверждения. И просмотр ориентировочного плана выполнения, который оценит затронутые строки, может быть полезным. Кроме того, в транзакции, которую вы откатываете, как говорили другие.

Дэвид М
источник
2
@SystemParadox - ничего, хотя WHERE 1 = 0более переносимо, если кто-нибудь столкнется с этим, кто работает с другой СУБД. Например, SQL Server не примет WHERE FALSE.
David M
2

В тех случаях, которые вы хотите протестировать, рекомендуется сосредоточиться только на текущих значениях столбцов и значениях столбцов, которые скоро будут обновлены .

Взгляните на следующий код, который я написал для обновления цен 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

Таким образом мы четко сравниваем уже существующие значения с новыми значениями.

Мохаммад Наджи
источник
1

Запустите запрос выбора в той же таблице со всеми whereусловиями, которые вы применяете в запросе на обновление.

Манураджхада
источник
0

сделать SELECTиз этого,

как если бы у тебя

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

преобразовать это в

SELECT * FROM users WHERE name='jan'

EaterOfCode
источник
0

Еще один вариант - запросить у MySQL план запроса. Это говорит вам о двух вещах:

  • Есть ли какие-либо синтаксические ошибки в запросе, если да, то сама команда плана запроса завершится ошибкой
  • Как MySQL планирует выполнить запрос, например, какие индексы он будет использовать

В MySQL и большинстве баз данных SQL команда плана запроса выглядит describeтак:

describe update ...;
Явар
источник