Mises à jour multiples dans MySQL

388

Je sais que vous pouvez insérer plusieurs lignes à la fois, existe-t-il un moyen de mettre à jour plusieurs lignes à la fois (comme dans, dans une seule requête) dans MySQL?

Edit: Par exemple, j'ai les éléments suivants

Name   id  Col1  Col2
Row1   1    6     1
Row2   2    2     3
Row3   3    9     5
Row4   4    16    8

Je souhaite combiner toutes les mises à jour suivantes en une seule requête

UPDATE table SET Col1 = 1 WHERE id = 1;
UPDATE table SET Col1 = 2 WHERE id = 2;
UPDATE table SET Col2 = 3 WHERE id = 3;
UPDATE table SET Col1 = 10 WHERE id = 4;
UPDATE table SET Col2 = 12 WHERE id = 4;
Teifion
la source

Réponses:

652

Oui, c'est possible - vous pouvez utiliser INSERT ... ON DUPLICATE KEY UPDATE.

En utilisant votre exemple:

INSERT INTO table (id,Col1,Col2) VALUES (1,1,1),(2,2,3),(3,9,3),(4,10,12)
ON DUPLICATE KEY UPDATE Col1=VALUES(Col1),Col2=VALUES(Col2);
Michiel de Mare
la source
22
S'il n'y a pas de doublons, je ne veux pas que cette ligne soit insérée. que dois-je faire? parce que je récupère des informations sur un autre site qui gère des tables avec des identifiants. J'insère des valeurs par rapport à cet identifiant. si le site a de nouveaux enregistrements, je finirai par insérer uniquement les identifiants et compter, sauf toutes les autres informations. si et seulement s'il y a une entrée pour l'id, alors il doit mettre à jour sinon il doit sauter. que dois-je faire?
Jayapal Chandran
33
Remarque: cette réponse suppose également que l'ID est la clé primaire
JM4
11
@JayapalChandran, vous devez utiliser INSERT IGNORE avec ON DUPLICATE KEY UPDATE. dev.mysql.com/doc/refman/5.5/en/insert.html
Haralan Dobrev
16
@HaralanDobrev L'utilisation de INSERT IGNORE insère toujours les enregistrements non dupliqués. que Jayapal voulait éviter. INSERT IGNORE transforme simplement toutes les erreurs en avertissement :( stackoverflow.com/questions/548541/…
Takehiro Adachi
2
Cette réponse suppose que l'ID est une clé unique (peut être principale comme d'autres l'ont dit), mais plus important encore, il suppose qu'il n'y a pas d'autres clés uniques. S'il y en a, cela peut jeter une clé dans les travaux.
Steve Horvath
130

Étant donné que vous avez des valeurs dynamiques, vous devez utiliser un IF ou CASE pour les colonnes à mettre à jour. Ça devient un peu moche, mais ça devrait marcher.

En utilisant votre exemple, vous pouvez le faire comme:

UPDATE table SET Col1 = CASE id 
                          QUAND 1 ALORS 1 
                          QUAND 2 ALORS 2 
                          QUAND 4 ALORS 10 
                          ELSE Col1 
                        FIN, 
                 Col2 = CASE id 
                          QUAND 3 ALORS 3 
                          QUAND 4 ALORS 12 
                          ELSE Col2 
                        FIN
             O id ID DANS (1, 2, 3, 4);
Harrison Fisk
la source
peut-être pas si joli à écrire pour une mise à jour dynamique mais un regard intéressant sur la fonctionnalité du boîtier ...
me_
1
@ user2536953, cela peut aussi être bien pour la mise à jour dynamique. Par exemple, j'ai utilisé cette solution en boucle dans php:$commandTxt = 'UPDATE operations SET chunk_finished = CASE id '; foreach ($blockOperationChecked as $operationID => $operationChecked) $commandTxt .= " WHEN $operationID THEN $operationChecked "; $commandTxt .= 'ELSE id END WHERE id IN ('.implode(', ', array_keys(blockOperationChecked )).');';
Boolean_Type
86

La question est ancienne, mais j'aimerais étendre le sujet avec une autre réponse.

