MySQL, atualize várias tabelas com uma consulta

132

Eu tenho uma função que atualiza três tabelas, mas uso três consultas para fazer isso. Desejo usar uma abordagem mais conveniente para boas práticas.

Como posso atualizar várias tabelas no MySQL com uma única consulta?

Adamski
fonte
3
você pode fornecer um exemplo do código gerado? Existe uma chave comum entre as tabelas?
Jonathan Day

Respostas:

451

Veja o caso de duas tabelas Bookse Orders. Caso aumentemos o número de livros em uma ordem específica Order.ID = 1002na Orderstabela, também precisamos reduzir o número total de livros disponíveis em nossas ações pelo mesmo número na Bookstabela.

UPDATE Books, Orders
SET Orders.Quantity = Orders.Quantity + 2,
    Books.InStock = Books.InStock - 2
WHERE
    Books.BookID = Orders.BookID
    AND Orders.OrderID = 1002;
Irfan
fonte
Se eu quiser incluir "LIMIT" na consulta SQL, devo dizer LIMIT 1 ou LIMIT 2?
Bluedayz
2
Qual é a vantagem de fazer isso versus uma transação? Obrigado!
paulkon
2
@ paulkon, presumo que, ao usar transações, haja muita sobrecarga, pois as reversões precisam estar disponíveis se algum procedimento na transação falhar.
Thijs Riezebeek
27
Aviso geral ao usar esta consulta. A cláusula WHERE é avaliada separadamente para cada tabela. O Books.BookID = Orders.BookID é muito importante, sem ele A atualização da tabela Livros aconteceria em todas as linhas e não apenas na linha com o ID especificado. Algumas lições são aprendidas da maneira mais difícil, essa foi aprendida da maneira mais assustadora.
nheimann1
1
@ nheimann1 E é exatamente por isso que eu sempre recomendo que as pessoas usem a sintaxe ANSI "inner join". É muito fácil esquecer essa condição e obter uma junção cartesiana completa.
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';

Para ver o que isso vai atualizar, você pode converter isso em uma instrução select, por exemplo:

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';

Um exemplo usando as mesmas tabelas da outra resposta:

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;

EDITAR:

Apenas por diversão, vamos adicionar algo um pouco mais interessante.

Digamos que você tenha uma mesa de bookse uma mesa de authors. Você bookstem um author_id. Porém, quando o banco de dados foi criado originalmente, nenhuma restrição de chave estrangeira foi configurada e, posteriormente, um bug no código front-end fez com que alguns livros fossem adicionados com author_ids inválidos . Como um DBA, você não precisa passar por tudo isso bookspara verificar o que author_iddeve ser; portanto, é tomada a decisão de que os capturadores de dados corrigam o booksponto para a direita authors. Mas há muitos livros para percorrer cada um e digamos que você saiba que os que têm um author_idque corresponde a um real authorestão corretos. São apenas aqueles que não existemauthor_ids inválidos. Já existe uma interface para os usuários atualizarem os detalhes do livro e os desenvolvedores não querem mudar isso apenas por esse problema. Mas a interface existente faz INNER JOIN authorsisso; portanto, todos os livros com autores inválidos são excluídos.

O que você pode fazer é o seguinte: insira um registro de autor falso, como "Autor desconhecido". Atualize author_idtodos os registros incorretos para apontar para o autor desconhecido. Em seguida, os capturadores de dados podem procurar todos os livros com o autor definido como "Autor desconhecido", procurar o autor correto e corrigi-los.

Como você atualiza todos os registros incorretos para apontar para o autor desconhecido? Assim (assumindo que o autor desconhecido 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;

O acima também atualizará booksque têm um NULL author_idpara o autor desconhecido. Se você não quer isso, é claro que pode adicionar AND books.author_id IS NOT NULL.

Wodin
fonte
35

Você também pode fazer isso com uma consulta usando uma junção da seguinte forma:

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

E então basta enviar esta consulta, é claro. Você pode ler mais sobre junções aqui: http://dev.mysql.com/doc/refman/5.0/en/join.html . Há também algumas restrições para solicitar e limitar várias atualizações de tabela que você pode ler aqui: http://dev.mysql.com/doc/refman/5.0/en/update.html (apenas ctrl + f "ingressar").

Stephen Searles
fonte
É um pouco generoso chamar isso de "junção" ;-)
underscore_d
2

É para isso que servem os procedimentos armazenados: implementar várias instruções SQL em uma sequência. Usando reversões, você pode garantir que eles sejam tratados como uma unidade de trabalho, ou seja, todos sejam executados ou nenhum deles, para manter os dados consistentes.

SteveCav
fonte
onde eu escreveria o procedimento? você poderia fornecer um exemplo?
Adamski
1
Aprovado para explicar a necessidade de atomicidade - também é importante perceber que o uso de procedimentos armazenados não garante sozinho a consistência, você ainda precisa usar transações; da mesma forma, as transações podem ser executadas sem o uso de um procedimento armazenado, desde que executadas na mesma conexão. Nesse caso, o uso de uma atualização de várias tabelas é ainda melhor.
Duncan
2

Quando você diz várias consultas, você quer dizer várias instruções SQL como em:

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

Ou várias chamadas de função de consulta como em:

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

O primeiro pode ser feito usando uma única chamada mySqlQuery, se é isso que você deseja alcançar, basta chamar a função mySqlQuery da seguinte maneira:

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

Isso executará todas as três consultas com uma chamada mySqlQuery ().

code_burgar
fonte
mySqlQuery () é uma função personalizada ou integrada? Eu quero saber mais sobre isso.
Debashis
3
Não há diferença significativa entre o envio de três consultas individualmente ou como uma consulta múltipla, exceto talvez se a função de consulta abrir uma nova conexão sempre. Do ponto de vista da execução do lado do servidor, é a mesma coisa
Duncan