Como as chaves de incremento automático são tratadas em INSERT (SELECT * FROM…)

12

Eu tenho table1e table2no MySQL. Ambos têm uma auto_incrementchave primária id.

Se os esquemas da tabela corresponderem e eu fizer o INSERT INTO table1 (SELECT * FROM table2)que acontece com relação às novas linhas inseridas em table1? Eles mantêm seus idvalores antigos e geram conflitos quando uma linha table1tem o mesmo id? Os novos valores são gerados pelo auto_increment? Depende do mecanismo de armazenamento ou do bloqueio?

Thomas Johnson
fonte

Respostas:

13

Você pode inserir em uma coluna de incremento automático e especificar um valor. Isto é bom; simplesmente substitui o gerador de incremento automático.

Se você tentar inserir um valor NULL ou 0 ou DEFAULT, ou se omitir a coluna de incremento automático das colunas na sua instrução INSERT, isso ativará o gerador de incremento automático.

Então, tudo bem INSERT INTO table1 SELECT * FROM table2(a propósito, você não precisa dos parênteses). Isto significa que os valores de ID em table2serão copiados na íntegra, e table1irá não geram novos valores.

Se você deseja table1 gerar novos valores, não pode fazer SELECT *. Você usa nulo ou 0 para a coluna do ID:

INSERT INTO table1 SELECT 0, col1, col2, col3, ... FROM table2;

Ou então, você omite a coluna da lista de colunas da instrução INSERT e da lista de seleção da instrução SELECT:

-- No id in either case:
INSERT INTO table1 (col1, col2, col3) SELECT col1, col2, col3, ... FROM table2;

Antes de você perguntar, não há sintaxe no SQL para "selecione *, exceto uma coluna". Você precisa especificar a lista completa dos nomes das colunas que deseja inserir.

Bill Karwin
fonte
Não existe uma opção que permita / não permita a inserção de 0 como um valor? (Em relação ao seu segundo parágrafo)
Sascha Rambeaud
1
Sim, SQL_MODE = NO_AUTO_VALUE_ON_ZERO , caso contrário, nunca poderíamos inserir um valor zero literal em uma coluna do AI. Se você usar esse modo SQL, NULL ainda funcionará para ativar o gerador de AI ou omitir a coluna também funcionará.
Bill Karwin
@ BillKarwin, em relação ao seu último parágrafo, depois de obter o sub-resultado select * from table, não há como operar esse sub-resultado para remover sua primeira coluna?
Pacerier 23/02
@ Pacerier, se bem entendi, você está perguntando se isso select *pode significar select * except for the first column. A resposta é não. select *sempre solicita todas as colunas dessa tabela. Se você deseja um subconjunto, deve especificar as colunas que deseja.
precisa
@ BillKarwin, Hmm, eu estava pensando em girar a mesa para fazer cláusulas verticais where, depois girá-la de volta, por exemplo, select*from transpose(select*from(transpose(select*from table where Id=1))where col_name<>Id)ou talvez de forma mais sucinta select*from table where Id=1 and $col<>Id, o que você acha?
Pacerier 24/02
3

O ID da seleção será o mesmo valor inserido na tabela. Isso resultará em um erro se você estiver tentando duplicar as linhas existentes.

Bill Karwin: Antes de você perguntar, não há sintaxe no SQL para "selecione *, exceto uma coluna".

Isso pode ser alcançado com alguma criatividade:

SET @sql = CONCAT('INSERT INTO <table> SELECT null, 
    ', (SELECT GROUP_CONCAT(COLUMN_NAME) 
    FROM information_schema.columns 
    WHERE table_schema = '<database>' 
    AND table_name = '<table>' 
    AND column_name NOT IN ('id')), ' 
from <table> WHERE id = <id>');  

PREPARE stmt1 FROM @sql;
EXECUTE stmt1;

Isso fará com que a nova linha obtenha um ID incrementado automaticamente em vez do ID da linha selecionada.

curmil
fonte
2
Inteligente, mas para mim, isso conta como soletrar a lista completa de nomes de colunas que você deseja inserir.
Bill Karwin
1
Bem, é o mysql soletrando automaticamente contra fazê-lo manualmente. Se você tiver tabelas com muitas colunas, isso impedirá que você perca uma ou escreva incorretamente algumas.
curmil 30/05