Mon point est que la façon la plus simple d'y parvenir est simplement d'envelopper plusieurs requêtes avec une transaction. La réponse acceptée INSERT ... ON DUPLICATE KEY UPDATEest un joli hack, mais il faut être conscient de ses inconvénients et limites:

  • Comme cela a été dit, si vous lancez la requête avec des lignes dont les clés primaires n'existent pas dans la table, la requête insère de nouveaux enregistrements "semi-cuits". Ce n'est probablement pas ce que tu veux
  • Si vous avez une table avec un champ non nul sans valeur par défaut et que vous ne voulez pas toucher ce champ dans la requête, vous obtiendrez un "Field 'fieldname' doesn't have a default value"avertissement MySQL même si vous n'insérez pas une seule ligne du tout. Cela vous posera des problèmes si vous décidez d'être strict et de transformer les avertissements mysql en exceptions d'exécution dans votre application.

J'ai fait quelques tests de performance pour trois des variantes suggérées, y compris la INSERT ... ON DUPLICATE KEY UPDATEvariante, une variante avec la clause "case / when / then" et une approche naïve avec la transaction. Vous pouvez obtenir le code python et les résultats ici . La conclusion générale est que la variante avec l'instruction case s'avère deux fois plus rapide que deux autres variantes, mais il est assez difficile d'écrire un code correct et sûr pour l'injection, donc je persiste personnellement à l'approche la plus simple: utiliser les transactions.

Edit: Les résultats de Dakusan prouvent que mes estimations de performances ne sont pas tout à fait valides. Veuillez consulter cette réponse pour une autre recherche plus élaborée.

Roman Imankulov
la source
En utilisant des transactions, très bon (et simple) conseil!
mTorres
Que faire si mes tables ne sont pas de type InnoDB?
TomeeNS
1
Quelqu'un pourrait-il fournir un lien vers les transactions auxquelles cela ressemble? Et / ou un code pour un code de sécurité d'injection pour la variante avec instruction case?
François M.
1
Je trouve que les informations données sur la vitesse dans ce post sont fausses. J'ai écrit à ce sujet dans un post ci-dessous. stackoverflow.com/questions/3432/multiple-updates-in-mysql/…
Dakusan
1
@Dakusan, excellente réponse. Merci beaucoup d'avoir étendu, commenté et corrigé mes résultats.
Roman Imankulov
72

Je ne sais pas pourquoi une autre option utile n'est pas encore mentionnée:

UPDATE my_table m
JOIN (
    SELECT 1 as id, 10 as _col1, 20 as _col2
    UNION ALL
    SELECT 2, 5, 10
    UNION ALL
    SELECT 3, 15, 30
) vals ON m.id = vals.id
SET col1 = _col1, col2 = _col2;
newtover
la source
4
C'est le meilleur. Surtout si vous tirez les valeurs de mise à jour d'une autre requête SQL comme je le faisais.
v010dya
1
C'était génial pour une mise à jour sur une table avec une énorme quantité de colonnes. J'utiliserai probablement beaucoup cette requête à l'avenir. Merci!
Casper Wilkes
J'ai essayé ce type de requête. Mais lorsque les enregistrements atteignent 30 000 serveurs Boundary arrêtés. N 'y a-t-il pas une autre solution?
Bhavin Chauhan
Ça a l'air génial. Je vais essayer de combiner cela avec une clause WHERE où les clés primaires ne sont pas mises à jour, mais utilisées pour identifier les colonnes à modifier.
nl-x
@BhavinChauhan Avez-vous essayé d'utiliser une table temporaire au lieu de la jointure-sélection pour contourner le problème?
nl-x
41

Tout ce qui suit s'applique à InnoDB.

Je pense que connaître les vitesses des 3 méthodes différentes est important.

Il existe 3 méthodes:

  1. INSÉRER: INSÉRER avec ON DUPLICATE KEY UPDATE
  2. TRANSACTION: où vous effectuez une mise à jour pour chaque enregistrement d'une transaction
  3. CAS: dans lequel vous avez un cas / quand pour chaque enregistrement différent dans une MISE À JOUR

Je viens de tester cela, et la méthode INSERT était de 6,7x plus rapide pour moi que la méthode TRANSACTION. J'ai essayé sur un ensemble de 3 000 et 30 000 lignes.

La méthode TRANSACTION doit toujours exécuter chaque requête individuellement, ce qui prend du temps, bien qu'elle traite les résultats en mémoire, ou quelque chose, lors de l'exécution. La méthode TRANSACTION est également assez coûteuse dans les journaux de réplication et de requête.

