web-dev-qa-db-fra.com

INSERT INTO ... SELECT sans détailler toutes les colonnes

Comment insérer des lignes sélectionnées de table_source à table_target en utilisant SQL dans MySQL où:

  • Les deux tables ont le même schéma
  • Toutes les colonnes doivent être transférées sauf l'incrémentation automatique id
  • Sans écrire explicitement tous les noms de colonnes, ce qui serait fastidieux

Le INSERT INTO table_target SELECT * FROM table_source trivial échoue sur les entrées en double pour la clé primaire.

18
Jonathan

Soit vous listez tous les champs souhaités dans l'insertion ... sélectionnez, soit vous utilisez autre chose en externe pour créer la liste pour vous.

SQL n'a pas quelque chose comme SELECT * except somefield FROM, vous devrez donc mordre la balle et écrire les noms de champs.

14
Marc B

Les noms de colonnes doivent être spécifiés -

INSERT INTO table_target SELECT NULL, column_name1, column_name2, column_name3, ...
  FROM table_source;

Il suffit de passer NULL comme valeur pour le champ ID d’incrémentation automatique.

18
Devart

Fastidieux mais sûr et correct.

L'écriture d'instructions INSERT sans fournir de liste de colonnes conduit à un code difficile à déboguer et, plus important encore, à un code très fragile qui se cassera si la définition de la table est modifiée.

Si vous ne pouvez absolument pas écrire vous-même les noms des colonnes, il est relativement facile de créer dans votre code un outil qui créera pour vous la liste séparée par des virgules.

4
Larry Lustig

Bien entendu, la clé primaire doit être unique. Cela dépend de ce que vous voulez atteindre, mais vous pouvez exclure des lignes avec une clé primaire déjà existante.

INSERT INTO table_target SELECT * FROM table_source 
WHERE table_source.id NOT IN (SELECT id FROM table_target)

UPDATE: étant donné que vous avez également besoin des lignes supplémentaires, vous devez d'abord résoudre le conflit. Est-ce que table_source a des relations? Sinon, vous pouvez changer ces clés:

UPDATE table_source SET id = id + 1000
WHERE id IN (SELECT id FROM table_target)

Où 1000, est une constante, assez grande pour qu'ils disparaissent à la fin de votre table.

3
stivlo

Je pense que vous pourriez utiliser une syntaxe comme celle-ci:

 INSCRIRE AU tableau (a, b, c) VALEURS (1,2,3) 
 ON DUPLICATE KEY UPDATE c = c + 1; 

REF: http://dev.mysql.com/doc/refman/5.0/fr/insert-on-duplicate.html J'espère que cela vous aidera

1
Sudhir Bastakoti

Ceci est ma solution finale à la mise à jour en masse avec la commande 'replace insert'.

SET @@session.group_concat_max_len = @@global.max_allowed_packet;
SET @schema_db = 'db';
SET @tabl = 'table';
SET @cols = (SELECT CONCAT('`',GROUP_CONCAT(COLUMN_NAME SEPARATOR '`, `'), '`') FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema_db AND TABLE_NAME = @tabl GROUP BY TABLE_NAME);
SET @Querystr = CONCAT('REPLACE INTO ',@schema_db,'.',@tabl,' SELECT ', @cols,' FROM import.tbl_', @tabl);

PREPARE stmt FROM @Querystr;
EXECUTE stmt;
1
Polarfrosch100

Il semble que les colonnes ne puissent pas être placées dans une instruction préparée MySQL. J'ai compilé la solution suivante pour le test:

SET @schema_db = 'DB';
SET @table = 'table';
SET @cols = (SELECT CONCAT(GROUP_CONCAT(COLUMN_NAME SEPARATOR ', '), "\n") FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = @schema_db AND TABLE_NAME = @table GROUP BY TABLE_NAME);
SET @Querystr = CONCAT('SELECT',' ', @cols,' ','FROM',' ',@schema_db,'.',@table,' ', 'Limit 5');
PREPARE stmt FROM @Querystr;
EXECUTE stmt;
0
Polarfrosch100

INSERT IGNORE juste "contourner" les lignes en double.

http://dev.mysql.com/doc/refman/5.5/fr/insert.html

0
Moshe L

Vous pouvez probablement le faire avec des déclarations préparées. 

PREPARE table_target_insert FROM 'INSERT INTO table_target SELECT ? FROM table_source';
SET @cols:='';
SELECT @cols:=GROUP_CONCAT(IF(column_name = 'id','NULL',column_name) separator ",") FROM information_schema.columns WHERE table_name='table_source';
EXECUTE table_target_insert USING @cols;
0
wisefish