web-dev-qa-db-fra.com

Comment effectuer un UPSERT afin que je puisse utiliser les nouvelles et les anciennes valeurs dans la partie update

Exemple stupide mais simple: Supposons que j'ai un tableau 'Item' où je garde les totaux des articles reçus.

Item_Name              Items_In_Stock

Le nom de l'élément est la clé primaire ici. Comment obtenir les résultats suivants chaque fois que je reçois un article A en quantité X. 

Si l'article n'existe pas, j'insère un nouveau stocké pour l'article A et mets les articles en stock à X. S'il existe un enregistrement où les articles en stock étaient Y, la nouvelle valeur des articles en stock est (X + Y).

INSERT INTO `item`
(`item_name`, items_in_stock)
VALUES( 'A', 27)
ON DUPLICATE KEY UPDATE
`new_items_count` = 27 + (SELECT items_in_stock where item_name = 'A' )

Mon problème est que j'ai plusieurs colonnes dans mon tableau actuel. Est-ce une bonne idée d'écrire plusieurs instructions select dans la partie mise à jour?

Bien sûr, je peux le faire en code, mais existe-t-il un meilleur moyen?

54

Comme mentionné dans mon commentaire, vous n'avez pas besoin de faire la sous-sélection pour faire référence à la ligne qui provoque le déclenchement de ON DUPLICATE KEY. Donc, dans votre exemple, vous pouvez utiliser ce qui suit:

INSERT INTO `item`
(`item_name`, items_in_stock)
VALUES( 'A', 27)
ON DUPLICATE KEY UPDATE
`new_items_count` = `new_items_count` + 27

Rappelez-vous que la plupart des choses sont vraiment simples, si vous vous trouvez trop compliqué avec quelque chose qui devrait être simple, alors vous le faites probablement de la mauvaise façon :)

120
Michael J.V.

Vous pouvez avoir une idée de cet exemple:

Supposons que vous souhaitiez ajouter des données utilisateur sur sept jours

Il devrait avoir une valeur unique pour userid et day comme 

UNIQUE KEY `seven_day` (`userid`,`day`)

Voici la table

CREATE TABLE `table_name` (
  `userid` char(4) NOT NULL,
  `day` char(3) NOT NULL,
  `open` char(5) NOT NULL,
  `close` char(5) NOT NULL,
  UNIQUE KEY `seven_day` (`userid`,`day`)
);

Et votre requête sera

INSERT INTO table_name (userid,day,open,close) 
    VALUES ('val1', 'val2','val3','val4') 
        ON DUPLICATE KEY UPDATE open='val3', close='val4';

Exemple:

<?php
//If your data is
$data= array(
        'sat'=>array("userid"=>"1001", "open"=>"01.01", "close"=>"11.01"),
        'Sun'=>array("userid"=>"1001", "open"=>"02.01", "close"=>"22.01"),
        'sat'=>array("userid"=>"1001", "open"=>"03.01", "close"=>"33.01"),
        'mon'=>array("userid"=>"1002", "open"=>"08.01", "close"=>"08.01"),
        'mon'=>array("userid"=>"1002", "open"=>"07.01", "close"=>"07.01")
    );


//If you query this in a loop
//$conn = mysql_connect("localhost","root","");
//mysql_select_db("test", $conn);

foreach($data as $day=>$info) {
    $sql = "INSERT INTO table_name (userid,day,open,close) 
                VALUES ('$info[userid]', '$day','$info[open]','$info[close]') 
            ON DUPLICATE KEY UPDATE open='$info[open]', close='$info[close]'";
    mysql_query($sql);
}
?>

Vos données seront dans la table:

+--------+-----+-------+-------+
| userid | day | open  | close |
+--------+-----+-------+-------+
| 1001   | sat | 03.01 | 33.01 |
| 1001   | Sun | 02.01 | 22.01 |
| 1002   | mon | 07.01 | 07.01 |
+--------+-----+-------+-------+
11
Salim

Bien que la réponse de Michael soit la bonne, vous devez en savoir un peu plus pour effectuer la programmation ascendante par programme:

Tout d’abord, créez votre table et spécifiez les colonnes sur lesquelles vous souhaitez un index unique:

CREATE TABLE IF NOT EXISTS Cell (
  cellId BIGINT UNSIGNED,
  attributeId BIGINT UNSIGNED,
  entityRowId BIGINT UNSIGNED,
  value DECIMAL(25,5),
  UNIQUE KEY `id_ce` (`cellId`,`entityRowId`)
)

Puis insérez-y quelques valeurs:

INSERT INTO Cell VALUES( 1, 6, 199, 1.0 );

Essayez de refaire la même chose et vous obtiendrez une erreur de clé en double, car cellId et entityRowId sont identiques.

INSERT INTO Cell VALUES( 1, 6, 199, 1.0 );

Duplicata '1-199' pour la clé 'id_ce'

C'est pourquoi nous utilisons la commande upsert:

INSERT INTO Cell ( cellId, attributeId, entityRowId, value, s, l, p, t )
VALUES( 1, 6, 199, 300.0 )
ON DUPLICATE KEY UPDATE `value` = `value` + VALUES(`value`)

Cette commande prend la valeur 1.0 déjà présente en tant que valeur et effectue un value = value + 300.0.

Ainsi, même après l'exécution de la commande ci-dessus, il n'y aura qu'une seule ligne dans la table et la valeur sera 302.0.

5
Nav

Si vous avez une valeur pour Column PK ou Index unique sur une colonne qui satisfait à l'unicité, vous pouvez utiliser INSERT IGNORE, INSERT INTO ... ON DUPLICATE ou REPLACE

Exemple avec INSERT IGNORE

INSERT IGNORE INTO Table1
    (ID, serverID, channelID, channelROLE)
VALUES
    (....);

Exemple avec INSERT INTO .. ON DUPLICATE KEY UPDATE

SET @id = 1,
    @serverId = 123545,
    @channelId = 512580,
    @channelRole = 'john';
INSERT INTO Table1
    (ID, serverID, channelID, channelROLE)
VALUES
    (@id, @serverId, @channelId, @channelRole)
ON DUPLICATE KEY UPDATE
    serverId = @serverId,
    channelId = @channelId,
    channelRole = @channelRole;

Exemple avec Replace

REPLACE INTO table1
    (ID, serverID, channelID, channelROLE)
VALUES
    (...);
0
Simonare

Ceci est la syntaxe pour un upsert

INSERT INTO `{TABLE}` (`{PKCOLUMN}`, `{COLUMN}`) VALUES (:value)
ON DUPLICATE KEY UPDATE `{COLUMN}` = :value_dup';
0
relipse