Pire encore, la méthode CASE était 41,1 fois plus lente que la méthode INSERT avec 30 000 enregistrements (6,1 fois plus lente que TRANSACTION). Et 75 fois plus lent dans MyISAM. Les méthodes INSERT et CASE ont même atteint environ 1 000 enregistrements. Même à 100 enregistrements, la méthode CASE est À peine plus rapide.

Donc, en général, je pense que la méthode INSERT est à la fois la meilleure et la plus facile à utiliser. Les requêtes sont plus petites et plus faciles à lire et ne prennent qu'une seule requête d'action. Cela s'applique à la fois à InnoDB et à MyISAM.

Trucs bonus:

La solution pour le problème INSERT non-champ par défaut est de désactiver temporairement les modes SQL pertinents: SET SESSION sql_mode=REPLACE(REPLACE(@@SESSION.sql_mode,"STRICT_TRANS_TABLES",""),"STRICT_ALL_TABLES",""). Assurez-vous d'enregistrer le sql_modepremier si vous prévoyez de le rétablir.

Quant aux autres commentaires que j'ai vus qui disent que l'auto_increment monte en utilisant la méthode INSERT, cela semble être le cas dans InnoDB, mais pas MyISAM.

Le code pour exécuter les tests est le suivant. Il génère également des fichiers .SQL pour supprimer la surcharge de l'interpréteur php

<?
//Variables
$NumRows=30000;

//These 2 functions need to be filled in
function InitSQL()
{

}
function RunSQLQuery($Q)
{

}

//Run the 3 tests
InitSQL();
for($i=0;$i<3;$i++)
    RunTest($i, $NumRows);

