mysql delete em modo de segurança

87

Tenho um instrutor de mesa e quero deletar os registros que têm salário em uma faixa. Uma forma intuitiva é assim:

delete from instructor where salary between 13000 and 15000;

No entanto, no modo de segurança, não posso excluir um registro sem fornecer uma chave primária (ID).

Então, escrevo o seguinte sql:

delete from instructor where ID in (select ID from instructor where salary between 13000 and 15000);

No entanto, há um erro:

You can't specify target table 'instructor' for update in FROM clause

Estou confuso porque quando escrevo

select * from instructor where ID in (select ID from instructor where salary between 13000 and 15000);

não produz um erro.

Minha pergunta é:

  1. o que essa mensagem de erro realmente significa e por que meu código está errado?
  2. como reescrever este código para fazê-lo funcionar no modo de segurança?

Obrigado!

Roland Luo
fonte
você deseja manter o modo de segurança ativado? e você está usando o ambiente de trabalho mySql?
escrito em
a resposta a ambas as perguntas é sim. E estou surpreso que, quando usei jdbc para excluir registros em bancos de dados mysql sem um PK, isso não produziu um erro. Portanto, o modo de segurança é apenas para o ambiente de trabalho mysql?
roland luo
1
não - eu estava perguntando porque se você quisesse desligá-lo no ambiente de trabalho mySQL, eu poderia ter dito como. Pessoalmente, eu trabalho com isso desligado ... ter que ter IDs é uma ótima medida de segurança - mas em relação ao desenvolvimento, eu achei uma dor
escrito em

Respostas:

225

Pesquisando, a resposta popular parece ser "apenas desligue o modo de segurança" :

SET SQL_SAFE_UPDATES = 0;
DELETE FROM instructor WHERE salary BETWEEN 13000 AND 15000;
SET SQL_SAFE_UPDATES = 1;

Para ser sincero, não posso dizer que criei o hábito de correr no modo de segurança. Ainda assim, não estou totalmente confortável com essa resposta, pois ela apenas pressupõe que você deve alterar a configuração do banco de dados toda vez que tiver um problema.

Portanto, sua segunda consulta está mais perto do alvo, mas acerta outro problema: o MySQL aplica algumas restrições às subconsultas e uma delas é que você não pode modificar uma tabela enquanto seleciona dela em uma subconsulta.

Citando o manual do MySQL, Restrições em subconsultas :

Em geral, você não pode modificar uma tabela e selecionar da mesma tabela em uma subconsulta. Por exemplo, esta limitação se aplica a declarações das seguintes formas:

DELETE FROM t WHERE ... (SELECT ... FROM t ...);
UPDATE t ... WHERE col = (SELECT ... FROM t ...);
{INSERT|REPLACE} INTO t (SELECT ... FROM t ...);

Exceção: a proibição anterior não se aplica se você estiver usando uma subconsulta para a tabela modificada na cláusula FROM. Exemplo:

UPDATE t ... WHERE col = (SELECT * FROM (SELECT ... FROM t...) AS _t ...);

Aqui, o resultado da subconsulta na cláusula FROM é armazenado como uma tabela temporária, de forma que as linhas relevantes em t já tenham sido selecionadas no momento em que ocorre a atualização em t.

Essa última parte é a sua resposta. Selecione os IDs de destino em uma tabela temporária e exclua fazendo referência aos IDs nessa tabela:

DELETE FROM instructor WHERE id IN (
  SELECT temp.id FROM (
    SELECT id FROM instructor WHERE salary BETWEEN 13000 AND 15000
  ) AS temp
);

Demonstração do SQLFiddle .

sarjeta
fonte
6
Obrigado pela sua explicação! No entanto, eu tentei seu código no mysql workbench e ele ainda dizia "Você está usando o modo de atualização seguro e tentou atualizar uma tabela sem um WHERE que usa uma coluna KEY".
roland luo
Isso é inesperado. A sua chave primária tem um nome diferente? Percebo que sua pergunta parece indicar que tem um nome ID, enquanto minha resposta usa id.
rutter
Sim, mudo para ID e não funciona. Eu tentei deletar do instrutor onde ID = '1' e funciona então o ID é a chave primária
roland luo
1
@rolandluo Eu sei que já faz muito tempo, mas estou curioso para saber se você descobriu uma solução alternativa. É claro que esse método funcionou em outras circunstâncias, então eu me pergunto por que seu caso é diferente. Algo específico para seu servidor ou versão, talvez?
rutter
19

Você pode levar o MySQL a pensar que está realmente especificando uma coluna de chave primária. Isso permite que você "substitua" o modo de segurança.

Supondo que você tenha uma tabela com uma chave primária numérica de incremento automático, você pode fazer o seguinte:

DELETE FROM tbl WHERE id <> 0
Hugo Zink
fonte
12

Desligando o modo de segurança no Mysql workbench 6.3.4.0

Menu Editar => Preferências => Editor SQL: Outra seção: clique em "Atualizações seguras" ... para desmarcar a opção

Preferências de workbench

Peter B
fonte
2
"como reescrever este código para fazê-lo funcionar no modo de segurança ?" ==> Sua resposta diz como desabilitar o modo de segurança;)
ByteHamster de
2
Desculpe, eu entendi totalmente mal a pergunta. Eu encontrei este tópico quando não consegui deletar das tabelas com o mysql workbench, e houve uma pergunta semelhante em um comentário com o mysql workbench, mas não consigo criar comentários. Eu pensei, que seria um bom lugar para escrever isso aqui para referência futura ...
Peter B
0

Tenho uma solução muito mais simples, está funcionando para mim; também é uma solução alternativa, mas pode ser utilizável e você não precisa alterar suas configurações. Presumo que você possa usar um valor que nunca estará lá, então você o usa em sua cláusula WHERE

DELETE FROM MyTable WHERE MyField IS_NOT_EQUAL AnyValueNoItemOnMyFieldWillEverHave

Também não gosto muito dessa solução, por isso estou aqui, mas funciona e parece melhor do que foi respondido

Joaq
fonte