MySQL, обновить несколько таблиц одним запросом

132

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

Как я могу обновить несколько таблиц в MySQL с помощью одного запроса?

Адамский
источник
3
вы можете привести пример сгенерированного кода? Есть ли общий ключ между таблицами?
Джонатан Дэй

Ответы:

451

Возьмем случай двух таблиц, Booksи Orders. В случае, если мы увеличиваем количество книг в определенном порядке с помощью Order.ID = 1002in Orderstable, нам также необходимо уменьшить это общее количество книг, доступных на нашем складе, на такое же количество в Booksтаблице.

UPDATE Books, Orders
SET Orders.Quantity = Orders.Quantity + 2,
    Books.InStock = Books.InStock - 2
WHERE
    Books.BookID = Orders.BookID
    AND Orders.OrderID = 1002;
Ирфан
источник
Если я хочу включить "LIMIT" в запрос SQL, должен ли я сказать LIMIT 1 или LIMIT 2?
Bluedayz
2
В чем преимущество этого перед транзакцией? Спасибо!
Паулкон
2
@paulkon, я предполагаю, что при использовании транзакций возникают большие накладные расходы, поскольку в случае сбоя какой-либо процедуры в транзакции должны быть доступны откаты.
Thijs Riezebeek
27
Общее предупреждение при использовании этого запроса. Предложение WHERE оценивается отдельно для каждой таблицы. Books.BookID = Orders.BookID очень важен, без него обновление таблицы Books произойдет для всех строк, а не только для строки с указанным идентификатором. Некоторые уроки усвоены трудным путем, этот - ужасающим.
nheimann1
1
@ nheimann1 И именно поэтому я всегда рекомендую использовать синтаксис «внутреннего соединения» ANSI. Слишком легко забыть об этом условии и вместо этого получить полное декартово соединение.
fool4jesus
77
UPDATE t1
INNER JOIN t2 ON t2.t1_id = t1.id
INNER JOIN t3 ON t2.t3_id = t3.id
SET t1.a = 'something',
    t2.b = 42,
    t3.c = t2.c
WHERE t1.a = 'blah';

Чтобы увидеть, что это будет обновляться, вы можете преобразовать это в оператор выбора, например:

SELECT t2.t1_id, t2.t3_id, t1.a, t2.b, t2.c AS t2_c, t3.c AS t3_c
FROM t1
INNER JOIN t2 ON t2.t1_id = t1.id
INNER JOIN t3 ON t2.t3_id = t3.id
WHERE t1.a = 'blah';

Пример с использованием тех же таблиц, что и другой ответ:

SELECT Books.BookID, Orders.OrderID,
    Orders.Quantity AS CurrentQuantity,
    Orders.Quantity + 2 AS NewQuantity,
    Books.InStock AS CurrentStock,
    Books.InStock - 2 AS NewStock
FROM Books
INNER JOIN Orders ON Books.BookID = Orders.BookID
WHERE Orders.OrderID = 1002;

UPDATE Books
INNER JOIN Orders ON Books.BookID = Orders.BookID
SET Orders.Quantity = Orders.Quantity + 2,
    Books.InStock = Books.InStock - 2
WHERE Orders.OrderID = 1002;

РЕДАКТИРОВАТЬ:

Ради интереса давайте добавим что-нибудь поинтереснее.

Допустим, у вас есть таблица из booksи таблица из authors. У вас booksесть author_id. Но когда база данных была изначально создана, никакие ограничения внешнего ключа не были установлены, а позже ошибка во внешнем коде привела к добавлению некоторых книг с недопустимыми author_ids. Как администратор баз данных вы не хотите проходить через все это, booksчтобы проверить, что author_idдолжно быть, поэтому принимается решение, что сборщики данных исправят booksуказатель вправо authors. Но существует слишком много книг, чтобы просмотреть каждую из них, и, допустим, вы знаете, что те, у author_idкоторых есть реальная ценность, authorявляются правильными. Это просто несуществующиеauthor_ids, которые недействительны. Уже существует интерфейс, позволяющий пользователям обновлять сведения о книге, и разработчики не хотят менять его только для этой проблемы. Но существующий интерфейс делает это INNER JOIN authors, поэтому все книги с недопустимыми авторами исключаются.

