如何执行UPSERT,以便在更新部分中可以同时使用新值和旧值


78

愚蠢但简单的示例:假设我有一个表“ Item”,其中保存了接收到的项目的总数。

Item_Name              Items_In_Stock

项目名称是此处的主键。每当我收到数量为X的物料A时,如何实现以下目标。

如果该商品不存在,则为商品A插入一个新的摘要,并将库存商品设置为X;如果存在一条记录,其中库存商品为Y,则库存商品的新值为(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' )

我的问题是我的实际表中有多个列。在更新部分中编写多个select语句是一个好主意吗?

我当然可以用代码完成,但是有更好的方法吗?


为什么在示例中使用子查询?您可能刚刚使用过ON DUPLICATE KEY UPDATE new_items_count = new_items_count + 27。看到您没有张贴其他专栏,很难回答您的问题,因为预期的行为是未知的。您在处理其他专栏吗?更新一些数字还是?
Michael JV

大。多数民众赞成在回答这个问题。我不知道我可以使用列名来代替子查询。如果您将其发布为答案,我将接受它作为答案。
WPFAbsoluteNewBie

有一个定义MySQL函数的ruby库:github.com/seamusabshere/upsert
Seamus Abshere 2014年

这是new_items_count什么 是您桌上的另一列吗?
突袭

Answers:


153

如我的评论中所述,您不必进行子选择即可引用导致ON DUPLICATE KEY触发的行。因此,在您的示例中,您可以使用以下代码:

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

请记住,大多数事情都是非常简单的,如果您发现自己过于复杂化了一些本应简单的事情,那么您很可能会以错误的方式进行操作:)


您节省了一天。也与PDO合作
Ruwantha 2014年

简洁明了的答案
MikeT

2
注意:如果PK字段是自动递增的,则无论触发了哪种效果(INSERT或UPDATE)(使用InnoDB引擎时),看起来ON DUPLICATE KEY UPDATE子句都会增加自动递增的值。请参阅:MySQL参考手册
alejandro,

为防止代码重复,您应该使用new_items_count = new_items_count + VALUES(items_in_stock)
Paul Spiegel

1
可以使用REPLACE查询吗?这样就更简单了
Shankar Thyagarajan

13

您可以从以下示例中获得灵感:

假设您要添加用户明智的7天数据

它对于userid和day应该具有唯一的值

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

这是桌子

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`)
);

您的查询将是

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

例:

<?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);
}
?>

您的数据将在表中:

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

6
切勿将数据直接串联到查询中。将准备好的/参数化的查询与PDO或类似产品一起使用。在此示例中,至少转义数据是适当的。
布拉德2014年

请注意,此代码使用的mysql_*是PHP 7中已删除的功能。在考虑更换时,请参阅PDO与MySQLi上的这个有趣的难题
i336_

11

尽管Michael的答案是正确的,但是您需要更多地知识来以编程方式进行升级:

首先,创建表并指定要在其上进行唯一索引的列:

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`)
)

然后将一些值插入其中:

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

再次尝试执行相同的操作,您会收到重复的键错误,因为cellIdentityRowId相同:

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

键“ id_ce”的条目“ 1-199”重复

这就是为什么我们使用upsert命令的原因:

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

此命令将1.0已经存在的值用作值,并执行value = value + 300.0

因此,即使执行了上述命令,表中也只有一行,其值将为301.0


最终值应该301.0不是302.0吗?
alejandro

2
这可能不是公认的答案,但其他细节对我来说却是漫长的,并给予了赞成。
HPWD

1

这是upsert的语法

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

1

如果你对PK列,或在列的唯一索引值满足单一性,您可以使用INSERT IGNOREINSERT INTO ... ON DUPLICATEREPLACE

例子 INSERT IGNORE

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

例子 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;

例子 Replace

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

0

Upsert示例

INSERT INTO table1 (col1, col2, col3)
VALUES ($1, $2, $3)
ON CONFLICT (col1)
DO
UPDATE
SET col2 = $2, col3 = $3
WHERE col1 = $1
RETURNING col1
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.