function RunTest($TestNum, $NumRows)
{
    $TheQueries=Array();
    $DoQuery=function($Query) use (&$TheQueries)
    {
        RunSQLQuery($Query);
        $TheQueries[]=$Query;
    };

    $TableName='Test';
    $DoQuery('DROP TABLE IF EXISTS '.$TableName);
    $DoQuery('CREATE TABLE '.$TableName.' (i1 int NOT NULL AUTO_INCREMENT, i2 int NOT NULL, primary key (i1)) ENGINE=InnoDB');
    $DoQuery('INSERT INTO '.$TableName.' (i2) VALUES ('.implode('), (', range(2, $NumRows+1)).')');

    if($TestNum==0)
    {
        $TestName='Transaction';
        $Start=microtime(true);
        $DoQuery('START TRANSACTION');
        for($i=1;$i<=$NumRows;$i++)
            $DoQuery('UPDATE '.$TableName.' SET i2='.(($i+5)*1000).' WHERE i1='.$i);
        $DoQuery('COMMIT');
    }

    if($TestNum==1)
    {
        $TestName='Insert';
        $Query=Array();
        for($i=1;$i<=$NumRows;$i++)
            $Query[]=sprintf("(%d,%d)", $i, (($i+5)*1000));
        $Start=microtime(true);
        $DoQuery('INSERT INTO '.$TableName.' VALUES '.implode(', ', $Query).' ON DUPLICATE KEY UPDATE i2=VALUES(i2)');
    }

    if($TestNum==2)
    {
        $TestName='Case';
        $Query=Array();
        for($i=1;$i<=$NumRows;$i++)
            $Query[]=sprintf('WHEN %d THEN %d', $i, (($i+5)*1000));
        $Start=microtime(true);
        $DoQuery("UPDATE $TableName SET i2=CASE i1\n".implode("\n", $Query)."\nEND\nWHERE i1 IN (".implode(',', range(1, $NumRows)).')');
    }

    print "$TestName: ".(microtime(true)-$Start)."<br>\n";

    file_put_contents("./$TestName.sql", implode(";\n", $TheQueries).';');
}
Dakusan
la source
1
Vous faites le travail du SEIGNEUR ici;) Très apprécié.
chili
En testant certaines performances entre GoLang et PHP, en utilisant 40k lignes sur MariaDB, j'obtenais 2 secondes sur PHP et plus de 6 secondes sur golang .... Eh bien, on m'a toujours dit que GoLang fonctionnerait plus vite que PHP !!! Donc, je commence à me demander comment améliorer les performances ... Utilisation de la fonction INSERER ... SUR LA MISE À JOUR DUPLICATE KEY ... J'ai eu 0,74 secondes sur Golang et 0,86 secondes sur PHP !!!!
Diego Favero
1
Le but de mon code est de limiter les résultats de synchronisation uniquement aux instructions SQL, et non au code du langage ou des bibliothèques. GoLang et PHP sont 2 langages complètement séparés destinés à des choses complètement différentes. PHP est destiné à un environnement de script à exécution unique sur un seul thread avec un garbage collection principalement limité et passif. GoLang est conçu pour les applications compilées de longue durée avec une récupération de place agressive et le multithreading comme l'une des principales fonctionnalités du langage. Ils pourraient à peine être plus différents en termes de fonctionnalité et de raison du langage. [Suite]
Dakusan
Ainsi, lors de l'exécution de vos tests, assurez-vous de limiter strictement les mesures de vitesse aux appels de la fonction "Requête" pour l'instruction SQL. Comparer et optimiser les autres parties du code source qui ne sont pas strictement l'appel à la requête, c'est comme comparer des pommes et des oranges. Si vous limitez vos résultats à cela (avoir les chaînes précompilées et prêtes à l'emploi), les résultats devraient être très similaires. Toute différence à ce stade est due à la bibliothèque SQL du langage, et pas nécessairement au langage lui-même. À mon avis, la solution INSERT ON DUPLICATE était et sera toujours la meilleure option. [Suite]
Dakusan
Quant à votre commentaire sur GoLang étant plus rapide, c'est une déclaration incroyablement large qui ne prend en compte aucune des nombreuses mises en garde ou nuances de ces langues et de leurs conceptions. Java est un langage interprété, mais j'ai découvert il y a 15 ans qu'il pouvait pratiquement égaler (et peut-être même parfois battre) C en vitesse dans certains scénarios. Et C est un langage compilé, et le plus commun des langages système de niveau le plus bas, outre l'assembleur. J'aime vraiment ce que fait GoLang et il a définitivement la puissance et la fluidité pour devenir l'un des systèmes les plus courants et optimisés [suite]
Dakusan
9

Utiliser une table temporaire

// Reorder items
function update_items_tempdb(&$items)
{
    shuffle($items);
    $table_name = uniqid('tmp_test_');
    $sql = "CREATE TEMPORARY TABLE `$table_name` ("
        ."  `id` int(10) unsigned NOT NULL AUTO_INCREMENT"
        .", `position` int(10) unsigned NOT NULL"
        .", PRIMARY KEY (`id`)"
        .") ENGINE = MEMORY";
    query($sql);
    $i = 0;
    $sql = '';
    foreach ($items as &$item)
    {
        $item->position = $i++;
        $sql .= ($sql ? ', ' : '')."({$item->id}, {$item->position})";
    }
    if ($sql)
    {
        query("INSERT INTO `$table_name` (id, position) VALUES $sql");
        $sql = "UPDATE `test`, `$table_name` SET `test`.position = `$table_name`.position"
            ." WHERE `$table_name`.id = `test`.id";
        query($sql);
    }
    query("DROP TABLE `$table_name`");
}
Laymain
la source
8
UPDATE table1, table2 SET table1.col1='value', table2.col1='value' WHERE table1.col3='567' AND table2.col6='567'

Cela devrait fonctionner pour toi.

Il y a une référence dans le manuel MySQL pour plusieurs tables.

UnkwnTech
la source
6

Pourquoi personne ne mentionne-t-il plusieurs déclarations dans une même requête ?

En php, vous utilisez la multi_queryméthode de l'instance mysqli.

Depuis le manuel php

MySQL permet éventuellement d'avoir plusieurs instructions dans une même chaîne d'instructions. L'envoi de plusieurs instructions à la fois réduit les allers-retours client-serveur mais nécessite une gestion particulière.

Voici le résultat par rapport aux 3 autres méthodes de la mise à jour 30 000 raw. Le code peut être trouvé ici, basé sur la réponse de @Dakusan

Transaction: 5,5194580554962
Insert: 0,20669293403625
Caisse: 16,474853992462
Multi: 0,0412278175354

Comme vous pouvez le voir, la requête à plusieurs déclarations est plus efficace que la réponse la plus élevée.

Si vous obtenez un message d'erreur comme celui-ci:

PHP Warning:  Error while sending SET_OPTION packet

Vous devrez peut-être augmenter le max_allowed_packetfichier de configuration dans mysql qui se trouve sur ma machine /etc/mysql/my.cnf, puis redémarrer mysqld.

mononoke
la source
Toutes les comparaisons ci-dessous sont exécutées par rapport au test INSERT. Je viens de lancer le test dans les mêmes conditions et, sans transactions, il était 145x plus lent sur 300 lignes et 753x plus lent sur 3000 lignes. À l'origine, j'avais commencé avec les 30 000 rangées, mais je suis allé me ​​préparer un déjeuner et je suis revenu et ça allait toujours. Cela est logique, car l'exécution de requêtes individuelles et le vidage de chacune d'elles dans la base de données individuellement seraient ridiculement coûteux. Surtout avec la réplication. L'activation des transactions fait cependant une grande différence. À 3 000 lignes, il en fallait 1,5 fois plus et à 30 000 lignes 2,34 fois . [suite]
Dakusan
Mais vous aviez raison d'être rapide (avec les transactions). À la fois à 3 000 et 30 000 lignes, il était plus rapide que tout sauf la méthode INSERT. Il n'y a absolument aucun moyen d'obtenir de meilleurs résultats en exécutant 1 requête que 30 000 requêtes, même si elles sont regroupées dans un appel API MySQL spécial. Courant seulement 300 lignes, il était BEAUCOUP plus rapide que toutes les autres méthodes (à ma grande surprise), ce qui suit à peu près la même courbe graphique que la méthode CASE. Cela étant plus rapide peut s'expliquer de 2 façons. La première étant que la méthode INSERT insère toujours essentiellement 2 lignes en raison de la "ON DUPLICATE KEY [cont]
Dakusan
UPDATE "provoquant à la fois un" INSERT "et un" UPDATE ". L'autre étant que c'est moins de travail dans le processeur SQL pour ne modifier qu'une seule ligne à la fois en raison des recherches d'index. Je ne sais pas comment vous avez obtenu des résultats différents que moi, mais votre test supplémentaire semble solide. Je ne suis même pas sûr de savoir comment la réplication gérerait cet appel. Cela ne fonctionnerait que pour les appels UPDATE. Les appels d'insertion seront TOUJOURS plus rapides avec la seule requête INSERT.
Dakusan
Je faisais 300 MISES À JOUR à la fois sur une table pour réviser une erreur dans une boucle for qui a pris 41 secondes. Mettre les mêmes requêtes UPDATE en une seule a $mysqli->multi_query($sql)pris "0" secondes. Cependant, les requêtes suivantes ont échoué, ce qui m'a obligé à en faire un "programme" distinct.
Chris K
Merci. A pu mettre à jour environ 5k lignes (n'a pas testé plus) en une minute en utilisant plusieurs requêtes. Si quelqu'un cherche une solution PDO: stackoverflow.com/questions/6346674/…
Scofield
3

Il existe un paramètre que vous pouvez modifier appelé «instruction multiple» qui désactive le «mécanisme de sécurité» de MySQL implémenté pour empêcher (plus d'une) commande d'injection. Typique de l'implémentation «brillante» de MySQL, elle empêche également l'utilisateur d'effectuer des requêtes efficaces.

Voici ( http://dev.mysql.com/doc/refman/5.1/en/mysql-set-server-option.html ) quelques informations sur l'implémentation C du paramètre.

Si vous utilisez PHP, vous pouvez utiliser mysqli pour faire plusieurs instructions (je pense que php est livré avec mysqli depuis un certain temps maintenant)

$con = new mysqli('localhost','user1','password','my_database');
$query = "Update MyTable SET col1='some value' WHERE id=1 LIMIT 1;";
$query .= "UPDATE MyTable SET col1='other value' WHERE id=2 LIMIT 1;";
//etc
$con->multi_query($query);
$con->close();

J'espère que cela pourra aider.

Brooks
la source
4
Cela revient à envoyer les requêtes séparément. La seule différence est que vous envoyez tout cela dans un seul paquet réseau, mais les MISES À JOUR seront toujours traitées comme des requêtes distinctes. Mieux vaut les regrouper en une seule transaction, puis les modifications seront enregistrées dans la table à la fois.
Marki555
3
Comment les regrouper en une seule transaction? Montrez-nous, s'il vous plaît.
TomeeNS du
@TomeeNS À utiliser mysqli::begin_transaction(..)avant d'envoyer la requête et mysql::commit(..)après. Ou utilisez START TRANSACTIONcomme première et COMMITdernière instruction dans la requête elle-même.
Juha Palomäki
3

Vous pouvez alias la même table pour vous donner les identifiants que vous souhaitez insérer (si vous effectuez une mise à jour ligne par ligne:

UPDATE table1 tab1, table1 tab2 -- alias references the same table
SET 
col1 = 1
,col2 = 2
. . . 
WHERE 
tab1.id = tab2.id;

De plus, il devrait sembler évident que vous pouvez également mettre à jour à partir d'autres tables. Dans ce cas, la mise à jour se double d'une instruction "SELECT", vous donnant les données de la table que vous spécifiez. Vous indiquez explicitement dans votre requête les valeurs de mise à jour, la deuxième table n'est donc pas affectée.

oeufmatters
la source
2

Vous pouvez également être intéressé par l'utilisation de jointures sur les mises à jour, ce qui est également possible.

Update someTable Set someValue = 4 From someTable s Inner Join anotherTable a on s.id = a.id Where a.id = 4
-- Only updates someValue in someTable who has a foreign key on anotherTable with a value of 4.

Modifier: si les valeurs que vous mettez à jour ne proviennent pas d'un autre endroit de la base de données, vous devrez émettre plusieurs requêtes de mise à jour.

Shawn
la source
1

Et maintenant, le moyen le plus simple

update speed m,
    (select 1 as id, 20 as speed union
     select 2 as id, 30 as speed union
     select 99 as id, 10 as speed
        ) t
set m.speed = t.speed where t.id=m.id
Stan Sokolov
la source
-1

utilisation

REPLACE INTO`table` VALUES (`id`,`col1`,`col2`) VALUES
(1,6,1),(2,2,3),(3,9,5),(4,16,8);

Notez s'il vous plaît:

  • l'id doit être une clé unique primaire
  • si vous utilisez des clés étrangères pour référencer la table, REPLACE supprime puis insère, ce qui peut provoquer une erreur
Justin Levene
la source
-3

Les éléments suivants mettront à jour toutes les lignes d'une même table

Update Table Set
Column1 = 'New Value'

Le suivant mettra à jour toutes les lignes où la valeur de Column2 est supérieure à 5

Update Table Set
Column1 = 'New Value'
Where
Column2 > 5

Il existe tous les exemples d' Unkwntech de mise à jour de plusieurs tables

UPDATE table1, table2 SET
table1.col1 = 'value',
table2.col1 = 'value'
WHERE
table1.col3 = '567'
AND table2.col6='567'
GateKiller
la source
-3

Oui .. c'est possible en utilisant l'instruction SQL INSERT ON DUPLICATE KEY UPDATE .. syntaxe: INSERT INTO nom_table (a, b, c) VALEURS (1,2,3), (4,5,6) ON DUPLICATE KEY UPDATE a = VALEURS (a), b = VALEURS (b), c = VALEURS (c)

sara191186
la source
-5
UPDATE tableName SET col1='000' WHERE id='3' OR id='5'

Cela devrait atteindre ce que vous recherchez. Ajoutez simplement plus d'identifiants. Je l'ai testé.

UnkwnTech
la source
-7
UPDATE `your_table` SET 

`something` = IF(`id`="1","new_value1",`something`), `smth2` = IF(`id`="1", "nv1",`smth2`),
`something` = IF(`id`="2","new_value2",`something`), `smth2` = IF(`id`="2", "nv2",`smth2`),
`something` = IF(`id`="4","new_value3",`something`), `smth2` = IF(`id`="4", "nv3",`smth2`),
`something` = IF(`id`="6","new_value4",`something`), `smth2` = IF(`id`="6", "nv4",`smth2`),
`something` = IF(`id`="3","new_value5",`something`), `smth2` = IF(`id`="3", "nv5",`smth2`),
`something` = IF(`id`="5","new_value6",`something`), `smth2` = IF(`id`="5", "nv6",`smth2`) 

// Vous venez de le construire en php comme

$q = 'UPDATE `your_table` SET ';

foreach($data as $dat){

  $q .= '

       `something` = IF(`id`="'.$dat->id.'","'.$dat->value.'",`something`), 
       `smth2` = IF(`id`="'.$dat->id.'", "'.$dat->value2.'",`smth2`),';

}

$q = substr($q,0,-1);

Vous pouvez donc mettre à jour la table de trous avec une seule requête

user2082581
la source
Je n'ai pas downvote, mais je pense que l'objection est de faire le set, quand ce n'est pas nécessaire (et vous le faites toujours, quand vous vous mettez somethingà something)
v010dya