Вы можете сделать следующее: вставить фиктивную запись об авторе, например «Неизвестный автор». Затем обновите author_idвсе плохие записи, чтобы они указывали на Неизвестного автора. Затем сборщики данных могут искать все книги с автором, установленным на «Неизвестный автор», искать правильного автора и исправлять их.

Как обновить все плохие записи, чтобы они указывали на Неизвестного автора? Вот так (при условии, что неизвестный автор author_id- 99999):

UPDATE books
LEFT OUTER JOIN authors ON books.author_id = authors.id
SET books.author_id = 99999
WHERE authors.id IS NULL;

Вышесказанное также обновит booksчто есть NULL author_idнеизвестный автор. Если вы этого не хотите, вы, конечно, можете добавить AND books.author_id IS NOT NULL.

Водин
источник
35

Вы также можете сделать это с одним запросом, используя такое соединение:

UPDATE table1,table2 SET table1.col=a,table2.col2=b
WHERE items.id=month.id;

И, конечно, просто отправьте этот запрос. Вы можете узнать больше о присоединениях здесь: http://dev.mysql.com/doc/refman/5.0/en/join.html . Также существует пара ограничений для упорядочивания и ограничения нескольких обновлений таблиц, о которых вы можете прочитать здесь: http://dev.mysql.com/doc/refman/5.0/en/update.html (просто ctrl + f «присоединиться»).

Стивен Сирлз
источник
Немного щедро называть это «присоединением» ;-)
underscore_d
2

Обычно для этого нужны хранимые процедуры: для реализации нескольких операторов SQL в последовательности. Используя откаты, вы можете гарантировать, что они обрабатываются как одна единица работы, то есть либо все они выполняются, либо ни одна из них не выполняется, чтобы данные были согласованными.

SteveCav
источник
где бы я написал процедуру? не могли бы вы привести пример?
Adamski
1
Проголосовали за объяснение необходимости атомарности - также важно понимать, что использование хранимых процедур не само по себе гарантирует согласованность, вам все равно необходимо использовать транзакции; аналогично, транзакции могут выполняться без использования хранимой процедуры при условии, что они выполняются через одно и то же соединение. В этом случае даже лучше использовать обновление нескольких таблиц.
Дункан
2

Когда вы говорите несколько запросов, вы имеете в виду несколько операторов SQL, например:

UPDATE table1 SET a=b WHERE c;
UPDATE table2 SET a=b WHERE d;
UPDATE table3 SET a=b WHERE e;

Или несколько вызовов функции запроса, как в:

mySqlQuery(UPDATE table1 SET a=b WHERE c;)
mySqlQuery(UPDATE table2 SET a=b WHERE d;)
mySqlQuery(UPDATE table3 SET a=b WHERE e;)

Первое можно сделать с помощью одного вызова mySqlQuery, если это то, чего вы хотели достичь, просто вызовите функцию mySqlQuery следующим образом:

mySqlQuery(UPDATE table1 SET a=b WHERE c; UPDATE table2 SET a=b WHERE d; UPDATE table3 SET a=b WHERE e;)

Это выполнит все три запроса с помощью одного вызова mySqlQuery ().

code_burgar
источник
mySqlQuery () - это настраиваемая функция или встроенная функция? Я хочу узнать об этом больше.
Debashis
3
Нет существенной разницы между отправкой трех запросов по отдельности или в виде нескольких запросов, кроме случаев, когда ваша функция запроса каждый раз открывает новое соединение. С точки зрения исполнения на стороне сервера, это то же самое,
Дункан